Skip to content

polevych/email-agent

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Email Agent

A privacy-first email digest that runs entirely on your machine. No cloud LLMs, no paid APIs – your emails never leave your Mac.

Every morning and evening it fetches your Gmail, runs it through a local Qwen3 model via Ollama, labels important emails, and sends you a clean digest on Telegram.

Telegram digest example


How it works

  1. Fetches emails from the last 24h via Gmail API (skips social/spam)
  2. Skips emails already processed in previous runs
  3. Sends them to qwen3:14b running locally via Ollama
  4. Labels important emails with AGENT_IMPORTANT in Gmail
  5. Pushes a digest + action items to your Telegram

Requirements

  • macOS (launchd scheduling)
  • Python 3.10+
  • Ollama installed and running
  • Gmail account with API access
  • Telegram account

Setup

1. Install Ollama and pull the model

curl -fsSL https://ollama.com/install.sh | sh
ollama pull qwen3:14b

2. Clone and install dependencies

git clone <repo-url>
cd email-agent
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

3. Gmail API credentials

  1. Go to console.cloud.google.com
  2. Create a project → enable Gmail API
  3. Go to CredentialsCreate OAuth 2.0 Client ID → Desktop app
  4. Download the JSON → save as credentials.json in the project root
  5. Go to OAuth consent screenTest users → add your Gmail address

On first run a browser window opens for authorization. A token.json is saved and refreshed automatically on subsequent runs.

4. Telegram bot

  1. Open Telegram → search @BotFather/newbot → follow prompts → copy the token
  2. Start your bot, send any message to it and then open this URL to get your chat ID:
    https://api.telegram.org/bot<YOUR_TOKEN>/getUpdates
    
    Your chat ID is the "id" field inside "chat".

5. Configure secrets

cp .env.example .env

Edit .env:

TELEGRAM_TOKEN=your_bot_token_here
TELEGRAM_CHAT_ID=your_chat_id_here

6. Run manually

source venv/bin/activate
python agent.py

The first run opens a browser for Gmail authorization. After that it runs silently.


Autostart with launchd (macOS)

Create ~/Library/LaunchAgents/com.user.email-agent.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.user.email-agent</string>

  <key>ProgramArguments</key>
  <array>
    <string>/ABSOLUTE/PATH/TO/email-agent/venv/bin/python</string>
    <string>/ABSOLUTE/PATH/TO/email-agent/agent.py</string>
    <string>--scheduled</string>
  </array>

  <key>WorkingDirectory</key>
  <string>/ABSOLUTE/PATH/TO/email-agent</string>

  <key>StartCalendarInterval</key>
  <array>
    <dict>
      <key>Hour</key><integer>8</integer>
      <key>Minute</key><integer>30</integer>
    </dict>
    <dict>
      <key>Hour</key><integer>16</integer>
      <key>Minute</key><integer>30</integer>
    </dict>
  </array>

  <key>StandardOutPath</key>
  <string>/ABSOLUTE/PATH/TO/email-agent/agent.log</string>
  <key>StandardErrorPath</key>
  <string>/ABSOLUTE/PATH/TO/email-agent/agent.log</string>

  <key>RunAtLoad</key>
  <false/>
</dict>
</plist>

Replace /ABSOLUTE/PATH/TO/email-agent with your actual path (pwd in the project folder).

Load it:

launchctl load ~/Library/LaunchAgents/com.user.email-agent.plist

If your Mac was asleep at the scheduled time, launchd runs the missed job on next wake.


Customisation

Importance rules

In agent.py, find this line inside analyze_emails():

Mark as important: bills, payment deadlines, calendar invites, anything requiring a response within 48h, legal/official documents, delivery notifications.

Add your own rules in plain English – the model understands natural language:

any emails from [email protected], invoices from [email protected]

Schedule

Edit StartCalendarInterval in the plist to change when it runs. Each <dict> block is one trigger.

Run cooldown (manual runs only)

MIN_INTERVAL_HOURS = 5 at the top of agent.py prevents accidental double-runs when triggering manually. Scheduled runs via launchd (--scheduled flag) always bypass this.

Model

Change model='qwen3:14b' in analyze_emails() to any model you have pulled in Ollama. Smaller models may produce less reliable JSON output.


Project structure

email-agent/
├── agent.py                  # main script
├── requirements.txt
├── .env                      # secrets – never commit
├── .env.example              # template
├── credentials.json          # Gmail OAuth client – never commit
├── token.json                # auto-generated on first run
├── processed_emails.json     # auto-generated, deduplication cache
├── last_run.txt              # auto-generated, cooldown timestamp
└── agent.log                 # auto-generated, rotates at 1MB

Security

  • credentials.json and token.json grant access to your Gmail – never commit them
  • .env contains your Telegram token – never commit it
  • All LLM inference is local via Ollama – email content never reaches any external server
  • Ollama binds to localhost by default – not exposed to the network

About

Privacy-first email digest agent – runs 100% locally on your Mac using Ollama + Qwen3. No cloud, no paid APIs, your emails never leave your machine.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages