DSAWiki
11使用 · 触达

推送到你每天真正会看的地方

企业微信、飞书、Telegram、邮件……同一份结论,如何适配不同渠道的「脾气」。

约 6 分钟 实战 第 11 章 · 使用

同一份结论,发到企业微信、Telegram 和邮件里,长相必须不同。企业微信对字节数敏感,Telegram 会被切成多条,邮件需要完整的 HTML,Discord 对频率限制很严格。通知层的价值,就是把这些「渠道脾气」吸收掉,让上层只管产出内容。

类比:同一份通知,三种送达方式

物业要通知全体业主停水。贴在电梯里要短(企业微信),发短信要分段(Telegram),寄信要正式(邮件)。内容核心一样,但排版、长度、称呼都不同。DSA 的通知服务就是那个「物业文案组」:拿到结论后,按渠道重新排版、分片、降噪,再逐户投递。

14 类渠道:能力不同,策略不同

渠道接入方式格式 / 限制适合
企业微信群机器人 WebhookMarkdown 约 4000 字节、文本约 2000;图片 ≤ 2MB国内团队、日常简报
飞书Webhook / App Bot长消息按字节分片(默认 20000);支持富文本与云文档正式报告、归档
钉钉群机器人 / StreamMarkdown;支持 Stream 长连接国内企业 IM
TelegramBot Token + Chat ID4096 字符分片;Markdown/HTML个人、海外
DiscordWebhook约 2000 字符;429 按 retry_after 退避社区、小团队
SlackBot TokenMrkdwn 方言,表格需转换海外团队
邮件SMTPHTML 正文,适合长文归档、正式记录
PushoverAPI Token移动推送,长度有限个人即时提醒
ntfyHTTP Topic纯文本/简单 Markdown自建、轻量
Gotify自建服务纯文本私有部署
PushPlusToken微信公众号推送国内个人
ServerChan3SendKey微信服务号国内个人
自定义 Webhook任意 HTTP 端点由你定义的 JSON接入自有系统
AstrBot适配器取决于适配器机器人生态集成

项目里常说「14 类渠道」,指的就是这些发送器。你不需要全配,配一个能稳定收到消息的就够了。配得越多,失败面越大,噪音也越大。

最少可用配置

企业微信:WECHAT_WEBHOOK_URL;飞书:FEISHU_WEBHOOK_URL;Telegram:TELEGRAM_BOT_TOKEN + TELEGRAM_CHAT_ID;Discord:DISCORD_WEBHOOK_URL;邮件:EMAIL_SENDER + EMAIL_PASSWORD + 收件人。至少配一个,否则分析能跑完但不会主动找你。

路由:什么消息,发给谁

通知不是「一股脑全发」。系统按消息类型路由:

消息类型配置变量典型内容
报告NOTIFICATION_REPORT_CHANNELS每日仪表盘、个股报告、大盘复盘
告警NOTIFICATION_ALERT_CHANNELS价格触发、指标触发、决策信号触发
系统错误NOTIFICATION_SYSTEM_ERROR_CHANNELS任务失败、配置错误、渠道异常

变量为空表示「全量渠道」,未知渠道名也按全量处理,避免因为拼写错误而静默丢消息。这种 fail-open 的选择有代价:可能多发,但比「以为配好了其实没发」好。

降噪:别让系统变成骚扰

通知层有一道「降噪门」,在真正发送前检查:

  • 严重级:info < warning < error < critical,低于 NOTIFICATION_MIN_SEVERITY 的直接丢弃。
  • 静默时段:HH:MM-HH:MM 内不发(时区由 NOTIFICATION_TIMEZONE 指定)。
  • 去重 TTL:相同内容在窗口内只发一次。
  • 冷却:同一规则触发后,短时间内不重复提醒。

这些状态保存在进程内存里,并且是 fail-open 的:降噪逻辑本身出错时,倾向于放行而不是拦截。代价是多实例部署时状态不共享——每个实例都可能各发一次。要严格去重,就只保留一个发送入口。

格式适配:一份内容,多种排版

报告可以在两个层面渲染:默认在通知层用字符串拼装;开启 REPORT_RENDERER_ENABLED 后,使用 Jinja2 模板按平台渲染。常见输出形态有:

完整仪表盘

摘要、情报、结论、数据透视、作战计划、历史对比。适合飞书、邮件、Web。

微信精简版

更短的字段截断、表格转键值行,避免在手机窄屏上错位。

一句话简报

一只股票一行,适合自选股很多的批量概览。

分享海报

1080×720 的图片,适合群聊与朋友圈,信息密度低于文本。

格式化层还处理一些很琐碎但很致命的问题:Markdown 表格在 Telegram 不生效、飞书有字节上限、企业微信的 Markdown 语法子集有限、Slack 的表格语法不同。这些不是「文案问题」,而是渠道协议差异。

🎯 2026-02-08 决策仪表盘

共分析 3 只股票 · 🟢买入 0 · 🟡观望 2 · 🔴卖出 1

⚪ 中钨高新 (000657)
观望 · 评分 65 · 看多

