Triage several Gmail accounts into one Discord feed. Urgent mail reaches you in 30 minutes; marketing never does.
3,000 unread across 3 accounts, none of it sorted
↓
Gmail Digest
↓
#digest every 24h — an interview / academic / important summary
#urgent within 30min + @you — interviews, assessments, a professor's reply
#ignored marketing and receipts, there if you want them
#bot /search /mute /boost /push — slash commands with autocomplete
Filters match strings. They cannot tell "we'd like to schedule an interview" from "we'd like to tell you about our bootcamp", and they cannot notice that a recruiter replied in a thread you were waiting on.
This does three things filters cannot:
- Classifies by intent, with an optional LLM pass that must quote the source line for any judgment it makes — so a wrong call is visible rather than mysterious.
- Tracks what you are waiting for. Reply to a scheduling email and it opens a pending item; when the confirmation lands it closes it.
- Escalates on a clock. A digest is fine for a newsletter and useless for an assessment that expires tonight, so urgent mail gets its own 30-minute path.
- Multiple Gmail accounts — work, school, personal, as many as you have
- Local by default — OAuth tokens stay on your machine; readonly scope
- Discord-native slash commands with autocomplete:
/search/mute/boost/push/ask - Action checklist — anything needing a reply is surfaced first and never truncated
- Optional LLM enhancement — bring your own key. Anthropic, OpenAI, OpenRouter, Groq, Together, or local Ollama. Every LLM judgment carries a quote from the email.
- Ask questions in plain language — "did Acme reply yet", "what's tomorrow morning" — answered from your own mail with citations
- WeCom (企业微信) push as an alternative to Discord
- Rolling 24h schedule, adjustable, plus manual pull any time
- 100 tests, run with
pytest
git clone <this repo> && cd gmail_digest
pip install -r requirements.txt
# 1. Gmail OAuth — put your Google Cloud client_secret at
# ~/.config/gmail_digest/credentials.json, then per account:
python digest.py auth you@gmail.com
python digest.py auth you@work.com
# 2. Try it without any Discord setup
python digest.py digest --dry-run
# 3. Wire up Discord (bot token + channel ids) and run the daemon
python bot_daemon.pyScope requested is gmail.readonly. Cleanup features that trash mail ask for a separate, explicitly granted scope, and confirm before acting.
The classifier ships with generic rules. The things that are about you — your school, your bank, how you want the assistant to describe you — live in one optional file:
Everything is optional and the defaults work without it. Nothing school-specific is compiled into the code.
| module | what it does |
|---|---|
gmail_client.py |
OAuth, message fetch, normalised Message |
classify.py |
rule-based categorisation — the deterministic first pass |
llm.py |
optional LLM judgment, multi-provider, quote-required |
digest.py |
digest assembly + CLI |
discord_bot.py / discord_channel.py / bot_daemon.py |
Discord surface |
reminders.py / state.py |
pending items, reminders, persistence |
qa.py / nl_router.py |
plain-language questions over your own mail |
wecom.py / wechat_*.py |
WeCom push |
user_profile.py |
the per-user config above |
cleanup.py |
opt-in bulk cleanup, always confirmed |
Rules first, model second. Most mail is decidable without an LLM, and rules are free, instant, and debuggable. The model only sees what the rules were unsure about — which keeps the bill near zero and means an outage degrades the product rather than breaking it.
Every LLM judgment must quote the email. The schema requires an evidence_quote. A model that cannot point at a line is not allowed to star a message. This is the difference between a classifier you can audit and one you have to trust.
Failures are visible. A message that could not be fetched or judged is reported as such rather than silently dropped — a triage tool that quietly loses mail is worse than no tool.
MIT.
完整的分步安装说明(含 Google Cloud 项目、Discord bot、launchd 自启)见 SETUP_zh.md。