#!/usr/bin/env python3 """Daily digest of email organizer activity — files summary into Digests IMAP folder.""" import sqlite3 import sys from collections import defaultdict from datetime import datetime, timedelta from pathlib import Path from zoneinfo import ZoneInfo sys.path.insert(0, str(Path(__file__).parent)) from lib.notify import send_email # noqa: E402 # ── config ─────────────────────────────────────────────────────────────────── ACCOUNTS = [ { "name": "lzbellina92@gmail.com", "db": Path(__file__).parent / "gmail-organizer" / "processed.db", }, { "name": "your-email@example.com", "db": Path(__file__).parent / "gmail-organizer-dvish" / "processed.db", }, { "name": "admin@thevish.io", "db": Path(__file__).parent / "proton-organizer" / "processed.db", }, ] # ── gather stats ───────────────────────────────────────────────────────────── def get_stats(db_path: Path, since: str) -> dict: """Query processed.db for entries since a given ISO timestamp.""" if not db_path.exists(): return {"total": 0, "categories": {}, "items": []} conn = sqlite3.connect(db_path) rows = conn.execute( "SELECT message_id, category, processed_at FROM processed WHERE processed_at >= ? ORDER BY processed_at DESC", (since,), ).fetchall() conn.close() categories = defaultdict(int) items = [] for msg_id, category, ts in rows: categories[category] += 1 items.append({"category": category, "time": ts}) return { "total": len(rows), "categories": dict(categories), "items": items, } def get_sender_cache_stats(db_path: Path) -> int: """Count entries in sender cache.""" if not db_path.exists(): return 0 conn = sqlite3.connect(db_path) try: row = conn.execute("SELECT COUNT(*) FROM sender_cache").fetchone() return row[0] if row else 0 except sqlite3.OperationalError: return 0 finally: conn.close() # ── format ─────────────────────────────────────────────────────────────────── # Category colors for the HTML bar charts _CAT_COLORS = { "receipts": "#27ae60", "newsletters": "#3498db", "finance": "#f39c12", "accounts": "#e67e22", "spam": "#e74c3c", "personal": "#9b59b6", "work": "#2c3e50", } def build_html(account_stats: list[dict], hours: int) -> str: """Build inner HTML content for the digest (wrapped by notify.py template).""" total_all = sum(a["stats"]["total"] for a in account_stats) parts = [ f'
Last {hours} hours — ' f'{total_all} emails classified across {len(account_stats)} accounts.
', ] if total_all == 0: parts.append('No new emails were classified in this period.
') else: for a in account_stats: stats = a["stats"] if stats["total"] == 0: continue # Account header parts.append( f'| {cat} | ' f'' f' | ' f'
' f'\U0001f9e0 Sender cache: {a["sender_cache"]} known senders
' ) return "\n".join(parts) def build_text(account_stats: list[dict], hours: int) -> str: """Build a plain-text email body.""" total_all = sum(a["stats"]["total"] for a in account_stats) lines = [ f"Email Organizer Digest — Last {hours} hours", f"Total: {total_all} emails classified across {len(account_stats)} accounts", "", ] if total_all == 0: lines.append("No new emails were classified in this period.") else: for a in account_stats: stats = a["stats"] if stats["total"] == 0: continue lines.append(f"--- {a['name']} ({stats['total']}) ---") for cat, count in sorted(stats["categories"].items(), key=lambda x: -x[1]): lines.append(f" {cat:>15}: {count}") if a.get("sender_cache"): lines.append(f" sender cache: {a['sender_cache']} known senders") lines.append("") return "\n".join(lines) # ── main ───────────────────────────────────────────────────────────────────── def main(): import argparse parser = argparse.ArgumentParser(description="Daily email organizer digest") parser.add_argument("--hours", type=int, default=24, help="Look back N hours (default: 24)") parser.add_argument("--dry-run", action="store_true", help="Print digest without sending") args = parser.parse_args() since = (datetime.now(tz=ZoneInfo("UTC")) - timedelta(hours=args.hours)).isoformat() account_stats = [] for acct in ACCOUNTS: stats = get_stats(acct["db"], since) sender_cache = get_sender_cache_stats(acct["db"]) account_stats.append({ "name": acct["name"], "stats": stats, "sender_cache": sender_cache, }) total = sum(a["stats"]["total"] for a in account_stats) now = datetime.now(tz=ZoneInfo("America/Los_Angeles")) subject = f"Email Digest: {total} classified — {now.strftime('%b %d')}" html_body = build_html(account_stats, args.hours) text_body = build_text(account_stats, args.hours) if args.dry_run: print(text_body) return send_email(subject=subject, html_body=html_body, text_body=text_body) if __name__ == "__main__": main()