🟡 新莱应材 (300260)
卖出 · 评分 35 · 看空

完整报告请查看 Web 工作台

DSA · 决策仪表盘

中钨高新 000657
观望|评分 65|看多

新莱应材 300260
卖出|评分 35|看空

────────
2026-02-08 18:00

每日股票分析 · 2026-02-08

今日共分析 3 只自选股,2 只观望、1 只建议卖出。

股票建议评分
中钨高新观望65
新莱应材卖出35

本邮件由 DSA 自动发送 · 不构成投资建议

排版示意,非真实推送内容

Markdown 转图片:当文字排版不可靠时

有些渠道对 Markdown 支持很差,或者长表格在手机上根本看不清。这时可以把报告渲染成图片:

  • 由 MARKDOWN_TO_IMAGE_CHANNELS 指定哪些渠道需要转图;只转一次,再分发给多个渠道。
  • 引擎可选 wkhtmltoimage(默认)、Markdown 文件渲染或 Playwright;缺依赖时自动回退文本。
  • 超过 MARKDOWN_TO_IMAGE_MAX_CHARS(默认 15000 字符)会跳过转图,避免生成一张看不清的超长图。
  • 分享海报走另一条路径:1080×720 的固定版式,历史报告可单独生成 PNG。

图片是「保底的可读性」,不是「更高级的格式」。它不可搜索、不可复制、对屏幕阅读器不友好,所以只在必要时使用。

Bot:把分析装进 IM

Bot 不是独立进程,而是跟着 --serve* 启动的后台能力。当前实际接入以Stream 长连接为主:飞书用 lark-oapi WebSocket,钉钉用 Stream 模式,自动重连、线程池处理。好处是不需要公网 IP,家里 NAS 也能用。

不要把「Webhook 入口」当成主路径

仓库里仍保留一些历史 Webhook 处理函数,但 FastAPI 并没有挂载统一的 Webhook 路由。想真正收到消息,请启用 FEISHU_STREAM_ENABLED 或 DINGTALK_STREAM_ENABLED,并让进程处于 --serve / --serve-only 模式。

10 个命令

命令别名作用
/helph、帮助、?命令帮助
/statuss、状态、info系统状态
/analyzea、分析、查单股/指数分析
/marketm、大盘、复盘大盘复盘
/batchb、批量、全部批量自选股(仅管理员)
/ask问股Agent 策略分析
/chatc、问Agent 自由对话
/research深研、deepsearch深度研究
/strategiesskills、策略列出策略
/history历史、会话Agent 会话历史

默认命令前缀是 /,中文别名可以不带前缀。命令分发器有滑动窗口限流(默认 10 次/60 秒)与管理员校验;/ask、/chat、/research 在 AGENT_MODE=false 时会明确拒绝。非命令消息可以走 LLM 意图路由,但只在私聊/@、且开启 AGENT_NL_ROUTING 与 Agent 模式时生效。

配置清单

# 至少一个通知渠道
WECHAT_WEBHOOK_URL=https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=...
# FEISHU_WEBHOOK_URL=https://open.feishu.cn/open-apis/bot/v2/hook/...
# TELEGRAM_BOT_TOKEN=...
# TELEGRAM_CHAT_ID=...

# 路由(可选)
NOTIFICATION_REPORT_CHANNELS=wechat,feishu
NOTIFICATION_MIN_SEVERITY=warning
NOTIFICATION_TIMEZONE=Asia/Shanghai

# 图片(可选)
MARKDOWN_TO_IMAGE_CHANNELS=wechat
MD2IMG_ENGINE=wkhtmltoimage

# Bot(可选)
BOT_ENABLED=true
FEISHU_STREAM_ENABLED=true
DINGTALK_STREAM_ENABLED=false

配好后,用 Web 设置页的「测试渠道」或 python main.py --check-notify 验证,不要等第二天 18:00 才发现 Webhook 少了一个字符。

五个常见坑

现象原因处理
报告很长但被截断渠道字节/字符上限开启分片或改用图片;检查 FEISHU_MAX_BYTES
表格变乱码/丢失渠道不支持 Markdown 表格用微信/Telegram 专用格式化,或转图片
同一条消息发了两遍多实例各自触发,降噪状态不共享只保留一个调度入口与发送进程
Bot 没反应没开 Stream、进程不在 serve 模式开 FEISHU_STREAM_ENABLED,用 --serve-only 常驻
图片发送失败缺 wkhtmltopdf/字体,或超过 2MB装依赖或回退文本;减少图片尺寸
自测一下

你只配置了 NOTIFICATION_REPORT_CHANNELS=feishu,没有配置告警和系统错误的路由。一条「任务失败」消息会发到哪里?

原因:路由变量为空时表示「全量渠道」。所以任务失败消息会走所有已配置的渠道,而不是只看报告路由。如果你希望严格分流,就显式配置 NOTIFICATION_ALERT_CHANNELS 与 NOTIFICATION_SYSTEM_ERROR_CHANNELS。这是「宁可多发,不可漏发」的取舍。

源码与文档锚点