- Posted on
- • Artificial Intelligence
Artificial Intelligence Email Automation
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Bash-Powered AI Email Automation on Linux: Summarize, Triage, and Draft Replies
Drowning in email? If your inbox drives your day more than your calendar, you’re not alone. The average knowledge worker spends hours per week sorting newsletters, triaging customer queries, and drafting repetitive replies. Good news: you can automate the boring parts with a few shell tools and an AI model—without abandoning your Linux workflow.
This guide shows how to:
Pull mail locally via IMAP
Summarize and auto-label new messages
Draft sensible replies you can review before sending
Schedule everything with cron or systemd—using only Bash, curl, and friends
You’ll get practical, copy-pasteable scripts and install commands for apt, dnf, and zypper.
Why AI Email Automation is Worth It
Time and focus: Reduce manual triage by 50–80% with smart summaries, priority flags, and labels.
Consistency: Drafts follow policy, tone, and formatting guidelines you define.
Works with your stack: Uses open protocols (IMAP/SMTP) and plain text tools you can audit.
Privacy control: Run everything locally on your machine; redact sensitive bits before they hit an external model; keep a human in the loop for sends.
What You’ll Build (Overview)
A local Maildir mirror (via isync/mbsync)
An indexer and searcher (notmuch)
A sender (msmtp)
A small Bash function that talks to an AI model (via curl + JSON)
A few scripts that:
- Summarize and auto-label new mail
- Draft reply templates for your review
- Run on a schedule
1) Install prerequisites
Packages: curl, jq, isync, notmuch, msmtp, swaks, inotify-tools, and cron.
Debian/Ubuntu (apt):
sudo apt update
sudo apt install -y curl jq isync notmuch msmtp swaks inotify-tools cron
Fedora/RHEL/CentOS Stream (dnf):
sudo dnf install -y curl jq isync notmuch msmtp swaks inotify-tools cronie
openSUSE (zypper):
sudo zypper refresh
sudo zypper install -y curl jq isync notmuch msmtp swaks inotify-tools cron
Notes:
On Fedora, “cron” is provided by cronie.
Ensure your user is allowed to run cron jobs, or use systemd user timers (shown later).
2) Sync your email locally with isync (mbsync)
Create a Maildir and an mbsync config. This mirrors your IMAP account locally, which lets notmuch and Bash do fast, private processing.
~/.mbsyncrc:
IMAPAccount myacct
Host imap.example.com
User your.name@example.com
Pass your_imap_app_password
SSLType IMAPS
IMAPStore remote-myacct
Account myacct
MaildirStore local-myacct
Path ~/.mail/myacct/
Inbox ~/.mail/myacct/INBOX
SubFolders Verbatim
Channel myacct-inbox
Master :remote-myacct:INBOX
Slave :local-myacct:INBOX
Patterns *
Create Both
Expunge Both
SyncState *
Make directories and run a first sync:
mkdir -p ~/.mail/myacct/INBOX
chmod 700 ~/.mail
mbsync -a
Index with notmuch:
notmuch setup # follow prompts, point it at ~/.mail
notmuch new
Security tip: protect credentials with file permissions:
chmod 600 ~/.mbsyncrc
3) Configure outbound mail with msmtp
~/.config/msmtp/config:
defaults
auth on
tls on
logfile ~/.msmtp.log
account default
host smtp.example.com
port 587
from your.name@example.com
user your.name@example.com
password your_smtp_app_password
Permissions:
chmod 600 ~/.config/msmtp/config
Test SMTP:
echo -e "Subject: msmtp test\n\nHello from msmtp." | msmtp -t your.name@example.com
swaks --server smtp.example.com --port 587 --au your.name@example.com --ap your_smtp_app_password --to your.name@example.com --from your.name@example.com --tls
4) Talk to an AI model from Bash
You can use any OpenAI-compatible endpoint. Set three environment variables:
AI_BASE_URL: base URL, e.g. https://api.openai.com
AI_MODEL: model name, e.g. gpt-4o-mini
AI_API_KEY: your API key
~/.profile (or your shell rc file):
export AI_BASE_URL="https://api.openai.com"
export AI_MODEL="gpt-4o-mini"
export AI_API_KEY="sk-your-key"
A safe, reusable Bash function to send prompts and get text results:
~/bin/ai_chat.sh:
#!/usr/bin/env bash
set -euo pipefail
prompt="$1"
if [[ -z "${AI_BASE_URL:-}" || -z "${AI_MODEL:-}" || -z "${AI_API_KEY:-}" ]]; then
echo "AI_BASE_URL, AI_MODEL, AI_API_KEY must be set" >&2
exit 1
fi
json_payload=$(jq -n --arg model "$AI_MODEL" --arg content "$prompt" '{
model: $model,
messages: [
{role: "system", content: "You are a helpful assistant that writes JSON and concise email summaries."},
{role: "user", content: $content}
],
temperature: 0.2
}')
curl -sS \
-H "Authorization: Bearer $AI_API_KEY" \
-H "Content-Type: application/json" \
-d "$json_payload" \
"$AI_BASE_URL/v1/chat/completions" \
| jq -r '.choices[0].message.content'
Make it executable:
chmod +x ~/bin/ai_chat.sh
Tip: For privacy, redact or truncate long emails before sending to the model. Example helper:
~/bin/redact_body.sh:
#!/usr/bin/env bash
set -euo pipefail
# Read message body from stdin, trim to 4000 chars, collapse numbers.
body=$(cat)
body=${body:0:4000}
# Replace long numbers/emails with tokens
echo "$body" \
| sed -E 's/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/[EMAIL]/g' \
| sed -E 's/[0-9]{6,}/[NUMBER]/g'
chmod +x ~/bin/redact_body.sh
5) Automations you can actually use
Below are three scripts that work together. They rely on notmuch to extract text and identify messages.
A) Summarize and auto-label new mail
Fetch new mail
Summarize the body
Get structured JSON with tags and urgency
Tag messages in notmuch
~/bin/ai_summarize_and_tag.sh:
#!/usr/bin/env bash
set -euo pipefail
# 1) Sync and index
mbsync -a
notmuch new >/dev/null
# 2) Find recent unread messages (limit to 25 per run)
ids=$(notmuch search --output=messages --format=text 'tag:unread' | head -n 25)
for id in $ids; do
# Extract plain text from the message
text=$(notmuch show --format=text --entire-thread=false --exclude=false id:"$id" \
| ~/bin/redact_body.sh)
# Build a JSON-instruction prompt
read -r -d '' prompt <<'EOF' || true
You are labeling and summarizing an email for a busy engineer.
Return only JSON with keys: summary, urgency (low|normal|high), tags (array of short strings).
Guidelines:
- tags: choose from ["billing","support","customers","recruiting","newsletter","internal","sales","legal","ops","personal"] or add at most 2 new ones if necessary.
- Keep summary <= 60 words. Be factual.
EOF
# Combine with the email body
full_prompt="$prompt
Email:
$text"
# 3) Ask the model for JSON
result=$(~/bin/ai_chat.sh "$full_prompt")
# 4) Parse JSON and apply tags
summary=$(printf "%s" "$result" | jq -r '.summary // empty' || true)
urgency=$(printf "%s" "$result" | jq -r '.urgency // "normal"' || true)
mapfile -t tags < <(printf "%s" "$result" | jq -r '.tags[]?' || true)
# Always tag a safe prefix for AI work
to_apply="+ai-processed"
case "$urgency" in
high) to_apply="$to_apply +urgent" ;;
low) to_apply="$to_apply -urgent" ;;
*) : ;;
esac
for t in "${tags[@]:-}"; do
# sanitize tag names
t_clean=$(echo "$t" | tr '[:upper:] ' '[:lower:]-' | tr -cd '[:alnum:]-_:' | sed 's/^-*//;s/-*$//')
[[ -n "$t_clean" ]] && to_apply="$to_apply +tag-$t_clean"
done
notmuch tag $to_apply id:"$id"
# Store a one-line summary in notmuch as a tag for quick view (truncated)
if [[ -n "$summary" ]]; then
short=$(echo "$summary" | tr -s ' ' | cut -c1-80)
# Embed as a pseudo-tag prefix; notmuch tags cannot have spaces
k="aiS-$(echo "$short" | tr ' ' '_' | tr -cd '[:alnum:]_')"
notmuch tag +"$k" id:"$id"
fi
echo "Processed: $id ($urgency) tags applied: $to_apply"
done
Run it:
~/bin/ai_summarize_and_tag.sh
Check tags:
notmuch search tag:ai-processed
B) Draft reply templates (human-in-the-loop)
Generates a reply draft you can edit
Uses thread context from notmuch
Sends with msmtp when you confirm
~/bin/ai_draft_reply.sh:
#!/usr/bin/env bash
set -euo pipefail
id="${1:-}"
[[ -z "$id" ]] && { echo "Usage: $0 <notmuch-id>"; exit 1; }
# Extract original message context (last message text)
orig=$(notmuch show --format=text --entire-thread=false id:"$id" | tail -n +1 | ~/bin/redact_body.sh)
read -r -d '' prompt <<'EOF' || true
You write professional, concise email replies.
Task:
- Draft a reply of 80–150 words.
- Start with a brief acknowledgment.
- Bullet any required next steps.
- Keep tone friendly and clear.
- Do not invent facts; ask for missing info.
Return only the email body text.
EOF
body=$(~/bin/ai_chat.sh "$prompt
Original message:
$orig")
# Build minimal RFC 5322 headers; pull From/To/Subject from notmuch metadata
from=$(notmuch show --format=json --entire-thread=false id:"$id" | jq -r '.[0].headers.To' | sed 's/^ *//;s/ *$//')
to=$(notmuch show --format=json --entire-thread=false id:"$id" | jq -r '.[0].headers.From' | sed 's/^ *//;s/ *$//')
subj=$(notmuch show --format=json --entire-thread=false id:"$id" | jq -r '.[0].headers.Subject' | sed 's/^Re: */Re: /;t;s/^/Re: /')
tmp="$(mktemp /tmp/reply-XXXXXX.eml)"
{
echo "From: $from"
echo "To: $to"
echo "Subject: $subj"
echo "MIME-Version: 1.0"
echo "Content-Type: text/plain; charset=UTF-8"
echo
echo "$body"
} > "$tmp"
${EDITOR:-vi} "$tmp"
read -rp "Send this email via msmtp? [y/N] " ans
if [[ "$ans" =~ ^[Yy]$ ]]; then
msmtp -t < "$tmp" && echo "Sent."
else
echo "Aborted. Draft at $tmp"
fi
Use it:
id=$(notmuch search --output=messages 'tag:ai-processed and tag:tag-customers' | head -n1)
~/bin/ai_draft_reply.sh "$id"
C) End-to-end triage runner
Combine sync, summarize, and a simple routing rule:
#!/usr/bin/env bash
set -euo pipefail
mbsync -a
notmuch new >/dev/null
~/bin/ai_summarize_and_tag.sh
# Example: auto-file newsletters
for id in $(notmuch search --output=messages 'tag:ai-processed and tag:tag-newsletter'); do
notmuch tag +inbox- -unread id:"$id"
done
# Example: surface urgent items
echo "Urgent:"
notmuch search 'tag:urgent and tag:unread' | head
6) Schedule it
Cron (system-wide):
crontab -e
Add:
*/10 * * * * PATH="$HOME/bin:/usr/local/bin:/usr/bin" /bin/bash -lc '~/bin/ai_summarize_and_tag.sh' >> ~/.ai-mail.log 2>&1
Fedora note: make sure cronie is running:
sudo systemctl enable --now crond
Or use a systemd user timer:
~/.config/systemd/user/ai-mail.service:
[Unit]
Description=AI mail triage
[Service]
Type=oneshot
ExecStart=/bin/bash -lc '~/bin/ai_summarize_and_tag.sh'
~/.config/systemd/user/ai-mail.timer:
[Unit]
Description=Run AI mail triage every 10 minutes
[Timer]
OnBootSec=2m
OnUnitActiveSec=10m
Unit=ai-mail.service
[Install]
WantedBy=default.target
Enable:
systemctl --user daemon-reload
systemctl --user enable --now ai-mail.timer
Real-world patterns that work well
Priority triage: Tag customer domains as high priority; auto-bucket newsletters and promos.
Support queues: Generate short summaries and probable intent (billing vs. bug) to route tickets faster.
Recruiting: Draft polite declines or info requests at scale, with a human reviewing before send.
Sales follow-ups: Create concise next-step templates that sales can personalize.
Safety, privacy, and quality tips
Secrets: Keep API keys and SMTP/IMAP passwords in files with 600 permissions. Consider password managers (
pass,secret-tool) with msmtp’spasswordeval.Redaction: Use
redact_body.sh(or expand it) to strip sensitive identifiers.Human-in-the-loop: Always review drafts before sending—especially legal, finance, or HR topics.
Rate limits and costs: Batch processing (25 at a time) and short prompts keep usage predictable.
Logging: Log outcomes, not message contents, if you care about incident forensics.
Conclusion and Next Steps
You don’t need a new SaaS or GUI client to make email manageable. With Bash, isync, notmuch, msmtp, and a single AI endpoint, you can:
Summarize and label new mail
Surface urgent messages quickly
Draft high-quality replies in seconds
Next steps:
Install the prerequisites with your package manager.
Drop the scripts into ~/bin and try a dry run on a test mailbox.
Tune tags, prompts, and thresholds to your workflow.
Got a unique workflow or a thorny mail pipeline? Extend these scripts—Bash, notmuch, and AI are wonderfully composable.