Larksuite(飞书)
飞书(国内版)和 Lark Suite(海外版)共享同一套 OpenAPI。Ongrid 的 provider 把它们当作同一种集成;按你的租户选 base URL 即可。
| 模式 | 行为 |
|---|---|
| Notification | 通过自定义机器人 webhook 推告警到飞书/Lark 群。 |
| IM bridge | 通过 WebSocket 长连接做双向 Agent 对话。 |
Notification 模式(自定义机器人)
飞书 sender 把飞书 / Lark 自定义机器人载荷 POST 到 bot 管理员给的 webhook URL。载荷形态:
{
"msg_type": "text",
"content": {"text": "[CRITICAL] swap_high node-01\nswap_in_pages > 1000 for 5m\nsource: alert\ndedupe: alert:swap_high:device=7"},
"timestamp": "1717012345",
"sign": "<base64-hmac>"
}签名 —— sign 字段
在飞书自定义机器人上开启 签名校验 时,飞书会给你一个共享 secret。sender 这样算 sign:
stringToSign = timestamp + "\n" + secret
sign = base64(HMAC-SHA256(key=stringToSign, message=<empty>))是的 —— secret 同时承担 HMAC 的 key 和参与 string-to-sign 两个角色。这是飞书文档定义的算法,signFeishu 是其实现。
把 Secret 留空的话,Ongrid 不带 sign / timestamp 直接 POST —— 只有当你的 bot 关闭了签名校验(或改用 IP 白名单)时才能跑通。
配置
- 在飞书群 → 设置 → 群机器人 → 添加机器人 → 自定义机器人。给它一个名字和头像。
- 勾 签名校验,复制 secret。
- 复制 webhook URL —— 长得像
https://open.feishu.cn/open-apis/bot/v2/hook/<uuid>(Lark Suite 是…/open.larksuite.com/…)。 - 在 Ongrid:Settings → Channels → New → Provider =
feishu→ Endpoint = webhook URL → Secret = 签名 secret。
自定义机器人 ≠ App
通知模式用 自定义机器人(仅群作用域、仅 webhook)。IM bridge 模式用飞书 App(租户作用域、OAuth + 事件)。两个是不同概念,凭据互不通用。
IM bridge 模式(长连接 stream)
双向 bridge 用 飞书长连接,承载方是官方 github.com/larksuite/oapi-sdk-go/v3/ws 客户端。manager 主动连到飞书事件端点,飞书通过 WebSocket 推事件 —— 不需要任何公网 webhook URL。
为什么选 stream 而非 webhook
为了向后兼容,schema 里保留 webhook 模式(mode=webhook),但长连接 stream 才是推荐路径:
- manager 不需要任何公网入口。
- 复用 supervisor 的 reconnect-with-backoff(SDK 自己也有重连;supervisor 给终止类失败加一层外层循环)。
- SDK 内部处理签名校验 + AES 解密,所以 stream 模式不用填
encrypt_key——encrypt_key仅 webhook 模式需要。
字段映射
im_apps 列 | 飞书含义 |
|---|---|
provider | "feishu" |
mode | "stream"(推荐)或 "webhook" |
app_id | 飞书 app_id(cli_…)。 |
app_secret | 飞书 app_secret。 |
verify_token | 可选。webhook 模式签名校验用。 |
encrypt_key | webhook 模式必填,stream 模式忽略。 |
allow_from | 可选。飞书天然受租户门控,白名单不强制。 |
default_locale
运维写中文的飞书租户填 zh;英文 Lark 团队填 en。空(默认)让 LLM 跟随用户。
配置步骤
- 开发者后台 → 创建企业自建应用。拿到
app_id+app_secret。 - 应用功能 → 机器人 → 启用。把 bot 加进测试群。
- 权限管理 → 授予:
im:message—— 读发给 bot 的消息。im:message.group_at_msg—— 群 @ 事件。im:message.p2p_msg—— DM 事件。im:message:send_as_bot——SendText/EditText。
- 事件订阅 → 长连接模式 (long-connection) → 启用。
- 在 Ongrid:Settings → IM bridge → New → Provider =
feishu→ Mode =stream→ 粘app_id+app_secret。保存并 Enable。 - 在群里
@bot。Agent 接管。
tenant_access_token 缓存
出站调用(SendText、EditText)用 tenant_access_token 鉴权,过期前 200s 内主动续期。token 按 Client 实例缓存,由 sync.Mutex 保护;换凭据就要重建 client。见 tenantAccessToken。
编辑也必须带 msg_type
飞书的 PUT /open-apis/im/v1/messages/<id> 要求 body 里带 msg_type —— 不带就返 code: 99992402(字段校验失败),尽管"编辑消息"那篇文档没把这点写明显。provider 一律带 msg_type: text。
Webhook 模式(遗留,保留兼容)
UI 里 webhook 模式仍可选;supervisor 自己做签名校验和载荷解密。
签名校验
HTTP handler 读 X-Lark-Signature 然后跑 VerifyEventSignature:
sig = sha256(timestamp + nonce + encrypt_key + body), hex-encoded是的 —— SHA-256 而非 HMAC。encrypt_key 在哈希输入里扮演共享 secret 的角色。校验器永远不会因坏输入 panic;不匹配返回 ErrBadSignature。
载荷解密
设置了 encrypt_key 时,飞书把事件 JSON 包成 AES-256-CBC。解密用:
- Key:
SHA-256(encrypt_key)。 - IV:base64 解码后的前 16 字节。
- Padding:PKCS#7。
见 DecryptEvent。
能用 stream 就用 stream
Webhook 模式需要公网 HTTPS 端点 + 上面那套 encrypt_key 装配。长连接 stream 两个都不需要。仅在确实需要时(比如要把飞书事件接到已有的公网 webhook 路由)才用 webhook 模式。
小坑
- 飞书 stream 客户端今天不强制
allow_from—— 飞书平台本身就有租户门控,只有企业成员能找到 bot。如果租户里有访客 / 外部合作者,把他们的open_id填进allow_from,可以进一步收紧对话。 - 贴纸、文件、卡片、富文本会被丢(只有
msg_type == "text"触发 Agent)。和 Telegram / Slack 同一份 S1 契约。 RootId字段被捕获为ImThread.ImThreadID,所以 thread 内回复延续同一会话 —— 用户不用每问一句都重置上下文。