Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Gmail 邮件整理台

用 TypeSafe 的 Jev 模型给 Gmail 邮件自动分类:判断每封邮件属于哪一类、要不要你亲自处理、多紧急, 先出报告给你过目,确认后再批量写回 Gmail 标签。附带钓鱼检测、退订分析、Gmail 过滤器生成, 以及一个 Chrome 侧边栏面板,不用敲命令也能用。

  • 只加标签,不删邮件:代码里没有任何删除调用;每次写回都记日志,可一键撤销
  • 先看再改:所有会改 Gmail 的操作都有预演模式和确认步骤
  • 数据不出本机:邮件只在你电脑上处理,只把主题和摘要发给 Jev 做分类
  • 很便宜:约 900 封邮件全量分类 ≈ $0.06

平台:核心脚本在 macOS / Linux / Windows 上都能跑。「每天自动整理」「服务开机自启」 和系统通知用的是 macOS 的 launchd 和 osascript,只支持 macOS。

功能一览

功能 脚本 会改 Gmail 吗
拉取邮件(只读 header 和摘要) fetch.py 否
Jev 分类 + 优先级打分 classify.py 否
分类报告 report.py 否
写回标签 / 撤销 apply.py / undo.py 是,可撤销
钓鱼检测 phish.py 否
退订分析(出清单,不替你点) unsubscribe.py 否
把固定发件人编译成 Gmail 过滤器 rules.py --apply 时是,可撤销
每天自动跑一轮,有要紧事才通知 daily.py 是,可撤销
Chrome 侧边栏面板的本地服务 server.py 按你点的操作
fetch.py  →  classify.py  →  report.py  →  apply.py
拉邮件        Jev 分类        出报告        写回标签(可 undo.py 撤销)

安装

需要 uv(Python 版本和依赖它会自动装好):

git clone <这个仓库的地址>
cd <仓库目录>
uv sync

1. Google OAuth 凭据(每个人用自己的)

  1. 打开 https://console.cloud.google.com ,新建一个项目
  2. 「API 和服务」→「库」→ 搜 Gmail API → 启用
  3. 「OAuth 同意屏幕」→ 用户类型选外部 → 把你自己的 Gmail 地址加进「测试用户」(不加会报 403)
  4. 「凭据」→ 创建 OAuth 客户端 ID → 应用类型选桌面应用
  5. 下载 JSON → 重命名为 credentials.json → 放到项目根目录

权限是 gmail.modify:能读邮件、能打标签,不能永久删除邮件。授权令牌存在本地 token.json,不会上传到任何地方。

应用处于「测试」状态时,Google 的授权大约 7 天过期,到时重新跑一次会再弹浏览器登录。

2. TypeSafe API Key

在 https://console.typesafe.ai/keys 创建一个 key,然后:

echo 'TYPESAFE_API_KEY=你的key' > .env

credentials.json、token*.json、.env、data/、out/ 都在 .gitignore 里,不会被提交。

快速上手

# 先拿 20 封试试分类准不准(几乎不花钱)
uv run src/fetch.py --limit 20          # 首次会弹浏览器授权
uv run src/classify.py
uv run src/report.py                    # 看 out/report.csv

# 满意了再全量
uv run src/fetch.py
uv run src/classify.py                  # 约 900 封 ≈ 3 分钟、$0.06
uv run src/report.py

# 确认分类准了再写回
uv run src/apply.py --dry-run           # 预演:只打印要做什么,不碰 Gmail
uv run src/apply.py --apply             # 真正写回
uv run src/undo.py out/applied-<时间戳>.jsonl   # 后悔了就撤销

以后定期增量更新:

uv run src/fetch.py --since 7d && uv run src/classify.py && uv run src/apply.py --apply

Chrome 扩展:邮件整理台

不想敲命令,就在浏览器侧边栏里点按钮。扩展只管界面,活还是上面这些脚本干, 所以要先开着本地服务:

uv run src/server.py --install  # 推荐(macOS):装成开机自启,装一次以后不用管
uv run src/server.py --status   # 看服务在不在跑、日志尾部
uv run src/server.py --uninstall

uv run src/server.py            # 或者临时手动开,监听 127.0.0.1:8765
uv run src/server.py --open     # 不装扩展也行:直接在浏览器里打开同一个面板

