# 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) ```python 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。 ## 修复 ```python # 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 查询兼容性再加一层 — 同时匹配: ```python _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` → 看有无 [处理规则|新消息|转发|命中黑名单] 3. 若只有 `Got difference for channel XXX updates` (telethon) 但**没有 [新消息|处理规则]** 日志: → channel 已收到 update 但 message handler **没匹配 source chat** 4. 验证频道真有消息: `cp /app/sessions/user.session /tmp/u.session` 然后 telethon 直连 + `iter_messages('shipan_jiankong', limit=10)` 5. 验证 DB 配置: `docker exec telegram-forwarder python3 -c "import sqlite3; conn=sqlite3.connect('/app/db/forward.db'); ..."` 读 chats/forward_rules/keywords 表 6. 验证 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 类型用前者**。