dsh-auto-email
Manifest validDaily AI digest by email: fetches ten sources (OpenAI, Anthropic, Google DeepMind, Hugging Face, arXiv, Hacker News and others), clusters near-duplicate stories, scores them in code and with an LLM, t
dsh-auto-email
每天把当天最重要的 3 条 AI 技术动态 发到你的邮箱,每条附带一段自己写的解读。
为 DSH Desktop 写的插件,也可以完全脱离 DSH 当命令行工具用。零 npm 依赖,只用 Node 内置模块。
采集 10 个信源 → 时间窗过滤 → 事件聚类 → 程序打分 → LLM 排序
→ 主题多样化精选 3 条 → LLM 写四段式解读 → 渲染邮件 → SMTP 投递
它产出什么
一封 HTML 邮件(同时带纯文本兜底),三条,每条包含:
| 部分 | 内容 |
|---|---|
| 中文标题 | 一句话说清「谁 + 做了什么 + 关键数字」,不用英文原标题 |
| 原文标题 | 保留原文,便于你自己去搜 |
| 来源与时间 | 如实标注信源层级(T1/T2/T3)、发布时间、主题簇 |
| 可信度徽章 | 🟢 交叉验证 / 🟡 单一来源 / ⚪ 信息不足,由程序客观计算,不交给模型编造 |
| 📌 事实 | 只复述原文,不添加原文没有的事实 |
| 🔍 为什么重要 | 机制与因果,不写「AI 将改变一切」式的空话 |
| 🎯 对你的影响 | 一句可操作判断(要不要跟进、等什么信号) |
| ⚖️ 存疑点 | 原文没给全的地方如实说出来 |
解读字数按 重要性 × 技术新颖度 自动伸缩:越难越新的写得越长(120–400 字),单纯的融资稿重要性可能高但新颖度低,解读就短。
安装
方式 A:dsh plugin(推荐,声明式)
dsh plugin --profile web add github:X-Xyy/dsh-auto-email
包里的 cordis.patch.yml 声明了 host 半边的挂载,package.json 的 dsh.bundle 让 loader 自动应用它 ——
不用手工拷文件、不用改 profile 的 patch 文件。重启(或刷新浏览器页面)后侧边栏面板出现;
卸载是 dsh plugin --profile web remove dsh-auto-email。
两种安装方式不要同时用:bundle 挂载的 id 是
dsh-auto-email,下面本地挂载用的是dsh-auto-email-1(带缓存破坏后缀),同时存在会让同一个插件被挂两次。
方式 B:本地挂载(开发 / 改代码时用)
一条命令(拷贝 + 挂载,幂等,可回滚):
node scripts/install.mjs --mount # 先加 --dry-run 看它会写哪里
node scripts/install.mjs --unmount # 卸载:patch 文件恢复到字节级一致
它做两件事:把 package.json 与 lib/ 拷到 %APPDATA%\dsh-desktop\plugins\dsh-auto-email,
再在 %APPDATA%\dsh-desktop\harness\profiles\web\cordis.patch.yml 里维护一个带围栏标记的挂载块
(写之前先备份成 cordis.patch.yml.bak-<时间戳>)。
手工等价操作是:
$dest = "$env:APPDATA\dsh-desktop\plugins\dsh-auto-email"
New-Item -ItemType Directory -Force $dest | Out-Null
Copy-Item package.json $dest -Force
Copy-Item lib $dest -Recurse -Force
- insert:
- id: dsh-auto-email-1
name: "file:///C:/Users/<你>/AppData/Roaming/dsh-desktop/plugins/dsh-auto-email/lib/index.js"
-N后缀与?rev=后缀都是给 dsh-hmr 的缓存破坏标记,不是版本号。改了代码又不想重启,就把dsh-auto-email-1改成dsh-auto-email-2——node scripts/install.mjs --mount --rev 2会同时改 id 和模块 URL,两者都新才会被重新导入。
然后写凭据文件(见下一节)。挂载后它会出现在插件列表里,
在插件管理面板里直接开关就是启用/禁用。host 半边由 dsh-hmr 热加载(logs/harness.log 里出现 auto-email: ready 即为加载成功);
browser 半边的侧边栏面板需要刷新一次浏览器页面才会出现。
⚠️ 如果你已经在用另一个版本(比如 Python 版 + Windows 任务计划程序)发同一封日报, 先把那个停掉再启用这个。两套调度都在跑 = 每天收到两封。
从 Python 版接管历史(可选)
如果之前用过本仓库的 Python 版,它的 seen.json 记录了已推送过的标题哈希。本插件的哈希算法与它逐位一致
(都是「标题归一化后取 sha1 的前 20 位十六进制」),所以可以整库搬过来,不会因为搬家而重发旧新闻:
node bin/dsh-auto-email.mjs --migrate <Python 版的 seen.json 路径>
合并是并集,且刻意保守:已存在的哈希保留更新的日期,last_sent_date 只在旧文件更新时才覆盖 ——
取旧了会重发,取新了会漏发,两个方向都不能错。导入幂等,第二次运行报告 0 条新增;
导入前把现有 state.json 备份成 state.json.bak-<时间戳>;只写状态文件,不发信、不动归档。
方式 C:命令行 / 任务计划程序
node bin/dsh-auto-email.mjs --dry-run -v # 先干跑看看会选哪三条
node bin/dsh-auto-email.mjs # 真发
node bin/dsh-auto-email.mjs --if-due # 幂等模式,给定时器调用
node bin/dsh-auto-email.mjs --migrate <file> # 并入 Python 版 seen.json
Windows 任务计划程序里注册一个每天 08:00 的任务,动作填 node <仓库路径>\bin\dsh-auto-email.mjs --if-due。--if-due 保证:没到点不发、今天发过就不再发,所以你可以放心把登录、解锁、每 30 分钟轮询都挂上同一个命令,永远不会重复投递。
配置
凭据
凭据不写进配置文件,单独放一个文件:
%APPDATA%\dsh-desktop\dsh-auto-email.secret.json
{
"deepseek_api_key": "sk-...",
"smtp_username": "you@qq.com",
"smtp_auth_code": "你的SMTP授权码",
"recipients": ["you@qq.com"]
}
deepseek_api_key用 DeepSeek 开放平台 的 Key,也可以用环境变量DEEPSEEK_API_KEY。smtp_auth_code是邮箱的 SMTP 授权码,不是登录密码。QQ 邮箱在「设置 → 账号 → POP3/SMTP服务」里开启后生成。- 用非 QQ 邮箱时,改
email.smtp_host/email.smtp_port/email.use_ssl即可。
检查是否配好:
node bin/dsh-auto-email.mjs --status # 列出所有缺失项
node bin/dsh-auto-email.mjs --send-test # 只验证 SMTP 登录,不发信
可调项
改成 DSH 插件选项、或写进 <userData>\dsh-auto-email\config.json、或命令行 --config。未列出的键用默认值。
| 键 | 默认 | 说明 |
|---|---|---|
digest.top_n | 3 | 每封邮件几条 |
digest.window_hours | 26 | 主时间窗(小时)。比 24 多 2 小时,防调度抖动 |
digest.fallback_window_hours | [48, 72] | 主窗不够时逐级回溯 |
digest.min_importance | 6 | 重要性门槛。调高=更挑剔,更容易触发回溯 |
schedule.time | "08:00" | 每天推送时刻(HH:MM) |
schedule.catch_up_hours | 12 | 错过多久之内还补发。避免补发一份过期新闻 |
schedule.enabled | true | 关掉调度,只留手动 |
digest.send_when_empty | true | 当天没有任何达标内容时,是否仍发一封「今日无重大动态」 |
llm.model | "deepseek-chat" | 排序与解读用的模型 |
使用
对话里的四个工具
| 工具 | 作用 |
|---|---|
dsh_auto_email_status | 今天发了没、下次什么时候发、配置缺什么 |
dsh_auto_email_preview | 只跑一遍看会选哪三条,不发信、不写状态 |
dsh_auto_email_send | 立即真发一封 |
dsh_auto_email_configure | 临时改条数 / 时间 / 门槛 / 暂停 N 天 |
侧边栏面板
设置区上方多一个「每日AI」入口,面板里能看今日状态、点立即发送、临时暂停、直接改配置和凭据、浏览历史归档。
它是怎么选的
这一步决定了这东西有没有用,所以写清楚。
信源(10 个,按层级加权)
| 层级 | 权重 | 信源 | 抓取方式 |
|---|---|---|---|
| T1 一手官方 | 1.0 | OpenAI News | RSS |
| T1 | 1.0 | Anthropic News | 页面解析(官方无 RSS) |
| T1 | 1.0 | Google DeepMind Blog | RSS |
| T1 | 1.0 | Hugging Face Blog | hf-mirror 镜像 RSS |
| T1 | 1.0 | arXiv cs.AI/CL/LG/CV | 官方 API |
| T2 权威媒体 | 0.7 | 量子位 | RSS |
| T2 | 0.7 | TechCrunch AI | RSS |
| T2 | 0.7 | MIT Tech Review AI | RSS |
| T2 | 0.7 | Simon Willison | Atom + AI 关键词过滤 |
| T3 社区信号 | 0.4 | Hacker News | Algolia API,≥50 分 |
刻意不接:机器之心(全站 WAF,客户端无法绕过)、TLDR AI / The Rundown / Import AI / The Batch 等 Newsletter、AITOP100 / alphaXiv 等聚合器。理由:Newsletter 和聚合器都是对上述 T1/T2 的二次加工,接进来等于把同一件事数两遍,会虚增「交叉验证」的可信度 —— 而交叉验证正是这套东西最硬的抓手。
三道闸门
闸门 1 · 程序打分(可复现、可审计)
0.35 × 信源层级 + 0.25 × min(独立信源数/3, 1) + 0.20 × 时效衰减
+ 0.20 × 影响信号词命中 − 0.15 ×(摘要为空)
闸门 2 · LLM 逐条打分:重要性 0–10 与技术新颖度 0–10 是两个独立轴。
- 重要性看影响面;新颖度看有没有技术增量 —— 所以融资稿能进(重要),但解读短(不新)。
- 四条硬性扣分:标题党/PR 软文/空洞评论;二手转述(
Quoting X、Weekly roundup)新颖度一律 ≤3;与 AI 技术无关的纯基建/人事/会议预告;摘要为空的条目重要性 ≤4、新颖度 ≤5,不得进精选。 - 融合:
final = 0.70 × (0.55×重要性 + 0.45×新颖度)/10 + 0.30 × 程序分。给程序分留 30% 是故意的 —— 即使 LLM 判偏,也不会选出纯噪音。
闸门 3 · 精选约束:主窗内重要性 ≥6 的不足 3 条,就回退 48h、再 72h(宁缺毋滥);三条必须来自不同信源、不同主题、不同事件;T3 社区信源永不单独成条,必须有第二个信源印证。
一个诚实的定位
这套标准不是「真理判定器」,是「证据强度排序器」。它把「谁说的、几个来源说的、有没有技术增量、原文给没给细节」变成可审计的分数,并把依据印在邮件里。它做不到替你判断某篇论文的方法是否成立、某笔融资是否合理 —— 那个判断权留给你。
可靠性设计
| 风险 | 处理 |
|---|---|
| 电脑在推送时刻睡眠/关机 | 四个触发器:08:00 定时 + 每 30 分钟轮询 + 登录 + 解锁,配合 --if-due 幂等闸门 |
| 重复投递 | --if-due 读 state.json 的 last_sent_date,当天发过就退出 0 |
| 半夜开机被提前推送 | --if-due 还有第二道守卫:早于 schedule.time 一律不发 |
| 单个信源抓取失败 | 失败信源在其余信源跑完后统一重试一次;仍失败就如实写进邮件告警条 |
| arXiv / 站点限流 | 读 Retry-After,指数退避;403/404/410 直接放弃(重试没意义) |
| 模型把段标题写进正文 | _strip_label 循环剥离 |
| 模型把解读写太长 | 三层控制:字段子预算 → 外科手术式重写(只改超标字段)→ 仍超标则按句子边界程序化裁剪 |
| 模型幻觉来源 | 可信度徽章由程序算;无摘要条目降权且不得进精选 |
| 状态文件损坏 | 读失败就从空状态开始(最坏是多发一条,不是不发) |
| 服务器不接受中文发件人 | From 显示名做 RFC 2047 编码 |
故障排查
| 现象 | 原因与处理 |
|---|---|
--status 列出「未配置…」 | 凭据文件路径或键名不对,对照上一节 |
SMTP 登录成功 但收不到信 | 看垃圾箱;把发件人加白名单 |
Google DeepMind ... fetch failed | 该域名在部分网络下间歇性不可达,不是代码问题。重试机制会自动再试一次 |
| 邮件里出现「⚠️ 部分信源本次不可达」 | 同上,属设计内的如实披露 |
| 选出来的三条看着不重要 | 调低 digest.min_importance 或放宽 window_hours;也可以跑 --preview 看完整候选池 |
--preview 每次都换内容 | LLM 采样非确定,同一份代码多跑几次结果会变。以「是否落在设计容差内」验收 |
| 想重发同一批内容 | --ignore-seen |
| 面板里改了配置不生效 | 配置写在 <userData>\dsh-auto-email\config.json,DSH 里改完点保存会立刻重新解析 |
开发
node test/all.mjs # 全部离线套件,一条命令;142 项检查
node test/all.mjs --live # 额外联网实测 10 个信源的条数与样例
# 也可以单独跑
node test/render.mjs # 离线:两个渲染器、HTML 转义、空链接(19 项)
node test/plugin.mjs # 离线:配置分层、时区算术、MIME 组装、幂等闸门、迁移合并、在跑时不重复触发(32 项)
node test/digest.mjs # 离线:聚类、打分、精选、字数控制(39 项;本地 http stub,不花 API 额度)
node test/feeds.mjs # 离线:RSS 2.0 / RSS 1.0-RDF / Atom 的边角(19 项)
node test/http.mjs # 离线:429 Retry-After、确定性失败不重试(15 项)
node test/host-half.mjs # 离线:用桩包加载 host 半边,验证 4 工具 / 8 路由的注册形状(18 项)
node test/sources-live.mjs # 联网:10 个信源逐个实测(会写 test/sources-live.out.txt)
这些套件全部离线,不会花 API 额度、不会发信:LLM 与 SMTP 都打到本地 stub。
test/digest.mjs 还会读 test/difflib-parity.json,把 JS 的 sim() 与 Python
difflib.SequenceMatcher 的 15 组比值比对到 6 位小数 —— 聚类阈值是从 Python 版调出来的,
两边算不出同一个数,阈值就失去意义。
零依赖是硬约束:只用 node: 内置模块和全局 fetch。不要为了省几十行引入包 —— 插件要在别人的机器上 clone 下来就能跑。
移植契约与两套实现的对齐关系见 docs/PORT-CONTRACT.md。
写 DSH 插件时踩过的坑(实测,非推测)
这些错误都只在运行中的 DSH 里才会现形,node --check 与离线单测原本看不见;现在
test/host-half.mjs 用桩包复刻了它们,改坏了会在本地立刻失败。
| 坑 | 症状 | 正确做法 |
|---|---|---|
对象型工具参数缺 additionalProperties | 插件整块加载失败:JsonSchemaError: unsupported JSON schema: parameters.set.additionalProperties must be explicitly true or false;/status 返回 401(路由根本没注册) | defineTool 的 parameters 里任何 type: 'object' 的节点都要显式写 additionalProperties: true/false |
启动器 id 与 ?rev= 没一起换 | 改了 lib/ 却像没生效:dsh-hmr 对同一 row 复用已导入的模块 | 用 node scripts/install.mjs --mount --rev N 同时改 row id 与模块 URL,两者都新才会重新导入 |
| browser 半边没注册槽位就渲染 | 控制台报 slot "sidebar.footer.action" is not declared —— 该槽位由内置侧边栏在 React 渲染期间声明,比插件加载更晚 | 用 ctx.slots.inject(key, factory),而不是裸调 ctx.slots.register() |
| 槽位注册值不是 React 组件 | 面板位置渲染不出任何东西(渲染器以 jsx(Comp, {...kit}) 挂载) | 注册一个组件,不要注册脱离的 DOM 元素 |
| 改动 client.js 后不刷新 | 面板还是旧的 | browser 半边的热更新需要 pnpm run dev:web;没有它时刷新一次浏览器页面 |
.ps1 用中文注释且不带 BOM | Windows PowerShell 5.1 按系统 ANSI 代码页解码,报假的 Missing closing '}' | 脚本保持纯 ASCII;必须用中文时让文件带 UTF-8 BOM |
.bat 里写中文注释 | cmd.exe 在 chcp 65001 生效前就按旧代码页解析,报 'M' is not recognized as an internal or external command | 批处理保持纯 ASCII |
查证入口:
%APPDATA%\dsh-desktop\logs\harness.log。插件加载失败会在这里写error dsh-auto-email: ...和warn hmr: Error: dsh: warning: 1 entry did not activate; 加载成功会写auto-email: ready; at=08:00 tz=Asia/Shanghai ...。
目录结构
lib/
index.js 入口 shim(?rev= 缓存破坏)
plugin.js host 半边:4 个工具 + 8 条路由
client.js browser 半边:侧边栏面板
config.js 配置分层、校验、路径解析
service.js 调度、幂等闸门、状态持久化
pipeline.js 六步编排
sources.js 10 个信源 + RSS/Atom/RDF 自写解析器
digest.js 聚类、双级打分、精选、字数控制、LLM 调用
render.js 纯文本 + 内联样式 HTML 渲染
mail.js 自写 SMTP 客户端(465 隐式 TLS / 587 STARTTLS)
state.js 状态库(仅存标题哈希)
item.js 流水线行记录
许可
Comments
Loading…
Similar plugins
by JohnXu22786
RSS/Atom digest bundle for dsh: subscribe to feeds, fetch on a schedule, dedupe exact and near-duplicate stories, and get LLM-summarized daily Markdown briefings delivered into conversations and/or fi
★ 1
↓ 105/wk
MIT
TypeScript
Sep 29, 2026
dsh plugin --profile web add dsh-rss-digestby Vergil-long
DeepSeek Harness 邮件通知插件:任务完成 / 工具等你授权 / 助手在等你回答时发邮件给你,配一个「离开模式」开关。设置全部集成在 harness 设置界面里。
★ 1
MIT
JavaScript
Sep 13, 2026
dsh plugin --profile web add dsh-email-notifyby wuuuuu123-32
Emails you when a long turn finishes or errors, when a goal completes or blocks, and when an approval or a question has waited past a timeout; each new notification deletes the previous one from the i
★ 0
MIT
JavaScript
Oct 17, 2026
dsh plugin --profile web add dsh-mailbox-notifyby Leo-Cjw
Desktop notifications for DSH: a system toast, a per-kind chime and a tab-title marker when the agent needs you (approval, plan review, question) or a reply finishes in the background; errors, interru
★ 3
↓ 85/wk
MIT
JavaScript
Oct 17, 2026
dsh plugin --profile web add dsh-pharosby lpf20200901
给 AI 编码助手的跨会话长期记忆:自动注入、只推变化。DSH 插件 + 零依赖 CLI。
★ 0
MIT
JavaScript
Oct 15, 2026
dsh plugin --profile web add dsh-memory-deltaby Xixixiao2007
把每轮回答压成一条详略得当的摘要发到邮箱,并支持邮件回信作答或续接对话(DeepSeek Harness 插件)
★ 0
MIT
JavaScript
Oct 5, 2026
dsh plugin --profile web add dsh-mail-digest