Files
Hermes-Skills/okx-auto-position/references/v4.5.114-2026-09-24-tg-forwarder-chat-id-extract-bug-and-channel-swap.md
T

5.4 KiB

v4.5.114 (2026-09-24) — TG Forwarder chat_id 提取 bug + 频道切换 (shipan_jiankong)

事故背景

用户问"现在用的是哪个频道 @shipan_jiankong 还是 @jiankongshipan"——切到新频道后, 发现新频道有信号(通过 telethon 直接读 shipan_jiankong 最近10条消息,9/23 11:39 ~ 9/24 13:48 有真实交易信号),但 forwarder 转发器完全没转发。

根因 (双层 bug 叠加)

Bug 1: chat_id 提取错 (/app/message_listener.py line 76)

chat = await event.get_chat()
chat_id = abs(chat.id)   # ❌ 错误: 拿掉 -100 前缀
  • event.chat.id (Channel 对象) = 正数 2651333064
  • event.chat_id (telethon 完整 channel id) = -1002651333064
  • DB 存的 telegram_chat_id 字段 = '-1002651333064' (字符串)

abs(chat.id) = 2651333064 → 查询 Chat.telegram_chat_id == '2651333064' → 永远不匹配 DB 里的 '-1002651333064'。

后续 line 80 的 int(f"100{chat_id}") = 1002651333064 也仍然不对,应是 -1002651333064。 但那段代码只在 state_manager.check_state() 为真时触发 (用户主动操作),正常转发流程不走那段,所以更靠前的 abs() 才是真正转发问题的根因。

Bug 2: 频道切换生效但漏了修复

2026-09-24 把 forward_rule source 从 chats.id=1 (jiankongshipan) 切到 chats.id=4 (shipan_jiankong)。 切换前: source_chat_id=1, channel id=3805472665 (个人号? 不是 -100 前缀), abs(chat.id)=3805472665 居然能匹配 → 之前能跑是因为旧频道不是 -100 格式频道。 切换后: source_chat_id=4, channel id=-1002651333064 (新格式), abs(chat.id)=2651333064 不匹配 → 立即暴露 Bug 1。

修复

# message_listener.py line 75-76 (user handler + bot handler 两处)
old:
    chat = await event.get_chat()
    chat_id = abs(chat.id)
new:
    chat = await event.get_chat()
    # v4.5.114: 用 event.chat_id (完整 TG channel id -100xxxxxxxx) 而不是 abs(chat.id) (剥掉了-100前缀)
    chat_id = event.chat_id

patch 后必须 docker restart telegram-forwarder。

双层防御 (也改 Chat 查询兼容性)

即便修了 chat_id 提取,DB 查询兼容性再加一层 — 同时匹配:

_chat_id_str = str(chat_id)
if abs(chat_id) > 1e10 and not _chat_id_str.startswith('-100'):
    _alt_id_str = f'-100{abs(chat_id) - 1e12}' if abs(chat_id) > 1e12 else f'-100{_chat_id_str}'
else:
    _alt_id_str = f'100{_chat_id_str.lstrip("-")}'
source_chat = session.query(Chat).filter(
    Chat.telegram_chat_id.in_([_chat_id_str, _alt_id_str, f'-100{_chat_id_str.lstrip("100")}'])
).first()

防御性编程: 即使将来又有人改 chat_id 提取方式,query 也能匹配。

诊断流程 (可复用)

当用户说"信号没转发"时:

  1. docker logs telegram-forwarder --tail 100 → 看有无 [处理规则|新消息|转发|命中黑名单]
  2. 若只有 Got difference for channel XXX updates (telethon) 但没有 [新消息|处理规则] 日志: → channel 已收到 update 但 message handler 没匹配 source chat
  3. 验证频道真有消息: cp /app/sessions/user.session /tmp/u.session 然后 telethon 直连 + iter_messages('shipan_jiankong', limit=10)
  4. 验证 DB 配置: docker exec telegram-forwarder python3 -c "import sqlite3; conn=sqlite3.connect('/app/db/forward.db'); ..." 读 chats/forward_rules/keywords 表
  5. 验证 chat_id 提取: 容器内 python 跑 telethon event.chat.id vs event.chat_id

容器内诊断关键

  • 容器内无 sqlite3 CLI, 用 python3 -c "import sqlite3; ..." 读 forward.db
  • session 文件 main.session/user.session/bot.session 被主进程锁 → 必须 cp 到 /tmp/xx.session 再 TelegramClient('/tmp/xx', ...) 连接
  • 容器内 telethon 连 TG 必带 proxy=('socks5','127.0.0.1',7891), request_retries=1 + 短 timeout (15s)
  • 容器内 python 默认没装 ccxt/socks 模块,不能直接 pip install(共享 docker venv)

双频道最终状态 (2026-09-24)

chats.id telegram_chat_id 名称 当前用途
4 -1002651333064 币圈实盘监控 | 熬鹰 风寻 ✅ source, 当前转发
2 -1003966251111 交易信号(Telegram消息转发) ✅ target, 你给我发消息的群
1 3805472665 实盘监控 (jiankongshipan) ❌ 停用,记录保留
3 656685714 Mike 黑六网 ❌ 不参与

forward_rule id=1: source=4 → target=2, enable=1, mode=WHITELIST_THEN_BLACKLIST, delay=5s。

用户偏好 (本session)

"换到新频道,其他的配置不动。" → 切换 = 只改 forward_rule.source_chat_id, 不要碰白名单/黑名单/关键词/transit delay 配置。

相关

  • v4.5.113 — classify 严格模式(同 session 部署)
  • v4.5.111 — 1% min 减仓过滤 (同 session 部署)
  • v4.5.110 — ZAMA 蚂蚁减仓双层过滤
  • v4.5.83 — 4 熬鹰号白名单
  • v4.5.77 — close 也闸

教训

  • 通道型 bug 必须端到端测: 只改 forward.db 的 source_chat_id 不够, 必须用 docker logs 看 [新消息|处理规则] 日志确认触发。如果没 log,说明 message_listener 在更早的阶段就 return 了(通常是 chat_id 提取错或 source chat 查不到)。
  • session 主进程锁的处理模式可复用: 容器内任何 *.session 文件被主转发器占着, 必须 cp 出来再用 telethon 直连查实体。
  • telethon event.chat_id vs event.chat.id: 前者是 TG 完整 ID(含 -100 前缀),后者是 Channel 对象的内部 ID(已剥前缀)。Channel 类型用前者。