背景与使用场景
这个能力做什么
飞书机器人允许你的应用主动向飞书用户或群组发送消息,支持纯文本、富文本(post)、交互卡片(interactive card)等多种格式。有两种发送通道:Webhook(免鉴权、仅限特定群)和 API(需要 tenant_access_token,可触达任何用户或群)。本能力是多个下游模块的基础。消息看板、告警播报等能力都依赖机器人先完成配置并加入群聊,才能正常工作。
业界怎么用
- 监控告警推送:服务器异常、接口报错、业务指标超阈值时,自动给运维群发送告警卡片,包含关键数据和一键跳转按钮。这是最高频的用法——几乎所有用飞书的企业都有监控机器人。
- 业务流转通知:审批通过、订单状态变更、任务分配等业务事件发生时,机器人即时通知相关人,替代人工通知。
- 定时报表播报:每日早报、周报、月报自动推送到对应群组,包含关键业务指标和趋势图。
- 交互式操作入口:通过卡片消息的按钮让用户在聊天窗口内直接完成操作(审批、确认、快速回复),不需跳转到外部系统。
- 自动化工作流终端:作为自动化管线的最后一环,将处理结果通知到责任人——CI/CD、数据处理、审计等流程结束后自动汇报。
为什么这么做
- 消息到达率极高:飞书是员工日常工作的主界面,机器人消息的打开率远高于邮件(邮件平均打开率 ~20%,飞书消息接近 90%)。
- Webhook 零门槛:创建一个群机器人只需 5 分钟,获得 Webhook URL 后任何能发 HTTP 请求的系统都能推送消息,不需要审批权限。
- 卡片消息可交互:不只是「通知」,而是「入口」。用户可以直接在卡片上点按钮完成操作,形成「消息即服务」的体验。
- 双通道互补:Webhook 简单快速但只能发到固定群,API 模式灵活强大但需要权限审批——两者结合覆盖了几乎所有消息推送场景。
下游依赖关系
本模块是以下能力的前置依赖:| 下游模块 | 依赖关系 | 说明 |
|---|---|---|
| 消息看板 | 强依赖 | 机器人必须加入群聊 → 才能接收群消息事件 → 才能在看板展示 |
| 告警播报 | 强依赖 | 告警本质上是通过机器人发送卡片消息(Webhook 或 API 通道) |
| 事件订阅 | 弱依赖 | 部分事件(如 im.message.receive_v1)需要机器人在群中才能触发 |
一、整体架构
本模块提供 Webhook 和 API 两种消息发送通道,统一通过feishu-contacts Edge Function 路由处理。
二、数据流程
Webhook 模式流程
API 模式流程
三、环境变量
| 变量 | 来源 | 必须 | 用途 |
|---|---|---|---|
SUPERUN_FEISHU_APP_ID | Edge Function Secret | API 模式必须 | 飞书应用 App ID |
SUPERUN_FEISHU_APP_SECRET | Edge Function Secret | API 模式必须 | 飞书应用 App Secret |
Webhook 模式不需要上述凭证,webhook_url 由前端传入,URL 本身即为鉴权凭证。
四、关键代码
4.1 Webhook 发送
- Webhook URL 格式固定:
https://open.feishu.cn/open-apis/bot/v2/hook/{token} - interactive 类型必须用
card字段,不能用content——这是飞书 Webhook 的特殊要求 - content 直接传对象,不需要 JSON.stringify(与 API 模式不同)
4.2 API 模式 — 获取群列表
- 只返回机器人作为成员的群——机器人未加入的群不会出现
- 如果返回空列表,大概率是机器人没有加入任何群,而不是权限问题
- 分页支持 page_token,但一般群数量不多,50 一页足够
4.3 API 模式 — 发送消息
content字段必须是 JSON 字符串(被 stringify 两次),这是飞书 API 的强制要求- 文本消息:
"{\"text\":\"hello\"}" - 卡片消息:
"{\"config\":{...},\"elements\":[...]}
- 文本消息:
receive_id_type决定了receive_id的含义:chat_id(群)、open_id(个人)、user_id、union_id、email- 向群发消息时,机器人必须是该群的成员(否则报 19024)
五、消息类型详解
| 类型 | msg_type | Webhook payload 格式 | API content 格式(JSON 字符串) |
|---|---|---|---|
| 纯文本 | text | { msg_type: "text", content: { "text": "hello" } } | "{\"text\":\"hello\"}" |
| 富文本 | post | { msg_type: "post", content: { "zh_cn": { "title": "标题", "content": [[...]] } } } | 同左结构,序列化为字符串 |
| 交互卡片 | interactive | { msg_type: "interactive", card: { "config": {...}, "elements": [...] } } | "{\"config\":{...},\"elements\":[...]}" |
| 图片 | image | 不支持 Webhook 直接发 | "{\"image_key\":\"img_xxx\"}" |
⚠️ Webhook vs API 的 interactive 差异
这是最常踩的坑:| 对比项 | Webhook 模式 | API 模式 |
|---|---|---|
| 字段名 | 用 card 字段 | 用 content 字段 |
| 值类型 | JavaScript 对象 | JSON 字符串 |
| 示例 | { msg_type: "interactive", card: cardObj } | { msg_type: "interactive", content: JSON.stringify(cardObj) } |
富文本 content 结构
六、useFeishuBot Hook 接口
ChatItem 数据结构
SendRecord 数据结构
七、BotPanel UI 结构
八、连接状态
BotPanel 通过onConnectionChange 回调向 DebugWorkbench 报告连接状态:
| 条件 | 状态 | 含义 |
|---|---|---|
API 模式群列表加载成功 (chatStatus === "ready") | ✅ 已连接 | APP_ID/SECRET 正确,且有群可发 |
| Webhook 模式 | 不影响 | Webhook 不需要凭证验证 |
| 群列表加载失败 | ❌ 未连接 | 检查凭证或权限配置 |
九、接口验证清单
本场景涉及以下飞书 API,可逐一验证是否在当前系统中调通。① 获取 tenant_access_token(API 模式内部调用)
| 项目 | 值 |
|---|---|
| 方法 | POST |
| URL | https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal |
| 鉴权 | 无需 token,使用 app_id + app_secret 请求体鉴权 |
| 所需权限 | 无(仅需有效的应用凭证) |
| 用途 | 获取 tenant_access_token,用于 send_message 和 list_chats |
| 请求体 | { "app_id": "cli_xxx", "app_secret": "xxx" } |
| 成功响应 | { "code": 0, "tenant_access_token": "t-xxx", "expire": 7200 } |
| 常见错误 | 10003 → app_id 不存在;10014 → app_secret 错误 |
| 官方文档 | 自建应用获取 tenant_access_token |
Webhook 模式不需要此接口,webhook URL 本身即为鉴权凭证。
② Webhook 消息发送
| 项目 | 值 |
|---|---|
| 方法 | POST |
| URL | https://open.feishu.cn/open-apis/bot/v2/hook/{webhook_token} |
| 鉴权 | 无需 token,URL 中的 webhook_token 即为鉴权凭证 |
| 所需权限 | 无(只需群中添加自定义机器人并获取 Webhook 地址) |
| 用途 | 向指定群发送消息(文本/富文本/交互卡片) |
| 请求体(文本) | { "msg_type": "text", "content": { "text": "hello" } } |
| 请求体(卡片) | { "msg_type": "interactive", "card": { "config": { "wide_screen_mode": true }, "header": { "title": { "tag": "plain_text", "content": "标题" } }, "elements": [{ "tag": "div", "text": { "tag": "plain_text", "content": "内容" } }] } } |
| 成功响应 | { "StatusCode": 0, "StatusMessage": "success" } |
| 常见错误 | StatusCode: 9499 → “invalid hook”,URL 无效或 token 已过期;msg_type not support → interactive 类型应用 card 字段而非 content |
| 官方文档 | 自定义机器人指南 |
③ API 发送消息
| 项目 | 值 |
|---|---|
| 方法 | POST |
| URL | https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type={type} |
| 鉴权 | Authorization: Bearer {tenant_access_token} |
| 所需权限 | im:message:send_as_bot 或 im:message(以应用身份发消息) |
| 用途 | 向任意群或个人发送消息 |
| 关键参数 | receive_id_type(query,可选 chat_id / open_id / user_id / union_id / email) |
| 请求体 | { "receive_id": "oc_xxx", "msg_type": "text", "content": "{\"text\":\"hello\"}" } |
| ⚠️ content 格式 | content 必须是 JSON 字符串(被 stringify 两次),这是飞书 API 的强制要求。直传对象会返回格式错误 |
| 成功响应 | { "code": 0, "data": { "message_id": "om_xxx", "root_id": "", "parent_id": "", "msg_type": "text", "create_time": "1700000000000", "body": { "content": "{\"text\":\"hello\"}" } } } |
| 常见错误 | 19001 → 缺少 im:message:send_as_bot 权限;19024 → 机器人未在目标群中,需先将机器人添加为群成员;99991672 → 权限未开通或应用未发布新版本 |
| 官方文档 | 发送消息 |
④ 获取机器人所在群列表
| 项目 | 值 |
|---|---|
| 方法 | GET |
| URL | https://open.feishu.cn/open-apis/im/v1/chats?page_size=50 |
| 鉴权 | Authorization: Bearer {tenant_access_token} |
| 所需权限 | im:chat:readonly 或 im:chat(获取群组信息) |
| 用途 | 获取机器人已加入的所有群聊列表,用于在面板中让用户选择发送目标 |
| 关键参数 | page_size(每页数量,最大 100)、page_token(分页令牌) |
| 成功响应 | { "code": 0, "data": { "items": [{ "chat_id": "oc_xxx", "name": "技术群", "avatar": "https://...", "owner_id": "ou_xxx", "chat_mode": "group", "external": false }], "has_more": false, "page_token": "" } } |
| 常见错误 | 99991672 → 缺少 im:chat:readonly 权限;返回空列表 → 机器人未加入任何群(不是权限问题) |
| 官方文档 | 获取用户或机器人所在的群列表 |
十、踩坑记录
10.1 消息格式与发送类
| 问题 | 现象 | 原因 | 解决方案 |
|---|---|---|---|
| Webhook 发卡片失败 | 返回 msg_type not support | interactive 类型使用了 content 字段 | Webhook 模式 interactive 必须用 card 字段:{ msg_type: "interactive", card: {...} } |
| API 发消息格式报错 | 返回 param invalid 类错误 | content 传了对象而不是 JSON 字符串 | API 模式 content 必须是 JSON 字符串(stringify 两次):content: JSON.stringify({text: "hello"}) |
| 富文本内容为空 | 发送成功但飞书端看到空消息 | post 类型缺少 zh_cn 或 en_us 顶层 key | 富文本必须有语言 key 包裹:{ zh_cn: { title: "...", content: [[...]] } } |
| Webhook URL 无效 | 返回 invalid hook | URL 不完整、token 已被回收、或群已解散 | 重新在群设置中获取 Webhook URL |
10.2 权限与配置类
| 问题 | 现象 | 原因 | 解决方案 |
|---|---|---|---|
| 群列表返回空 | list_chats 返回 items: [] | 机器人未加入任何群 | 将机器人添加到至少一个群(群设置 → 群机器人 → 添加 → 搜索应用名) |
| 发消息报 19001 | ”no permission to send message” | 应用未开通 im:message:send_as_bot 权限 | 飞书开发者后台 → 权限管理 → 搜索 im:message:send_as_bot → 开通 → 发布新版本 |
| 发消息报 19024 | ”bot not in this chat” | 机器人不在目标群中 | 将机器人添加到目标群(群设置 → 群成员 → 添加 → 搜索应用名) |
| 权限开通后仍报错 | 刚在后台开权限就测试 | 权限变更需创建新版本并发布后才生效 | 版本管理 → 创建新版本 → 发布 → 等审核通过 |
| 发消息给个人报错 | open_id 正确但报 permission | 用户不在应用可见范围内 | 在应用设置中扩大可见范围,或让管理员将用户加入 |
10.3 与下游模块联动
| 问题 | 现象 | 原因 | 解决方案 |
|---|---|---|---|
| 消息看板群列表为空 | 看板中加载群列表返回空 | 机器人未开通或未加群 | 先完成本模块的「启用机器人能力 + 加入群聊」步骤 |
| 消息看板收不到新消息 | 事件订阅配了但看板无反应 | 机器人不在群中,无法接收群消息事件 | 将机器人添加到目标群后,群内消息事件才会推送 |
| 告警发送到群失败 | 告警播报的 Webhook/API 发送报错 | Webhook URL 过期或 API 权限未配置 | Webhook: 重新获取 URL;API: 确认 send_as_bot 权限已开通 |
附录:Agent 权限与安全配置参考
Agent 权限配置
| scope | name | type |
|---|---|---|
im:message:send_as_bot | 以应用身份发送消息 | tenant |
im:chat:readonly | 获取机器人所在群列表 | tenant |

