# 机器人管理公共 API 接入说明 本文档是其他业务项目和 AI 编程代理接入 `bot-management` 的权威调用说明。生产公共 API 为 `https://bot.yunxuanwl.cn/api/v1`。 ## 1. AI 快速结论 - 仅从业务服务端调用,不要在网页、H5、Flutter 安装包中保存生产 AppKey。 - 使用 AppID、Unix 秒级时间戳、随机 nonce、原始 payload 字符串和 AppKey 计算 HMAC-SHA256 + Base64 签名。 - 不提供机器人或会话筛选时,向当前应用配置的全部启用目标广播。 - 提供机器人和会话筛选时取交集,只发送当前应用已经配置的目标组合。 - 优先使用仓库 `sdks/` 下的 Rust、Go、TypeScript 或 Dart SDK。 - `request_id` 当前用于追踪,不是服务端强制幂等键;不要对结果不确定的请求自动重试。 ## 2. 服务地址 | 用途 | 地址 | | --- | --- | | 在线落地页 | `https://bot.yunxuanwl.cn/` | | SDK 下载 | `https://bot.yunxuanwl.cn/#downloads` | | 公共 API | `https://bot.yunxuanwl.cn/api/v1` | | 管理后台 | `https://bot-ht.yunxuanwl.cn/admin/` | | 飞书事件回调 | `https://bot-ht.yunxuanwl.cn/api/v1/channels/feishu/events` | | 本地公共 API | `http://127.0.0.1:13250/api/v1` | 公共域名 `bot.yunxuanwl.cn` 的根路径提供落地页、文档与 SDK;后端 API 只开放健康检查和消息发送。项目、应用、机器人、会话及凭证配置必须在管理后台完成。 ## 3. 接入准备 1. 在管理后台确认或创建业务项目。 2. 在项目下创建应用,取得 `appid` 和 `appkey`。 3. 在“应用管理 → 设置机器人”中配置允许该应用使用的机器人和会话。 4. 把公共 API 地址、AppID、AppKey 写入业务服务端环境变量。 5. 安装 SDK,或严格按照下文规则签名。 AppID 可以作为普通配置保存;AppKey 是敏感凭证,只能进入服务端安全配置。不要在聊天、issue、日志、提交记录或错误响应中输出 AppKey。 ## 4. 健康检查 ```http GET /health ``` 不需要签名。 ```json { "code": 0, "message": "ok", "data": { "service": "bot-management-api", "status": "ok" } } ``` ## 5. 发送消息 ```http POST /messages/send Content-Type: application/json ``` ### 5.1 外层签名请求 ```json { "appid": "", "timestamp": 1760000000, "nonce": "unique-random-value", "signature": "base64-hmac-sha256", "payload": "{\"channel\":\"feishu\",\"content\":\"任务审核完成\",\"bot_ids\":[100001],\"chat_ids\":[\"oc_xxx\"],\"request_id\":\"task-reviewed-100001\"}" } ``` | 字段 | 必填 | 说明 | | --- | --- | --- | | `appid` | 是 | 管理后台生成的应用标识。 | | `timestamp` | 是 | Unix 秒级时间戳,允许误差 5 分钟。 | | `nonce` | 是 | 当前有效窗口内不可重复的随机字符串。 | | `signature` | 是 | 按第 7 节生成的标准 Base64 HMAC-SHA256。 | | `payload` | 是 | 参与签名并发送的同一份紧凑 JSON 字符串。 | ### 5.2 payload 字段 | 字段 | 类型 | 必填 | 说明 | | --- | --- | --- | --- | | `channel` | string | 是 | `feishu`、`telegram` 或 `dingtalk`。目标机器人渠道必须一致。 | | `content` | string | 是 | 非空消息正文。 | | `bot_id` | integer | 否 | 单个机器人 ID;必须为正整数。 | | `bot_ids` | integer[] | 否 | 一个或多个机器人 ID,最多 100 项;不能与 `bot_id` 同时提供。 | | `chat_id` | string | 否 | 单个会话 ID。 | | `chat_ids` | string[] | 否 | 一个或多个非空会话 ID,最多 100 项;不能与 `chat_id` 同时提供。 | | `request_id` | string | 否 | 调用方追踪编号;当前不提供服务端强制去重。 | | `mock` | boolean | 否 | 测试开关;生产业务不要设置。 | 显式空数组会返回 `400`,不会被解释成“不筛选”。数组在服务端去重,会话 ID 会去除首尾空白。 ## 6. 目标路由规则 所有目标都必须来自当前应用的 `app_bot_targets` 启用配置;接口不会使用机器人默认会话,也不允许显式 ID 绕过应用关系。 | 机器人条件 | 会话条件 | 行为 | | --- | --- | --- | | 无 | 无 | 向当前应用、指定渠道下的全部启用目标广播。 | | 有 | 无 | 向指定机器人在当前应用下配置的全部启用会话发送。 | | 无 | 有 | 向当前应用中匹配指定会话 ID 的全部启用目标发送。 | | 有 | 有 | 机器人与会话条件取交集,只发送数据库中已配置的组合。 | 指定筛选条件时,请求中的每个机器人 ID 和会话 ID 都必须在最终交集内至少匹配一个目标。任何指定 ID 未匹配时,整次请求会在外部平台发送前失败,不会只发送部分匹配目标。 ### 6.1 默认广播 ```json { "channel": "feishu", "content": "系统维护通知", "request_id": "maintenance-20260815" } ``` ### 6.2 单机器人全部会话 ```json { "channel": "feishu", "content": "任务审核完成", "bot_id": 100001 } ``` ### 6.3 多机器人、多会话交集 ```json { "channel": "feishu", "content": "财务状态发生变化", "bot_ids": [100001, 100002], "chat_ids": ["oc_xxx", "oc_yyy"] } ``` ## 7. 签名规则 签名原文必须严格为: ```text appid + "\n" + timestamp + "\n" + nonce + "\n" + payload ``` 生成步骤: 1. 把业务 payload 对象序列化成紧凑 JSON 字符串,只序列化一次。 2. 用 AppID、时间戳、nonce 和该字符串拼接签名原文。 3. 使用 AppKey 计算 HMAC-SHA256。 4. 对 HMAC bytes 做标准 Base64 编码。 5. 把参与签名的原字符串原样放入外层 `payload`,不得再次格式化。 伪代码: ```text payload = compact_json(payload_object) raw = appid + "\n" + unix_seconds + "\n" + nonce + "\n" + payload signature = base64(hmac_sha256(appkey, raw)) ``` 同一个 nonce 在有效窗口内只能成功使用一次。签名失败、时间戳过期或 nonce 重复返回 `401`。 ## 8. 成功响应 ```json { "code": 0, "message": "ok", "data": { "status": "accepted", "channel": "feishu", "message_log_id": 100003, "mock": false, "result_message": "feishu sent message_id=...", "provider_message_id": "...", "results": [ { "bot_id": 100001, "chat_id": "oc_xxx", "message_log_id": 100003, "status": "accepted", "mock": false, "result_message": "feishu sent message_id=...", "provider_message_id": "..." } ] } } ``` | `data.status` | 含义 | | --- | --- | | `accepted` | 全部目标发送成功。 | | `partial` | 部分目标成功、部分失败,必须逐项检查 `results`。 | | 全部失败 | 返回 HTTP 错误响应,不返回成功 envelope。 | 顶层 `message_log_id`、`mock`、`result_message` 和 `provider_message_id` 为历史单发兼容字段,批量发送时只代表 `results` 第一项。新调用方应以 `results` 为准。 ## 9. 错误处理与重试 错误示例: ```json { "code": 400, "message": "部分会话 ID 未匹配当前应用的启用发送目标" } ``` | HTTP / code | 常见原因 | 调用方行为 | | --- | --- | --- | | `400` | payload 非法、筛选冲突、目标未配置、渠道不匹配 | 修正请求或后台配置,不自动重试。 | | `401` | 应用不存在、签名错误、时间戳过期、nonce 重复 | 检查时钟和凭证,使用新 nonce。 | | `403` | 应用停用或策略拒绝 | 联系管理员处理。 | | `500` | 数据库、缓存或内部错误 | 记录安全的追踪信息,人工或受控重试。 | 外部平台可能已接收消息但客户端没有收到响应。由于 `request_id` 当前只记录到消息日志,并不阻止重复发送,SDK默认不自动重试不确定结果的请求。业务方需要强幂等时,应先在自身发件箱中保存状态,再人工或按业务规则确认后重试。 ## 10. SDK 仓库提供: | 语言 | 本地路径 | 在线下载 | | --- | --- | --- | | Rust | `sdks/rust` | `https://bot.yunxuanwl.cn/downloads/bot-management-sdk-rust.zip` | | Go | `sdks/go` | `https://bot.yunxuanwl.cn/downloads/bot-management-sdk-go.zip` | | TypeScript | `sdks/ts` | `https://bot.yunxuanwl.cn/downloads/bot-management-sdk-ts.zip` | | Dart / Flutter | `sdks/dart` | `https://bot.yunxuanwl.cn/downloads/bot-management-sdk-dart.zip` | 完整示例见 [`SDK用法.md`](SDK用法.md) 和各 SDK 的 `README.md`。 ## 11. 飞书事件回调边界 飞书事件回调不是业务项目调用接口。回调地址只配置在飞书开放平台: ```text https://bot-ht.yunxuanwl.cn/api/v1/channels/feishu/events ``` 当前支持 URL 验证、明文消息事件和机器人进群/出群事件。Encrypt Key 加密推送尚未完整支持,启用加密前必须补齐解密处理。 ## 12. AI 接入检查清单 - [ ] 代码运行在可信服务端,不在客户端保存 AppKey。 - [ ] 使用生产公共 API `https://bot.yunxuanwl.cn/api/v1`。 - [ ] 管理后台已配置应用允许使用的机器人和会话。 - [ ] payload 只序列化一次,签名和请求使用同一字符串。 - [ ] 每次请求使用新的随机 nonce 和当前 Unix 秒级时间戳。 - [ ] 明确需要广播还是精确筛选。 - [ ] 同时提供机器人和会话时理解为交集,而不是任意笛卡尔积。 - [ ] 以 `data.results` 判断每个发送目标结果。 - [ ] 不把 `request_id` 误认为服务端强制幂等键。 - [ ] 不记录 AppKey、签名和包含敏感业务数据的完整请求体。 ## 钉钉 Stream 与群发送 `channel=dingtalk` 复用相同签名、应用目标授权、筛选与每目标结果协议。`chat_id` 为钉钉群 `openConversationId`(`cid` 开头),机器人必须在群内且具有群消息发送权限;当前主动发送仅支持群文本 `sampleText`,不支持钉钉私聊发送。`provider_message_id` 保存钉钉 `processQueryKey`,代表平台已受理,最终可见性需在群内确认。 钉钉凭证缺失或发送失败返回目标 `failed`,不会静默降级为模拟成功;网络超时可能已投递,调用方应核查后再决定是否重发。显式 `mock=true` 保持原有模拟语义。 独立 `dingtalk-stream` 进程订阅机器人消息回调与开发者平台勾选的事件。群里需要 @机器人才能触发收消息;收到消息后登记会话并写入去重 inbox,已绑定应用的消息同时记入消息日志。消息接收不自动回复,也不自动给应用授权新的群目标。入站记录仅保留白名单业务字段,不保存 `sessionWebhook`、令牌或发送者私有标识。