@huluohu
76a4e33e44
fix(telegram): Rich Message 失败时保留原始错误并降级纯文本 ( #6993 )
...
* fix(telegram): Rich Message 失败时保留原始错误并降级纯文本
Telegram Rich Message 发送失败时存在三个叠加问题:
1. `__send_rich_message` 用固定文本 `RetryException("发送 Telegram Rich
Message 失败")` 包装异常,`str(e)` 里没有 Telegram 返回的 description,
日志只剩“发送失败”四个字,无法判断是内容非法、方法不存在还是格式问题。
2. 内容非法这类确定性失败(400)同样走满3 次退避重试(3s、6s),
期间同一条坏消息反复阻塞,且最终报错仍无有效信息。
3. 失败即丢失通知:Rich Message 发不出去时没有任何降级路径,
整条通知直接消失。
改动:
- 异常文本改为 f"...失败:{err}",保留 Telegram 原始 description 与错误码。
- 新增 `NonRetryableTelegramError`(继承 `ImmediateException`)与
`_is_non_retryable_api_error`,400 立即抛出以跳过 retry 退避;
5xx 与网络异常保持原有重试策略。
- `send_msg` / `edit_msg` 增加纯文本降级出口,Rich Message 失败时
用不带格式解析的纯文本重发,保证通知内容不丢失。
- 降级路径显式传 `parse_mode=""`:`TelegramBotApi._parse_mode` 在参数为
`None` 时会回落到客户端默认的 MarkdownV2,那正是 Rich Message 已经
失败过的解析路径,漏传会让降级重演同一个错误。
新增 tests/test_telegram_rich_message_fallback.py(14 项)覆盖错误信息保留、
400 免重试、5xx 仍重试、发送与编辑两条降级路径,以及 parse_mode 回落陷阱的
回归防护。
* fix(telegram): 修正 Rich Message 降级路径的 mypy 与架构基线
上一提交引入的两处门禁失败:
1. `__send_plain_fallback` 用 `**self._topic_kwargs(chat_id)` 展开参数,mypy
把 `**` 匹配到 `send_message` 第 5 个形参 `disable_web_page_preview`
(Optional[bool]),判定 `dict[str, int]` 不兼容,arg-type 由 1 增至 2。
改为显式传 `message_thread_id`,`_topic_kwargs` 只可能产出该键。
2. 新增 `app.schemas.exception` 导入带来 2 条依赖边,宿主基线由 9542 变为
9544。已用 `baseline.py --write-host` 固化,其余 3 个基线文件字节不变。
连带影响:docs/architecture-overview.md 与 docs/refactor/
optimization-checklist.md 硬编码了边数,`test_architecture_documents_
match_generated_quality_metrics` 断言两处必须与基线一致,同步为 9,544。
门禁与测试验证:
- scripts/architecture/mypy_ratchet.py exit 0(9163 个类型错误)
- scripts/architecture/baseline.py --check-host exit 0
- pytest(fallback + contract + telegram) 75 passed
---------
Co-authored-by: jxxghp <jxxghp@gmail.com >
2026-10-15 07:32:16 +08:00