消息推送助手
把同一条通知分发到多个渠道:钉钉、飞书、企业微信、微信、邮件、短信、QQ、Slack、Telegram、Bark、通用 Webhook,以及系统通知(本地,零配置不过网)。统一负责签名、格式适配、发送与结果聚合。仅用 Python 标准库,无第三方依赖。
支持对话触发("把这条消息推送到钉钉和邮件")和被其他 Skill / 自动化调用两种入口。
🚀 5 分钟上手(推荐路径)
# 1) 一键生成配置模板到 ~/.workbuddy/totorosir-push-message/config.json
python scripts/push.py sample-config --write
# 2) 编辑配置文件:只保留要用的渠道,填入凭据(不会泄露)
# Windows: notepad ~/.workbuddy/totorosir-push-message/config.json
# macOS: open -e ~/.workbuddy/totorosir-push-message/config.json
# 3) 检查哪些渠道已就绪(只读,不联网、不输出凭据值)
python scripts/check_config.py --config ~/.workbuddy/totorosir-push-message/config.json
# 4) 发送!对话里直接说,或运行命令
python scripts/push.py send --channels dingtalk,email --title "标题" --content "正文"
> 没配的渠道不会发、也不会报错中断;只发已就绪的。装完技能 ≠ 各渠道立刻全通,需先填凭据(见下方"原生 vs 第三方")。
💡 最小可运行示例
只想先跑通一个渠道? 选下面任意一个,配好即可发:
-
钉钉(群机器人)— config.json 只留:
json {"channels":{"dingtalk":{"webhook":"https://oapi.dingtalk.com/robot/send?access_token=xxx","secret":"可选"}}}
发送:python scripts/push.py send --channels dingtalk --title "测试" --content "你好" -
邮件(QQ 邮箱举例)— 只留:
json {"channels":{"email":{"smtp_host":"smtp.qq.com","smtp_port":465,"smtp_mode":"ssl","smtp_user":"123@qq.com","smtp_pass":"邮箱授权码","from":"123@qq.com","to":"to@qq.com"}}}
发送:python scripts/push.py send --channels email --title "测试" --content "你好"
何时使用
- 用户说"把这条消息推送到钉钉和邮件""通知一下团队:…""发条消息给运维群"。
- 被其他 Skill、自动化或定时任务调用,作为统一推送能力。
原生 vs 第三方 vs 本地系统:怎么选
不同"要推到的地方",原生和第三方能做的不同。原则:原生能直连就优先原生(更快、无第三方账号、数据不过中继);个人微信 / 手机实时通知等原生做不到的再用第三方。
| 想推送到… | 原生方案 | 第三方方案 |
|---|---|---|
| 我的 iPhone | —(无原生直推手机) | Bark(已内置)/ ntfy / Pushover |
| 我的 Android | — | ntfy / Gotify / Pushover |
| 我的微信(个人号) | 无(个人微信无官方 API) | pushplus(已内置)/ Server酱 / pushdeer |
| 钉钉 / 飞书 / 企业微信群 | 群机器人(已内置) | pushplus |
| 邮件 | SMTP(已内置) | pushplus |
| 短信 | 云短信 API(已内置,付费需确认) | pushplus |
| Slack / Telegram / QQ | 各自官方(已内置) | — |
完整第三方服务商能力清单见 @references/channel-taxonomy.md。
两种触发入口
- 对话触发:从用户自然语言解析 内容、目标渠道、收件人。
- 程序调用:其他流程传入结构化参数(标题、正文、渠道列表),调用
scripts/push.py。
配置(凭据只存本地)
凭据统一放本地配置文件,不要写进本技能目录或对话。默认路径 ~/.workbuddy/totorosir-push-message/config.json。
读取优先级:命令行 --config > 环境变量 MESSAGE_PUSH_CONFIG > 默认路径。
各渠道获取凭据的步骤与安全存储见 @references/setup-guide.md。
分发流程
- 确定内容:标题 + 正文 + 格式(markdown / text / html,默认 markdown)。
- 确定渠道:用户指定,或默认"全部已配置渠道";支持"全部""已配置"语义。
- 运行配置检查,剔除未配置渠道并明确告知用户。
- 调用
scripts/push.py send发送。付费渠道(短信)默认不自动发,须明确确认。 - 汇总结果清单(成功 / 失败 / 原因),失败项附一键重试命令。
输入契约(被其他流程调用)
{"title":"标题","content":"正文","content_type":"markdown","channels":["dingtalk","email"],"config":"~/.workbuddy/totorosir-push-message/config.json","confirm_paid":false}
python scripts/push.py send --config ~/.workbuddy/totorosir-push-message/config.json --channels dingtalk,email --title "标题" --content "正文"
渠道路由表
| 渠道 | 标识 | 配置关键字段 | 说明 |
|---|---|---|---|
| 钉钉 | dingtalk | webhook, secret(可选加签) | 群机器人 |
| 飞书 | feishu | webhook, secret(可选) | 群机器人 |
| 企业微信 | wecom | webhook, secret(可选) | 群机器人 |
| 微信 | pushplus_token | pushplus 中转(第三方) | |
| 邮件 | smtp_host, smtp_user, smtp_pass, from, to | SMTP | |
| 短信 | sms | url + payload_template | 付费,需确认 |
| url(通用 webhook 网关) | 见 setup-guide | ||
| Slack | slack | webhook_url | Incoming Webhook |
| Telegram | telegram | bot_token, chat_id | Bot API |
| Bark | bark | server, device_key | iOS 推送(第三方) |
| 通用 | webhook | url, payload_template | 任意 HTTP JSON 端点(可接 ntfy / Server酱 等) |
| 系统通知 | system | 无(零配置) | 本地通知中心文件 + 尽力桌面弹窗,不过网、不依赖任何账号 |
常见命令速查
| 目的 | 命令 |
|---|---|
| 生成配置模板 | python scripts/push.py sample-config --write |
| 检查渠道就绪 | python scripts/check_config.py --config ~/.workbuddy/totorosir-push-message/config.json |
| 仅系统通知(零配置) | python scripts/push.py send --channels system --title T --content C |
| 发到指定渠道 | python scripts/push.py send --channels dingtalk,email --title T --content C |
| 发 HTML 内容 | python scripts/push.py send ... --content-type html |
| 发送付费渠道(短信) | python scripts/push.py send ... --channels sms --confirm-paid |
| 运行测试套件 | python tests/test_push.py(31 项离线测试,不联网) |
输出模板(结果清单)
✅ dingtalk: 200 发送成功
✅ email: 250 已投递
❌ feishu: 签名错误 (detail)
⚠️ sms: 未发送(付费渠道,需确认)
完整结果以 JSON 输出,每项含 channel、status、http_code、detail,便于其他流程解析。
降级与停止条件
- 某渠道配置缺失:跳过,结果标注
unconfigured与缺失字段。 - 某渠道发送失败:单独记录,不影响其他渠道。
- 网络超时:每渠道独立超时(默认 10 秒),单次发送无内置自动重试;失败由调用方据结果清单对
failed项重发。 - 付费渠道(短信):未获
confirm_paid明确确认不发送。 - 凭据绝不写入本技能、日志或对话,只在本地配置文件。
复杂场景的完整行为与排查表(部分失败隔离、超时/重试、内容格式适配矩阵、付费闸门、未配置跳过、被其他流程调用如何解析结果、大批量发送、系统通知兜底等)见 @references/advanced-scenarios.md。
复杂场景处理(速览)
实际运行的边界场景要点如下,逐项详解与排查表见 @references/advanced-scenarios.md:
| 场景 | 技能行为 |
|---|---|
| 某渠道发送失败 | 单记 failed,不影响其他渠道(结果聚合,ok=false) |
| 某渠道未配置 | 标 unconfigured + 缺失字段名,不中断 |
| 网络超时 | 每渠道独立超时(默认 10s);单次发送无自动重试 |
| 重试 | 由调用方据结果清单对 failed/unconfigured 项重发 |
| 付费渠道(短信) | 默认 skipped,需显式 --confirm-paid |
| 内容格式适配 | markdown/text/html 在各渠道映射不同,详见文档 §4 |
| 系统通知 | 零配置、不过网、写本地文件 + 尽力弹窗,可作失败兜底 |
加渠道 recipe
新增 HTTP API 渠道只需三步,不动整体结构:
1. scripts/push.py 增加 build_<channel>(cfg, msg) 返回 ("http", url, payload, headers),并在 CHANNELS 注册表登记。
2. @references/setup-guide.md 增加官方配置步骤与凭据存储说明。
3. 本文件路由表加一行。
质量自查
- [ ] 已确认目标渠道均已配置
- [ ] 付费渠道已获明确确认
- [ ] 凭据来自本地配置,未出现在对话
- [ ] 输出结果清单覆盖所有目标渠道
- [ ] 改动后已跑
python tests/test_push.py全绿(新增/修改发送逻辑时必做)