装扩展:Chrome 打开 chrome://extensions → 右上角开「开发者模式」→ 「加载已解压的扩展程序」→ 选项目里的 extension/ 目录。之后点工具栏图标打开侧边栏。

页 能做什么
今天 今天要办几封、最要紧的几封(点开直达 Gmail)、可疑邮件提醒;「再整理一轮」
本轮结果 分类分布、可疑邮件、报告下载;待你确认:模型拿不准的邮件点 ✓ 认可或改类;底部一键写回
工具 钓鱼检测、退订分析、编译过滤器、撤销;单步跑每个环节
记录 每天自动整理的开关和时间;历次写回一键撤销;最近运行,失败的能看日志

「再整理一轮」旁边的只预演默认勾着:跑一遍但不改 Gmail,看完结果再点「写回 Gmail」。 跑任务时有分步进度和实时日志,可以停止,也可以放后台、跑完弹系统通知。

「待你确认」的判断存在 data/overrides.json,和分类结果分开放 —— 重跑 classify.py --force 不会冲掉你的判断。

本地服务的护栏:

  • 只听 127.0.0.1,只接受扩展和它自己页面的请求 —— 你逛的其他网站没法借浏览器 偷偷让它跑任务(校验 Origin 和 Host,防 DNS rebinding)
  • 会改 Gmail 的操作界面上先弹确认,服务端也强制要求确认
  • 同一时间只跑一个任务;撤销只接受 out/ 里真实存在的日志文件

为什么这样分工

Jev 不是 LLM,它不生成文本,只返回带概率的类型化判断(Choice / Score)。所以:

环节 谁做
拉邮件、去重、拼输入 Python 代码
「属于哪一类 / 要不要我处理 / 多急」 Jev
阈值、打不打标签、打哪个 Python 代码(taxonomy.yaml 的 policy)

好处:改阈值、换策略不需要重新推理 —— 完整概率分布都存在 data/classified.jsonl 里。

退订助手

按发件人(不是按邮件)评估哪些订阅能退,产出分档清单和退订链接:

uv run src/unsubscribe.py                  # 分析带退订链接的发件人
uv run src/unsubscribe.py --reuse --html   # 复用判断 + 生成能点的本地网页($0)

复用已有的分类结果,只对每个发件人多问三个问题:能不能退、是不是你真实持有账号的服务商、 内容本身值多少。分四档写进 out/unsubscribe.csv:

档 含义
🟢 建议退订 退了不会后悔
🟡 人工判断 模型拿不准,或者你其实在读
🔴 小心 有账号关系,退营销可能连带退掉通知
⚪ 保留 你在读这些

为什么「平均优先级低」不等于能退:银行、券商的营销邮件平均优先级可能很低,但那是你真实开户的 服务商,而且它们的营销邮件和交易通知常常来自相近的域名。所以除了「值不值得读」,必须单独问 「是不是你真实持有账号的服务商」—— 这一档一律不进「建议退订」。

三道硬兜底写在 taxonomy.yaml 的 unsubscribe.policy 里,优先于模型判断:

  • 有账号关系概率 ≥ 0.40 → 降级
  • 平均优先级 > 0.60 或最高优先级 > 2.0 → 降级
  • 未读率 < 0.35(说明你每封都打开了)→ 降级到人工判断

--html 产出的 out/unsubscribe.html 刻意是本地文件而不是在线页面 —— 退订链接里带着邮箱 标识和一次性令牌,传给任何第三方都等于泄出去。脚本不会替你点退订:那是对外部服务的不可逆 操作,你自己看过再点。

规则编译器

有些发件人几乎总是同一类(≥3 封、纯度 ≥90%、置信度 ≥0.75)。这种「看发件人就够了」的情况 不需要每次都花钱推理 —— 编译成 Gmail 原生过滤器:新邮件一到就有标签,秒级、零成本。

uv run src/rules.py                 # 预览(不碰 Gmail)
uv run src/rules.py --xml           # 产出 out/filters.xml,手动导入
uv run src/rules.py --apply         # 直接用 API 创建(需要多授权一次)
uv run src/rules.py --undo out/filters-applied-<时间戳>.jsonl
权限 怎么用
--xml 不需要额外权限 Gmail → 设置 →「过滤器和屏蔽的地址」→ 底部「导入过滤器」
--apply 需要 gmail.settings.basic 会再弹一次浏览器授权,存在 token-settings.json

