Claude Code实战9|250行代码打通飞书CLI与Claude的双向通信结构

张开发
2026/5/19 17:53:02 15 分钟阅读
Claude Code实战9|250行代码打通飞书CLI与Claude的双向通信结构
装好了 Lark CLI也配好了 Claude Code然后呢很多人卡在这一步两个工具都能用但就是串不起来。在飞书对话框里发了消息Claude 那边毫无反应Claude 处理完了又不知道怎么把结果送回飞书。核心原因很简单——Lark CLI 和 Claude Code 是两个独立的命令行工具它们之间没有现成的桥梁。今天这篇文章我用一个 Node.js 脚本约250行实现了在飞书对话框发消息 → 自动触发 Claude Code 处理Claude Code 响应 → 自动发回飞书对话框全程自动化无需切换窗口环境要求macOS / LinuxNode.js ≥ 16lark-cli已安装并登录npm i -g anthropic/lark-cliclaudeCLI 已安装Claude Code 2.x飞书机器人已创建需要im:message相关权限方案设计轮询 管道调用架构总览飞书用户 → 发消息给机器人 ↓ Bridge (Node.js 轮询) ├── lark-cli 拉取新消息 ├── echo 消息 | claude -p 处理 └── lark-cli 发送响应 ↓ 飞书用户 ← 收到 Claude 回复为什么选轮询而不是 Webhook轮询模式Webhook 模式配置复杂度低无需改飞书后台高需配置事件订阅本地端口不需要暴露需要公网可达延迟约5秒实时适用场景个人/小团队生产环境对个人使用来说5秒延迟完全够用省掉的配置工作量是值得的。核心代码解析完整代码约250行这里拆解最核心的三个函数。1. 轮询飞书消息function fetchLatestMessages() { const cmd lark-cli im chat-messages-list \ --as bot \ --user-id ${CONFIG.userOpenId} \ --format json; const output execSync(cmd, { encoding: utf-8, timeout: 15000 }); const data JSON.parse(output); if (data.ok data.data data.data.messages) { return data.data.messages; } return []; }关键点--as bot以机器人身份获取消息--user-id指定用户的open_id获取与该用户的 P2P 对话--format json返回 JSON 格式方便解析返回的消息数组中第一条是最新消息2. 调用 Claude Codefunction callClaude(userMessage) { return new Promise((resolve, reject) { sendToLark(⏳ 正在思考中...); const cmd echo ${userMessage.replace(//g, \\)} | claude -p 21; exec(cmd, { encoding: utf-8, timeout: 120000, // 2分钟超时 maxBuffer: 1024 * 1024, }, (error, stdout) { if (error) { resolve(error.killed ? 抱歉处理超时了。请尝试简化你的问题。 : 处理出错请稍后再试。); return; } resolve(stdout.trim() || 抱歉没有生成有效响应。); }); }); }关键点claude -p是 print 模式非交互式直接输出结果后退出用管道echo 消息 | claude -p传递用户输入设置 2 分钟超时防止复杂任务长时间阻塞21合并错误输出确保能捕获所有信息3. 发送消息回飞书function sendToLark(text) { const tmpFile path.join(CONFIG.workDir, .tmp_msg.txt); fs.writeFileSync(tmpFile, text); const cmd lark-cli im messages-send \ --as bot \ --user-id ${CONFIG.userOpenId} \ --text $(cat ${tmpFile}); execSync(cmd, { encoding: utf-8, timeout: 15000 }); fs.unlinkSync(tmpFile); }关键点用临时文件中转消息内容避免特殊字符转义问题--text参数直接发送纯文本无需手动构造 JSON发完即删临时文件保持工作目录整洁4. 主循环串联一切setInterval(async () { if (isProcessing) return; // 防止并发 const messages fetchLatestMessages(); if (messages.length 0) return; const latestMessage messages[0]; if (latestMessage.message_id lastMessageId) return; // 关键过滤 bot 自己发的消息 const isFromUser latestMessage.sender latestMessage.sender.sender_type user; if (isFromUser) { saveLastMessageId(latestMessage.message_id); lastMessageId latestMessage.message_id; await handleMessage(latestMessage); } else { // bot 消息只更新 ID不处理 saveLastMessageId(latestMessage.message_id); lastMessageId latestMessage.message_id; } }, 5000);实战中的坑点 避坑指南坑1JSON 字段名不是items是messageslark-cli im chat-messages-list返回的 JSON 中消息数组的字段名是data.messages不是很多人以为的data.items。// ❌ 错误 data.data.items // ✅ 正确 data.data.messages避坑先用--format json手动执行一次确认实际字段结构。坑2Bot 消息回环死循环这是最危险的坑。Bot 发出的响应消息会出现在chat-messages-list的结果中。如果不过滤Bridge 会把 Bot 自己的消息当成新消息处理再次调用 Claude再次发送响应……无限循环。// ❌ 不过滤 → 死循环 if (latestMessage.message_id ! lastMessageId) { handleMessage(latestMessage); } // ✅ 必须检查 sender_type const isFromUser latestMessage.sender latestMessage.sender.sender_type user; if (isFromUser) { handleMessage(latestMessage); }避坑务必通过sender.sender_type user过滤只处理真实用户消息。坑3claude -p的正确调用方式Claude Code CLI 的-pprint模式有几个容易踩的坑# ❌ --no-input 不是有效参数 claude -p 你好 --no-input # ❌ 单引号在 Node.js exec 中容易出转义问题 claude -p 包含引号的消息 # ✅ 用管道传入最稳定 echo 你好 | claude -p避坑统一用echo 消息 | claude -p 21管道方式调用。坑4发送消息的参数是--text不是--datalark-cli im messages-send支持多种参数很多人习惯性地用--data传 JSON但实际上纯文本消息用--text更简洁可靠。# ❌ --data 需要构造嵌套 JSON容易出错 lark-cli im messages-send --data {msg_type:text,content:{\text\:\你好\}} # ✅ --text 自动处理格式 lark-cli im messages-send --text 你好避坑优先用--text发送纯文本用--markdown发送富文本。坑5特殊字符导致命令执行失败用户消息可能包含引号、反斜杠、换行等特殊字符直接拼接到命令中会导致 shell 解析失败。// ❌ 直接拼接 → 特殊字符炸裂 const cmd echo ${userMessage} | claude -p; // ✅ 先转义引号 const cmd echo ${userMessage.replace(//g, \\)} | claude -p; // ✅✅ 更稳妥用临时文件中转 fs.writeFileSync(tmpFile, text); const cmd lark-cli im messages-send --text $(cat ${tmpFile});避坑涉及用户输入的地方统一用临时文件中转彻底规避转义问题。快速启动步骤1获取你的 open_idlark-cli auth status找到userOpenId字段类似ou_xxxxxxxxxxxx。步骤2修改配置编辑bridge.js替换CONFIG.userOpenId为你自己的 open_id。步骤3启动服务cd ~/lark-claude-bridge chmod x start.sh stop.sh ./start.sh步骤4测试在飞书给你的机器人发一条消息几秒后应该收到 Claude 的回复。停止服务./stop.sh心得体会 总结做这个项目最大的感受简单的需求不简单的细节。表面上看这个项目就是读消息 → 处理 → 发回去三步搞定。但实际开发中光是消息回环这一个坑就让我的飞书对话框被刷屏了一轮。每一个看似 trivial 的细节——JSON 字段名、CLI 参数格式、特殊字符转义、bot 消息过滤——都可能让整个系统瘫痪。这也是为什么很多人装好了工具但串不起来的原因不是工具不行是粘合层的细节太多。这个方案的价值对于个人开发者来说这套方案的价值在于移动端可用在手机飞书上就能给 Claude 下达任务不必守在电脑前异步工作流发出任务后可以去做别的事Claude 处理完自动通知团队协作基础稍加扩展就能支持多人使用让团队成员都能通过飞书调用 Claude后续优化方向当前版本是 MVP还有几个值得优化的方向多轮上下文目前每条消息独立处理加上claude --resume可以实现连续对话Markdown 支持用--markdown发送富文本代码块和表格显示更友好任务队列支持多条消息排队处理而不是简单跳过Webhook 模式如果需要实时性可以升级为事件订阅方案需要完整代码请关注我评论区留言“Claude Code实战”我将发送可以直接使用的代码给你。获取更多 AI 智能制造、飞书自动化、Claude Code 实战干货欢迎关注我的公众号「Rubin 智造社」【关键词标签】#ClaudeCode #AI编程 #开发工作流 #代码重构 #飞书 #子代理 #实战教程 #代码分析 #飞书CLI #AI协作者相关阅读Claude Code实战8: 高效排错修复问题实战手记Claude Code实战75分钟“吃透”陌生代码库的工程心法Claude Code实战6: 告别黑框安装可视化界面这才是AI编程该有的面子Claude code实战5: Claude 4.5升级介绍让AI工程化落地快了不止一倍

更多文章