用飞书远程操控本地 Claude Code —— 手机上发消息、发图、发语音,本地 Claude 执行,回复实时流式推回到手机。
渠道说明:目前仅维护 飞书渠道(功能完整)。Telegram 渠道代码保留但暂停维护,不建议新用户配置。
- 文字消息 → 直接发给 Claude Code 执行
- 语音消息 → 本地 Whisper 转写后执行
- 图片消息 → 多模态,支持附加文字说明
- 会话管理 → 列表 / 切换 / 重命名 / 新建,按当前工作目录过滤
- 流式回复 → Claude 一边生成,一边更新手机上的消息(Telegram 用 edit_text,飞书用 interactive 卡片 PATCH)
- 工作目录切换(飞书) →
/cwd交互式导航,每个用户独立 cwd,切换后自动接续该目录最近会话 - 运行控制(飞书) →
/stop中断任务,/model切模型,/mode切权限模式(bypass / plan / default / accept) - Subagent 透传(飞书) →
/agents列出 Claude Code 自定义 subagent,/agent <name> <任务>一键派发 - Skills 透传(飞书) → 未注册的
/xxx直接作为 prompt 传给claudeCLI,可直接用官方 skill - 语音回复(飞书) → 每条回复在文字之后再追一条语音条(TTS 合成 → opus),默认开启,
/voice off可按会话关闭
两个渠道完全独立,可以只装一个、也可以两个都装。
所有渠道共同:
- Claude Code CLI 已安装并能在终端运行
claude命令 → 安装指南 - Python 3.10+(
python3 --version确认) - 本仓库代码
git clone https://github.com/xuanbingbingo/claude-code-bot.git cd claude-code-bot
克隆后跑一键脚本,自动建 venv、装齐核心依赖、生成 .env 模板、检查 Claude CLI:
./install.sh # 飞书 + Telegram 核心依赖
./install.sh --with-voice # 额外装语音转写(faster-whisper,收发语音才需要)装完只剩一件事:编辑 .env 填凭证(飞书三项 / Telegram 两项,见下方各渠道说明)。
想手动装或只装某个渠道,看下面分渠道步骤;依赖清单也可直接
pip install -r requirements.txt。
飞书渠道用 长连接(WebSocket) 模式,本地直接连飞书服务器,不需要公网地址、nginx、SSH 隧道、ngrok。
- 打开 飞书开放平台,用你的飞书账号登录
- 点击 「创建企业自建应用」,填名称、描述、图标
- 创建完成后进入应用后台
- 左侧菜单 「添加应用能力」
- 找到 「机器人」,点击 「添加」
左侧菜单 「权限管理」 → 依次开通:
| 权限标识 | 中文名 | 用途 |
|---|---|---|
im:message |
获取与发送单聊、群组消息 | 通用收发 |
im:message:send_as_bot |
以应用身份发消息 | 发卡片 / 文本 |
im:message:update |
更新消息 | 流式 PATCH 卡片 |
im:message.p2p_msg:readonly |
读取用户发给机器人的单聊消息 | 接收用户消息 |
im:message:update 不开,卡片无法流式刷新,实测会一直停在 "⏳ 处理中..."。
左侧菜单 「事件与回调」 → 「事件配置」:
- 订阅方式 → 选择 「使用长连接接收事件」(重点:不要选 HTTP 回调)
- 添加事件 → 搜索并添加:
im.message.receive_v1(接收消息 v2.0)
左侧菜单 「版本管理与发布」:
- 点击 「创建版本」,填版本号和说明
- 提交审核(企业自建应用一般由管理员秒过)
- 审核通过后状态变为「已启用」
权限和事件变更必须发布新版本才会生效,已开通 ≠ 已生效。
左侧菜单 「凭证与基础信息」:
- 复制 App ID(
cli_xxxxxxxxxx) - 复制 App Secret
- 私聊:在飞书里搜索你应用的名字 → 点击进入 → 直接开聊
- 群聊:群设置 → 群机器人 → 添加机器人 → 选择你的应用
pip install httpx lark-oapi faster-whispercp .env.example .env编辑 .env,飞书渠道填这三个:
FEISHU_APP_ID=cli_xxxxxxxxxx # 第 6 步 App ID
FEISHU_APP_SECRET=xxxxxxxxxxxxx # 第 6 步 App Secret
FEISHU_VERIFY_TOKEN= # 长连接模式留空即可
CLAUDE_CWD=/Users/you/projects # Claude 执行时的工作目录CLAUDE_CWD 是 Claude 启动后 cwd,Claude 读写文件都以这个目录为根。
长连接模式下
FEISHU_VERIFY_TOKEN不校验,可以留空。
bash start-claude-feishu.sh看到:
🤖 Claude Code Feishu Gateway 启动中(长连接模式)...
App ID: cli_xxxxx...
等待飞书消息...
[Lark] [xxxx] [INFO] connected to wss://msg-frontier.feishu.cn/...
就连上了。日志路径 ./claude-feishu.log。
在飞书里给你的 bot 发:
- 一段文字 → 卡片从 "⏳ 处理中..." 实时刷新到 Claude 回复
- 一张图片(可带文字说明)→ 多模态
- 一段语音 → Whisper 转写后执行(首次下载模型)
| 命令 | 说明 |
|---|---|
/start |
显示帮助和当前状态 |
/status |
查看当前 cwd / 模型 / 模式 / sessionId / 任务状态 |
/sessions |
列出当前工作目录下最近 10 条历史会话 |
/resume <编号> |
按 /sessions 里的编号切换 |
/resume <sessionId> |
按完整 sessionId 切换 |
/resume <标题> |
按 /sessions 显示的标题切换(精确匹配) |
/rename <新名称> |
重命名当前会话 |
/rename <sessionId> <新名称> |
重命名指定会话 |
/new |
下一条消息开启全新会话 |
启动时会自动接续当前工作目录下最近一条可用会话;如果该会话历史数据不兼容(API 400),第一次消息会自动回退新会话模式,你重发即可。
| 命令 | 说明 |
|---|---|
/voice |
查看当前会话的语音开关与音色 |
/voice on |
开启语音回复(默认即开) |
/voice off |
关闭语音回复(文字照常发) |
/voice list |
列出全部可用音色(按引擎分组,不可用的单独标注) |
/voice <音色名> |
换音色并当场发一条试听;名字 / 别名 / 编号都认(磁性 = 12 = 磁性男声) |
音色按会话记,与 BOT_VOICE_NAME 的关系是:会话内 /voice <音色> > 该 bot 的 BOT_VOICE_NAME > hfvoice 默认(清爽男声)。
音色表直接读 ~/aiProjects/hf-voice/voices.json(唯一真相源),加音色不用改网关代码。
本地 kokoro 那 8 个(k1–k8)模型文件缺失时会自动标为不可用并拦住,不会出现「切了却没声音」。
文字回复收尾后,网关会异步合成一条语音追发过去——合成不占对话主流程,下一条消息不会被它堵住。
被 /stop 或新消息抢占的半截回复不会被念出来。
实现要点(core/tts.py):
- 念稿会先清洗:代码块整块替换为「代码略」、URL 换成「链接」、表格/emoji/markdown 符号剔除
- 多音字走
~/aiProjects/koubo-subtitle-kit/polyphones.json(唯一真相源)的auto_fix规则, 另有「塞 → 腮」这类改音不改字的内置补充;只进合成稿,不影响发给用户的文字 - 超过
BOT_VOICE_MAX_CHARS(默认 600 字)在句末截断,并在结尾补一句「后面还有内容,请看文字」 - 合成用
hfvoice(默认清爽男声),失败自动重试 3 次并剥掉HTTP(S)_PROXY(edge-tts 走代理必挂) - 🔴 飞书语音条只认 opus(单声道 / 16kHz),由 ffmpeg 转码;传 wav 能上传成功但会渲染成文件附件
依赖:hfvoice(~/aiProjects/hf-voice/)与 ffmpeg / ffprobe 需在 PATH 中。
| 命令 | 说明 |
|---|---|
/stop |
中断当前正在运行的 Claude 任务(新消息也会自动中断旧任务) |
/model |
查看当前模型 |
/model opus | sonnet | haiku |
切换模型(下一条消息生效) |
/model default |
重置为 Claude CLI 默认 |
/mode |
查看当前权限模式 |
/mode bypass |
跳过所有确认(默认,对应 --dangerously-skip-permissions) |
/mode plan |
只规划不执行(Plan 模式) |
/mode default |
每次工具调用需确认 |
/mode accept |
自动接受文件编辑 |
| 命令 | 说明 |
|---|---|
/cwd |
查看当前目录 + 列出子目录(带编号) |
/cwd <编号> |
进入上一步列出的子目录 |
/cwd .. |
返回上级 |
/cwd <相对/绝对/~路径> |
跳到任意目录 |
切换目录时会自动接续该目录下最近的可用会话;如果该目录还没有历史会话,则下一条消息开启新会话。
| 命令 | 说明 |
|---|---|
/agents |
列出可用 subagent(~/.claude/agents + 项目 .claude/agents) |
/agents <关键词> |
按名称或描述模糊过滤 |
/agent <name> |
查看指定 agent 的详情 |
/agent <name> <任务描述> |
让 Claude 调用该 subagent 完成任务 |
未注册的 /xxx 命令会直接作为 prompt 传给 claude CLI,可以直接使用 Claude Code 官方 skill(如 /commit、/review、/init 等)。
⚠️ Telegram 渠道目前暂停维护,代码保留供参考,不建议新用户配置。
- 手机 / 桌面 Telegram 搜索 @BotFather,开始对话
- 发送
/newbot - 按提示输入 bot 名字和 username(username 必须以
bot结尾,例如my_claude_bot) - BotFather 回复一串 Token,形如:
保存好,下一步要用
123456789:AAE...xyz - 在 BotFather 给你的链接里点击 START,或直接搜索你的 bot 并发送
/start,否则 bot 无法主动给你发消息
pip install python-telegram-bot faster-whisper如果需要代理(下见第 4 步):
pip install "python-telegram-bot[socks]"如果还没创建 .env:
cp .env.example .env编辑 .env,Telegram 渠道只需要填这两个:
TELEGRAM_BOT_TOKEN=123456789:AAE...xyz # 第 1 步 BotFather 给的 Token
CLAUDE_CWD=/Users/you/projects # Claude 执行时的工作目录(绝对路径)Telegram 在国内需要代理。代码里写死了 socks5://127.0.0.1:53542(位于 claude-telegram.py 的 main() 函数)。
- 如果你用 Clash / Shadowrocket 等,把 SOCKS5 端口改成它们的监听端口(Clash 默认
7890是 HTTP,SOCKS5 通常另配一个端口) - 不需要代理的用户:把
.proxy(proxy)这一行删掉或注释
编辑方式:
vim claude-telegram.py # 搜索 socks5://127.0.0.1:53542,改成你的bash start-claude-telegram.sh看到 🤖 Claude Code Telegram Gateway 启动中... 就成功了。
日志路径:./claude-telegram.log。
在 Telegram 给你的 bot 发一条消息,比如:
你好
bot 会先回复 ⏳ 处理中...,然后流式改写成 Claude 的回复。
再试:
- 发条语音 → 首次会下载 Whisper base 模型(约 150MB)
- 发张图片(带文字说明)→ 多模态分析
在 Telegram 里直接输入:
| 命令 | 说明 |
|---|---|
/start |
显示帮助 |
/sessions |
列出最近 10 条历史会话 |
/resume <编号> |
切换到指定会话继续对话 |
/new |
下一条消息开启全新会话 |
| 命令 | 飞书 | Telegram |
|---|---|---|
/start |
✅ | ✅ |
/status |
✅ | ❌ |
/sessions |
✅ | ✅ |
/resume <编号> |
✅ | ✅ |
/resume <sessionId> |
✅ | ❌ |
/resume <标题> |
✅ | ❌ |
/rename ... |
✅ | ❌ |
/new |
✅ | ✅ |
/stop |
✅ | ❌ |
/model ... |
✅ | ❌ |
/mode ... |
✅ | ❌ |
/cwd ... |
✅ | ❌ |
/agents / /agent |
✅ | ❌ |
未注册 /xxx 透传给 Claude |
✅ | ❌ |
Telegram 渠道的命令集较精简,后续如有需要可迁移飞书的增强命令。
claude-code-bot/
├── claude_core.py # 共享核心:会话管理、调 claude CLI、流式解析、Whisper
├── claude-telegram.py # Telegram 渠道
├── claude-feishu.py # 飞书渠道(长连接)
├── start-claude-telegram.sh # Telegram 启动脚本
├── start-claude-feishu.sh # 飞书启动脚本
├── .env # 你的凭证(已在 .gitignore,不会提交)
├── .env.example # 凭证模板
├── claude-telegram.log # Telegram 运行日志(已在 .gitignore)
├── claude-feishu.log # 飞书运行日志(已在 .gitignore)
└── README.md
| 包 | 用途 | 必需渠道 |
|---|---|---|
claude CLI |
实际执行引擎 | 两个都需要 |
python-telegram-bot >=20 |
Telegram SDK | Telegram |
lark-oapi |
飞书开放平台 SDK | 飞书 |
httpx |
飞书 REST API 调用 | 飞书 |
faster-whisper |
本地语音转写 | 语音消息 |
Q:飞书发消息后机器人不回复,日志只有 [DEBUG] 收到消息
权限或事件订阅没发布版本生效。回到「版本管理与发布」重新发布;特别检查 im:message:update 是否已开通且已随版本发布。
Q:飞书卡片一直停在 "⏳ 处理中..." 不刷新
几乎 100% 是 im:message:update 权限没开或没发布版本。重启 gateway 前先在飞书后台确认该权限状态为「已开通」且版本已发布。
Q:/resume 报 "未找到 session"
- 确认输入的是
/sessions里显示的🔖 sessionId或编号;输入标题时必须完全一致 - 如果 session 文件已被清理(
~/.claude/projects/<project>/<sessionId>.jsonl不存在)则无法恢复
Q:飞书恢复会话后报 API Error: 400 ... tool_use.id
历史会话 jsonl 里存了不合法的 tool_use id(多见于跨 provider / model 的旧会话)。gateway 已经做了兜底:这条错误会推给你并自动切到新会话模式,重发一次消息即可。
Q:Telegram 连不上 / 一直超时
代理没配对。检查 claude-telegram.py 里 socks5://127.0.0.1:53542,改成你本机代理的 SOCKS5 端口;或完全去掉 .proxy(proxy) 行(如果不需要代理)。
Q:首次发语音卡很久
Whisper base 模型首次使用会下载 ~150MB,之后常驻内存。下载完再发一条语音就秒响。
Q:两个渠道的会话列表会相互干扰吗?
共享 CLAUDE_CWD,所以 /sessions 看到的是同一份历史;但两个渠道各自维护 "当前会话"(飞书按 open_id 分,Telegram 是全局单会话)。一边 /new 不会影响另一边。
开箱即用,零配置。在飞书里直接对机器人说「把这个视频发我」「发到聊天框」即可,无需自己写 CLAUDE.md。
原理(两件事都由网关自动完成):
- spawn claude 子进程时,注入当前会话发起人 open_id(
FEISHU_SENDER_OPEN_ID)+ 当前 bot 凭证(FEISHU_APP_ID/SECRET)到子进程环境——「当前窗口」自动定位收件人; - 通过
--append-system-prompt告知 claude「有tools/feishu-send-file.py这个工具、何时该调」——claude 默认不知道工具存在,这一步把它接通。工具路径按仓库位置动态推导,不写死任何绝对路径。
于是 claude 在你要发文件时会自动执行 python3 tools/feishu-send-file.py <文件绝对路径>:图片→image、视频→media(带封面/时长)、其它→file,视频 >28MB 自动 ffmpeg 压到飞书 30MB 上限内(缺 ffmpeg 时优雅降级:不压缩、原样尝试)。
手动验证(在网关机器上任意终端,可选):
python3 tools/feishu-send-file.py /path/to/video.mp4 ou_xxxxxxxx # 发给指定 open_id收件人解析优先级:命令行第二参 > 环境变量 FEISHU_SENDER_OPEN_ID(网关注入=当前窗口)> ~/.claude/tools/feishu-send-file.conf 的 DEFAULT_RECEIVE_ID(兜底,见 tools/feishu-send-file.conf.example)。
凭证解析优先级:环境变量(当前 bot)> FEISHU_SEND_ENV > conf 的 GATEWAY_ENV > ~/aiProjects/claude-gateway/.env。
大视频:飞书单文件硬上限 30MB,建议网关机器装 ffmpeg。
⚠️ 飞书单文件硬上限 30MB,视频建议装ffmpeg(缺失时工具优雅降级:不压缩、不生成封面,仍尝试原样发送)。
同一份 claude-feishu.py 可以用不同凭证 + 不同人设同时跑成多个独立的飞书 bot(比如「研究员」「工程师」「复盘」各一个聊天窗),互不干扰、各自持久会话。
启动时读两个可选环境变量,把人设作为 --append-system-prompt 注入 Claude(与发文件提示自动合并):
| 环境变量 | 说明 |
|---|---|
BOT_PERSONA |
直接内联一段人设文本(优先级最高) |
BOT_PERSONA_FILE |
指向一个角色 .md 文件,自动去掉 YAML frontmatter 后作为人设 |
BOT_NAME |
仅用于启动日志显示 |
BOT_VOICE |
0/off/false 关闭语音回复;缺省为开 |
BOT_VOICE_NAME |
指定 hfvoice 音色(如 edge2=微软云扬);留空用 hfvoice 默认(清爽男声) |
BOT_VOICE_MAX_CHARS |
语音念稿字数上限,默认 600,超出截断并提示看文字 |
两者都不设时行为与原版完全一致(通用无人设),对现有单 bot / 主网关零影响。
cp .env.role.example .env.research # 复制模板
# 编辑 .env.research:填该 bot 的飞书 App ID/Secret、CLAUDE_CWD、BOT_PERSONA_FILE
bash start-bot.sh research # 以「research 角色」启动;日志 bot-research.logstart-bot.sh <role> 会 source .env.<role>,用仓库 venv(没有则系统 python3)启动网关。
.env.<role> 含私有凭证,已被 .gitignore 忽略,不入库;提交的只有 .env.role.example 模板。
scripts/new-agent.sh 把本地步骤(生成 .env.<role> + launchd 自启 + 启动)合成一条命令。建飞书应用有两种方式:
方式一:扫码自动建应用(推荐,免手动进开放平台)
# 不传 App ID/Secret:脚本弹出二维码,手机飞书扫码即自动建好应用并拿到凭证
bash scripts/new-agent.sh researcher ~/.claude/agents/researcher.md 研究员扫码走的是飞书账号中心的设备码注册流(accounts.feishu.cn/oauth/v1/app/registration,PersonalAgent 模板),扫码授权后自动创建自建应用并预配机器人能力/权限/事件订阅,直接返回 App ID/Secret。也可单独跑 python3 scripts/feishu_setup.py --role researcher 只拿凭证。
扫码页会显示飞书 OpenClaw 生态的注册模板文案(这套端点飞书也给 lark-cli 用),不影响拿到凭证。该端点非飞书 server-docs 公开承诺稳定的 API,若某天失效可退回方式二手动建。
方式二:手动填凭证(已在开放平台建好应用的)
bash scripts/new-agent.sh researcher cli_xxx secret_yyy ~/.claude/agents/researcher.md 研究员两种方式都会:① 生成 .env.researcher ② 生成 launchd 自启配置 ③ 启动 bot。
人设文件用 Claude Code 子代理格式(frontmatter + 正文=角色系统提示),模板见 agents-example/role.md。
要让新角色在群里 @ 队友协作,再在它的
.env.<role>里加BOT_RELAY=1/BOT_TEAMMATES(见.env.role.example)。 企业微信无对应的扫码/API 建应用能力,只能在管理后台手动创建应用后填凭证。
把网关挂在 Mac 上长期跑、用手机远程时,有两个坑会导致「手机端能收到但回复到一半就断」:
Mac 一睡眠就会冻结网关进程:正在生成的回复会停在一半,长连接也会断。装一个常驻防睡眠服务:
bash scripts/macos-keep-awake.sh # 安装并启动(launchd 常驻,开机自启、退出自拉起)
bash scripts/macos-keep-awake.sh stop # 卸载它用 caffeinate -i -m -s 阻止空闲/磁盘/系统睡眠。
-s(阻止系统睡眠)只在【插电】时生效;- caffeinate 无法阻止【合盖睡眠 / clamshell】。
- 👉 所以要真正稳定 7×24:插上电源 + 保持开盖。合盖或纯电池仍会进入维护睡眠(约每 15 分钟一次),导致手机端断断续续。
- 若必须合盖运行(自担过热风险):
sudo pmset disablesleep 1。
网关对每条回复有两档时长上限:跑满软上限(默认 30 分钟)不会中断,卡片顶上挂一条「继续在跑」的横幅,任务结束后另发一条提醒;跑满硬上限(默认 4 小时)才会真正杀掉。
任务在跑时,「好了吗 / 进度呢 / 还要多久」这类短句不会打断它,网关直接回复已运行时长和当前步骤;其它普通消息仍然会打断当前任务,/stop 也照常可用。识别规则在 core/progress_query.py。
需要调整时,把环境变量写进各 bot 的 .env / .env.<role>:
CLAUDE_RUN_TIMEOUT=1800 # 软上限(秒),默认 1800:到点不杀,只挂横幅、结束另发提醒
CLAUDE_RUN_HARD_TIMEOUT=14400 # 硬上限(秒),默认 14400:到点才真杀
CLAUDE_STALL_TIMEOUT=600 # 流静默多久判「卡死」(秒),默认 180;长工具调用(回测)要调大避免误杀改完重启对应 bot 生效。
- 凭证保护:
.env已在.gitignore,任何情况下不要把 Token / Secret 提交到仓库 - 后台运行:
bash start-claude-xxx.sh是前台脚本;想常驻建议nohup bash start-claude-xxx.sh > /dev/null 2>&1 & - 停止服务:
pkill -f claude-telegram或pkill -f claude-feishu - 自定义 Whisper 模型:编辑
claude_core.py的WhisperModel("base", ...),可改成small/medium(更大更准,但更慢) - 飞书长连接不需要公网:本地启动就能工作,适合开发者本机部署