过滤器只加标签,不归档、不删除、不标已读;你自己的邮箱地址会被自动排除。 过滤器只对今后的邮件生效,已有邮件的标签由 apply.py 负责。

钓鱼检测

header 归代码判,内容归 Jev 判:

uv run src/phish.py              # 只对可能被伪装的分类跑推理
uv run src/phish.py --hard-only  # 只跑 header 硬校验($0)
uv run src/phish.py --reuse      # 复用上次推理,只重算阈值($0)

代码这一半读 Gmail 已经验好的 spf / dkim / dmarc,再查域名仿冒:

  • 形近字:rn 冒充 m、1 冒充 l、vv 冒充 w —— rnicrosoft.com 和 microsoft 的编辑距离是 2,直接比抓不到,得先归一化
  • 改字符:容忍度随品牌名长度走,短名字差两个字符往往就是另一家真实公司
  • 品牌名 + 信任词:paypal-secure.com。光「包含品牌名」不够 —— amazonaws.com 就是 Amazon 自己的
  • 同品牌换后缀不算仿冒:amazon.de / amazon.co.jp 都是人家自己的域名

可信域名不是写死的品牌榜,而是从你收件箱自己的历史里长出来的:反复出现且 dmarc 通过的 域名就是可信域名。

Jev 这一半只看主题和正文开头,回答三件 header 看不出来的事:这封信自称是谁、有没有在制造 紧迫感、有没有在骗你点进去输东西。输入里刻意不放 header 校验结果 —— 两条证据各自独立, 互相印证才有价值。

每天自动整理(macOS)

每天自动跑一轮,只在真有要紧事时才弹通知 —— 一个每天都响的提醒等于没有提醒。

uv run src/daily.py                 # 手动跑一轮
uv run src/daily.py --dry-run       # 不写 Gmail、不弹通知,只打印
uv run src/daily.py --install       # 装成 launchd 定时任务,每天 9:00
uv run src/daily.py --install --at 8:30
uv run src/daily.py --status
uv run src/daily.py --uninstall

串起 fetch --since 7d → classify(已分类的自动跳过)→ apply --apply → phish --reuse, 把新出现的「⚡ Today 今天办」和可疑邮件合并成一条系统通知,同一封不会天天提醒。 任一步失败就中断、不推送 —— 半截的数据写回 Gmail 比不写更糟。

⚠ 它会自动写回 Gmail 标签(只加标签、可撤销)。不想自动写就用 --dry-run,或者别装定时任务。

调分类

全部改 taxonomy.yaml,不用动代码:

  • categories —— 加 / 删分类、改判断标准。描述要用英文写(Jev 的英文能力强于中文), 可以嵌中文线索词(日报 / 验证码 / 订单)帮模型对上号
  • policy —— 置信度阈值、归档条件、「需要你处理」清单的入选条件
  • rename_labels —— 想把你 Gmail 里原有的标签并进这套分类时才填(默认空,不动你的标签)

改完重跑 uv run src/classify.py --force(几分钱)。

安全护栏

  • 低置信度(< 0.6)和 other 一律不打标签,留给你人工确认
  • 默认只加标签,不移出收件箱;要归档得显式 --archive
  • 每次写回都记 out/applied-<时间戳>.jsonl,undo.py 可完整回滚
  • 代码里没有任何 trash / delete 调用
  • 只读 header 和 Gmail 给的正文摘要(snippet),不读完整正文,附件不碰
  • 发给 TypeSafe 之前遮掉验证码(独立的 4~8 位数字,见 src/redact.py);金额、年份、订单号保留。实测分类结果几乎不受影响

成本

Jev 按输入 token 计费($0.042 / 百万),输出免费。每封邮件约 1600 token。

规模 成本
20 封试用 ≈ $0.001
约 900 封全量 ≈ $0.06
每周增量 50 封 ≈ $0.003

许可

MIT

About

用 TypeSafe Jev 给 Gmail 自动分类:写回标签、钓鱼检测、退订分析、Chrome 侧边栏面板

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages