---
title: "把 Claude Code 接入 Telegram，我走过的弯路与开发历程"
description: "把 Claude Code 接入 Telegram 的四个月开发复盘。内容包括短进程架构、会话队列、tmux 抓屏失败、JSONL transcript、AI 自动开发、密钥泄漏和项目停更。"
pubDate: 2026-07-03
tags: ["Claude Code", "工程复盘", "开源", "AI编程", "Python", "Telegram"]
---

tgcc 是一个 Telegram Bot。用户在手机上发送任务，Bot 在电脑上启动 Claude Code，再把执行状态和最终结果发回 Telegram。

项目从 2026 年 2 月开发到 6 月。最终版本有 11016 行源码、15029 行测试代码和 7887 行文档，发布了 5 个 Git tag 和 2 个 PyPI 版本。6 月以后，已停止使用和维护这个项目。

## 结论

短进程适合 Telegram 这类请求式入口，进程容易启动、终止和清理，缺点是无法打开 Claude Code 的终端选择器。正式版本没有把 `/model`、`/effort` 和 `/permissions` 的交互界面搬进 Telegram，而是在 Bot 中提供对应设置。用户为当前 chat 选择模型、推理强度和权限后，tgcc 保存这些值，后续任务继续使用同一组设置。

tmux 抓屏不能作为可靠的回答数据源。TUI 可能仍在重绘，解析器无法恢复屏幕上尚未出现的数据。能够读取 JSONL transcript 时，应直接消费结构化事件，只在处理终端选择器时抓屏。

自动化代理可以持续发现并修复局部问题，项目范围和停止条件仍需人工控制。四天内产生的 480 个提交通过了各自的检查，最终没有进入发布版本。

测试只能验证预先定义的行为。真实凭据进入测试数据后，覆盖率和通过数量无法发现来源错误。凭据轮换和公开历史清理也不能由代码补丁替代。

tgcc 完成了从 Telegram 调用 Claude Code 的目标。项目没有获得足以支撑长期维护的真实使用反馈，我也转向了其他远程入口，因此在 2026 年 6 月停止开发。

## 一、从消息转发开始

最初需求只有三个步骤。

```text
Telegram 接收消息
电脑运行 Claude Code
Telegram 返回结果
```

第一版在一天内完成了包结构、配置、执行器、流式输出、Bot 命令和发布流程。当天首先暴露的是输出缓冲问题。

```text
22:08  stdout buffer 调到 50MB
22:09  stream buffer 调到 2GB
22:11  stream buffer 收回到 100MB
```

Claude Code 的输出会超过 asyncio 默认的 64KB 流缓冲。缓冲区只是开始，连续对话很快带来了会话和并发问题。

tgcc 保存 Telegram chat 与 Claude session 的对应关系，下一次请求通过 `--resume` 恢复上下文。同一个 chat 内的任务进入 FIFO 队列，避免两次执行同时修改同一段会话。不同 chat 之间可以并行。

长任务需要持续反馈。Bot 将运行状态与最终答案分开，状态消息显示工具调用、耗时和停止按钮，最终答案单独分页发送。这个结构一直保留到最后一个版本。

项目随后增加多实例配置。每个项目拥有独立的 Bot、环境变量、运行状态和日志。多实例又要求统一处理启动、停止、重启、实例发现和进程冲突。准备公开发布后，项目继续加入群聊权限、日志脱敏、安装诊断、文件权限修复和发布检查。

## 二、短进程架构的边界

tgcc 每次收到请求都会启动一个新的 `claude -p` 进程。任务结束后，进程随之退出。

```bash
claude -p \
  --resume <session_id> \
  --output-format stream-json \
  --verbose
```

会话状态由 Claude Code 管理，tgcc 只维护 chat 与 session id 的对应关系。短进程也便于设置超时、终止任务和清理异常。

长驻交互终端需要处理 TTY 状态、ANSI 输出、输入提示、崩溃恢复和进程池。短进程避开了这些问题，同时失去了终端交互能力。

`claude -p` 运行在 headless 模式。`/model`、`/effort` 和 `/permissions` 等命令依赖终端选择器，无法直接通过 Telegram 使用。为了解决这几个命令，我开始实现 tmux 交互模式。

## 三、tmux 抓屏为什么失败

交互模式在 tmux 中启动完整的 `claude`，用 `send-keys` 输入文字，再用 `capture-pane` 读取屏幕。

这个方案先后遇到四类问题。

### alt-screen 没有完整历史

Claude Code 的 TUI 使用备用屏幕，`capture-pane -S -` 无法取得完整 scrollback。临时方案是将 pane 高度设为 200 行，尽量让一轮输出留在当前帧中。长输出仍可能被截断。

### 无法可靠判断任务结束

Claude Code 的 spinner 会显示随机词，固定字符串匹配无法判断运行状态。`Crunched for 3s` 一类文本在任务结束后仍会留在屏幕上，也不能作为运行标志。最后采用连续两帧完全相同的判定方式。

### Markdown 会干扰边界识别

早期解析器使用横线定位输入框。Markdown 分隔线和表格边框也包含横线，回答会被错误截断。解析器后来改用孤立的空 `❯` 作为输入框锚点。

### 抓到的是渲染中间状态

前三类问题都能通过修改解析器缓解。Markdown 表格暴露了抓屏方案的根本限制。抓取结果有表头、分隔线和底边，中间的数据行却是空的。解析器测试没有发现错误，缺失发生在 `capture-pane` 之前。

Claude Code 会先绘制表格框架，再填入数据。稳定检测结束时，TUI 可能仍在重绘。`capture-pane` 读取的是当前屏幕，无法保证它对应完整回答。

后续实现改为读取 Claude Code 的 JSONL transcript。transcript 保存结构化事件和原始 Markdown，不包含终端边框，也不存在表格渲染中间帧。tmux 只负责发送输入，普通回答由 transcript reader 读取。终端选择器等不写入 transcript 的界面继续使用抓屏。

增量读取时，reader 只有在整行 JSON 成功解析后才推进 byte offset。读到尚未写完的行就停止，下一轮从原位置重试。文件被截断且 offset 大于文件大小时，reader 从头读取。

重启又暴露了一个问题。tmux 是独立 daemon，`tgcc restart` 只重启 Bot，原来的 Claude Code 进程仍然存在。旧实现会生成新的 session id，reader 找不到对应 transcript，随后静默退回抓屏。

修复后，chat 与 interactive session id 被持久化到 `status.json`。tmux session 存活时复用旧 id，已经退出才创建新 id。这里不能使用 `--resume`，因为 resume 会派生新的 session id，transcript 路径也会改变。

这条分支最终没有合并。正式版本继续使用短进程，模型、推理强度和权限改由 tgcc 保存每个 chat 的设置。主分支一度保留了 `/screen`、`/key` 和 tmux 模式文档，但没有对应代码，发布前已经删除。

## 四、四天 480 个提交

2026 年 5 月 29 日到 6 月 1 日，项目产生了 480 个提交，四天分别为 66、199、174 和 41 个。

这批工作主要围绕开源发布检查展开。AI 编写检查脚本，扫描密钥、文件权限、配置、文档和发布流程。检查发现问题后，AI 修改代码、补测试、更新报告，再运行下一轮检查。

修改次数最多的文件集中在发布文档和检查工具。

```text
297 次  CHANGELOG.md
281 次  docs/releases/v0.1.0.md
219 次  tests/release_readiness_fixture.py
201 次  scripts/release_readiness_config.py
145 次  scripts/check_release_readiness.py
```

提交信息记录了约束、否决方案、影响范围、测试结果和未验证项。单个提交大多有明确目标，连续运行却形成了自我扩张的检查循环。检查脚本新增规则，AI 修复对应问题，修复又产生新的检查项。

这 480 个提交中的发布检查和版本文档没有进入最终仓库。项目版本从 0.1 推进到 0.7，也没有对应七轮用户验证。自动化提高了局部任务的完成速度，没有替项目判断继续投入是否有价值。

## 五、脱敏测试泄漏了真实 Token

2026 年 6 月 2 日，Robin 报告 `tests/test_sanitizer.py` 中存在一个仍然有效的 Telegram Bot Token。Token 在 5 月 25 日进入公开提交，暴露了八天。

`sanitizer.py` 用于清理 Claude Code 输出中的 Token、API key 和私钥。测试需要一段符合 Telegram Token 格式的字符串，测试数据却使用了真实 Token。

泄漏发生在标题为 `Prevent tgcc instance collisions and secret leakage` 的提交中。该提交记录了 245 个测试通过，覆盖率为 86%。现有测试验证了脱敏器能否识别 Token，没有验证测试数据是否来自真实凭据。

代码完成了两项修复。

1. 用运行时生成的无效 Token 替换真实值。
2. 增加仓库密钥扫描测试。

已经泄漏的 Telegram Token 仍需在 BotFather 中手动轮换，现有记录无法确认这一步是否完成。代码修复也无法删除公开 Git 历史里的旧 Token。

从 Git 历史和 GitHub 创建时间判断，仓库随后被删除并重新创建。当前 GitHub 历史从 `Initial release: tgcc v0.8.0` 开始，旧提交不再可达。本地保留的旧历史有 598 个提交，重建后的主分支有 54 个提交。

## 六、项目结束时的状态

仓库重建后，项目继续完成安全加固和重构。

群聊权限改为默认拒绝，恢复的 session id 在进入 `claude --resume` 参数前必须通过 UUID 校验。脱敏器增加 ANSI、OSC、URL 凭据和 HTTP Basic Auth 的处理。

`Executor.run` 从 367 行拆到 91 行，并提取 6 个辅助方法。整个 `executor.py` 从 911 行增加到 1034 行。重构降低了单个方法的复杂度，同时增加了函数边界和文件内跳转。

后续又加入 3 个 Protocol 接口和 ServiceContainer。原计划中的统一配置、状态持久化抽象和命令注册框架没有继续实施。`BotCommandHandlers` 已经依赖二十多个 `TGBot` 属性，进一步拆分的收益不足以覆盖新增复杂度。

项目最终规模如下。

| 项目 | 数量 |
|---|---|
| 源码 | 11016 行 |
| 测试代码 | 15029 行 |
| 文档 | 7887 行 |
| 测试函数 | 775 个 |
| Git tag | 5 个 |
| PyPI 版本 | 2 个 |

到 2026 年 9 月 3 日，GitHub 为 0 star、0 fork、0 watcher。两个 PR 均来自 Dependabot，没有真人 issue 或 PR。PyPI 不含镜像的累计下载量为 403 次，其中 251 次集中在两个发布日期。下载数据无法证明有多少真人实际使用。

6 月 9 日，开发记录已经转向调查 Claude Code Agent SDK 等替代方案。tgcc 此后只收到一次 Dependabot 自动提交，项目停止维护。
