首页 产品 为什么选 OCTO 解决方案 文档 关于
文档中心 / 用户手册 / 第一个 Bot 开发教程
← 返回文档中心

第一个 Bot 开发教程

OCTO 文档中心 · 用户手册

  • 04-user-manual/01-bot-developer-onboarding.md v0.1(接入前准备/概念/鉴权概览)
  • 06-api-integration/OCTO-Bot-API接入快速上手指南-v0.1.md v0.1(curl骨架/字段/禁止项)
  • 06-api-integration/Bot-API概览.md v0.1(端点分组)
  • source-repos/openclaw-channel-octo/src/api-fetch.ts(鉴权header/sendMessage/register/heartbeat/events 调用点)
  • source-repos/octo-server/modules/bot_api/(bot_api.go 路由/auth.go/events.go/groups.go/file.go/card_profile.go)
  • 02-architecture/OCTO总体架构.md v1.0(请求5步/ACK/at-least-once)
  • 03-deployment-ops/OCTO部署形态总览.md v1.0(apiBase/端口/HTTPS)
  • 源码研究/schema边界汇总卡-v0.4.md / nonBot入站路径一致性验证小卡-v0.1.md(硬校验/拒卡/OBO边界)

创建第一个 Bot 实操 Walkthrough v0.1

状态:`draft` / v0.1。confidence:medium。

本文带你从零开始,在 OCTO 上创建第一个 Bot、发出第一条消息、收到第一条回复。本文基于 [01-bot-developer-onboarding.md](01-bot-developer-onboarding.md) 的概念,给出 step-by-step 实操。

所有未确认的 UI 路径、URL 前缀、数字常量一律用 `{{占位}}` 或 `⚠️ 待确认` 标注,并对应到 061 提问稿问题编号;等产品管家回答后回填,不编造可运行的完整 curl。本文不替代官方 API 文档,不作为客户交付手册。


适用场景与前置假设

  • 你走哪条路径:本文默认走 路径② 直接调 Bot REST API(用 curl/HTTP 客户端),不依赖 OpenClaw 插件;这是最通用、最能看清机制的路径。走完本文你也能理解路径①插件背后做了什么。
  • 你需要准备:
  1. 一套能访问的 OCTO 部署,知道 HTTP/HTTPS 入口地址(参考 `03-deployment-ops/OCTO部署形态总览.md` v1.0 端口表)。
  2. 一个能登录 OCTO 客户端(Web/PC)的普通用户账号,用来测试。
  3. OCTO 管理员配合创建 Bot 身份。
  4. 一个 HTTP 客户端(本文用 curl;Postman/Node/Python/Go 等价)。

ℹ️ 如果你要做带 AI 能力的 Agent,路径① openclaw-channel-octo 插件会自动帮你做本文里的 register/heartbeat/events 轮询/ACK/card profile 协商/降级,不用自己写。走完本文理解机制后再切插件路径。


Step 1:获取 botToken

1.1 请管理员创建 Bot 身份

  • 需要提供给管理员的信息:Bot 名字(中英文均可)、头像(可选)、简介(可选)、Bot 用途说明。

⚠️ 待确认(对应 061 P0-API与鉴权 Q2 / onboarding K1-K2):

- Bot 创建入口是在 octo-admin 管理后台、普通客户端的"BotFather"页面,还是通过 API 自助注册——源码 `modules/botfather` 存在但具体 UI 路径未确认。

- 创建 Bot 需要什么角色权限(superAdmin?space 管理员?普通开发者是否可自助创建?)。

- botToken 是在创建成功后立即展示一次,还是可以在管理页反复查看/重置。

- botToken 形态(长度/前缀/JWT 还是 opaque 字符串)、是否会过期、吊销/重置流程。

1.2 保存 botToken 并确认 apiBase

  • botToken:字符串,类似 ob-xxxxxxxxxxxxxxxx(示例形态,实际以管理员给的为准,本文不编具体前缀)。
  • Bot 的 uid(Bot 在 OCTO 里的用户 ID,32 位 hex)。
部署形态 apiBase(占位)
Docker 单机 HTTP http://:28080
Docker 单机 HTTPS https://:28443
K8s + Ingress https://
前置反代 https://

⚠️ 待确认(onboarding K3/A1):URL 前缀是直接挂根 `/v1/bot/...` 还是带 `/api/v1/bot/...`——openclaw-channel-octo 源码直接打 `/v1/bot/*`,docker nginx 同时反代 `/v1/` 和 `/api/v1/`;生产以实际 ingress 配置为准。本文统一用占位符 `{{apiBase}}`。

1.3 (可选)把 Bot 拉进一个测试群

⚠️ 待确认:Bot 能否自己建群、拉人(061 P0-API与鉴权 Q4)——本文先让人工把 Bot 拉进群,不假设 Bot 有建群 API。

1.4 确认 channel_id

  • 方式 A(推荐先走):先让 Bot 跑起来,在群里 @Bot 发一句话,Bot 从 /v1/bot/events 拉到的入站消息事件里会带 channel_id 和 channel_type(见 Step 4)。
  • 方式 B:调 GET {{apiBase}}/v1/bot/groups(路由源码 bot_api.go:416 已见)拿 Bot 所在群列表,从返回里读 group_no 作为 channel_id。

⚠️ 待确认(A3):`channel_type` 数字常量值——群聊/私聊/Thread 分别对应哪个数字,源码 constants.ts 有定义但当前未精读;本文用 `{{channelType}}` 占位。实操建议先走方式 A,从入站事件里读服务端给的 `channel_type`,直接回发即可。


Step 2:鉴权连通性测试(register + heartbeat)

2.1 调 heartbeat 验证 token

curl -i -X POST "{{apiBase}}/v1/bot/heartbeat" \
  -H "Authorization: Bearer {{botToken}}"

ℹ️ 路径①插件会自动调 heartbeat,间隔由插件内部实现;路径②自实现时建议周期性调用(具体间隔待确认 A7,先按 30s 起步,观察服务端是否返回 401/掉线信号)。

2.2 (可选)调 register 刷 IM token

curl -i -X POST "{{apiBase}}/v1/bot/register" \
  -H "Authorization: Bearer {{botToken}}" \
  -H "Content-Type: application/json" \
  -d '{}'

⚠️ 不要把 register 当"登录接口"在每次发消息前都调——它是限流豁免的重连入口,正常在线状态用 heartbeat 保活即可。

2.3 (可选)查询 Bot 卡片能力 profile

curl "{{apiBase}}/v1/bot/card/profile" \
  -H "Authorization: Bearer {{botToken}}"

Step 3:发送第一条文本消息

3.1 准备请求体

  • Authorization: Bearer {{botToken}}
  • Content-Type: application/json
{
  "channel_id": "{{channelId}}",
  "channel_type": {{channelType}},
  "payload": {
    "type": "text",
    "content": "👋 你好,我是第一个 Bot!收到请回复。"
  },
  "client_msg_no": "walkthrough-msg-001"
}
  • channel_id:目标会话 ID(群填 group_no,私聊填对方 uid)。
  • channel_type:数字常量,待确认 A3(群/私聊/Thread 分别是几);先走方式 A 从入站事件读。
  • payload.type:文本消息类型字符串,待确认 A4(源码 sendTextMessage 构造 type 字段,精读后可确认是 "text" 还是数字)。
  • payload.content:文本内容。
  • client_msg_no:客户端消息唯一 ID(UUID 或递增序号),用于幂等去重(具体去重语义待确认),建议每次发送生成新值。

3.2 发 curl

curl -i -X POST "{{apiBase}}/v1/bot/sendMessage" \
  -H "Authorization: Bearer {{botToken}}" \
  -H "Content-Type: application/json" \
  -d '{
    "channel_id": "{{channelId}}",
    "channel_type": {{channelType}},
    "payload": { "type": "text", "content": "👋 你好,我是第一个 Bot!收到请回复。" },
    "client_msg_no": "walkthrough-msg-001"
  }'
  • 401:token 错/过期;重新走 Step 2。
  • 400 Bad Request:
  • 是否漏了必填字段;
  • 是否把 channel_type 写成字符串(数字常量,不加引号);
  • 若发卡片(本步不用):检查 type/profile/card_version/card 四必填、是否双传 card+template_ref、是否伪造了 catalog_provenance(详见 059 指南第 4 章 10 条禁止项)。
  • 403 Forbidden:Bot 不在目标群里,或对该会话无发消息权限。
  • 429 Too Many Requests:被限流;等待 Retry-After 秒数(具体阈值待确认 A8)再重试。
  • 客户端看不到消息:确认客户端版本是否支持;检查你是往正确的 channel_id/channel_type 发;可能 WuKongIM 长连接抖动,稍等几秒。

⚠️ 注意硬边界(源码已验证):

- 用 bot_token + `/v1/bot/sendMessage` 才能发;普通用户 token 发消息的接口是另一套,且普通用户 API 绝对拒绝卡片 payload(046 事实)。

- 不要同时在 payload 里传 `card` 和 `template_ref`(XOR,双传 400)。

- 不要伪造 `catalog_provenance`(入站 loud 4xx 拒绝)。

3.3 验证消息已送达

  • 消息应该出现在会话里,Bot 的名字/头像正确显示;
  • 如果是群,其他群成员也能看到。

Step 4:接收并回复消息(events 长轮询 + ACK)

4.1 启动事件轮询

curl -i -X POST "{{apiBase}}/v1/bot/events" \
  -H "Authorization: Bearer {{botToken}}" \
  -H "Content-Type: application/json" \
  -d '{}'
{
  "events": [
    {
      "event_id": "evt-xxxxxxx",
      "type": "message",
      "channel_id": "...",
      "channel_type": ...,
      "message": {
        "message_id": "...",
        "sender_uid": "...",
        "content": { "type": "text", "text": "@Bot 你好" }
      }
    }
  ],
  "cursor": "next-cursor-value"
}

⚠️ 待确认(A8):完整事件 JSON schema(事件类型枚举、字段名、@提及格式、附件格式)、长轮询超时秒数、cursor 传参方式。

4.2 让普通用户在群里 @Bot 发一句话

  • event_id:用于 ACK;
  • message.sender_uid:发消息的用户 uid(不是你 Bot 自己);
  • message.content:消息文本;
  • channel_id / channel_type:这个会话的 ID 和类型——这正是你在 Step 3 需要的,可以从这里读出来直接回发。

ℹ️ 除了 `message` 事件,你还可能收到:

- `card_action`:用户点击你发的卡片按钮时产生(Action.Submit 回调);

- 成员变更事件(加群/退群);

- 其他系统事件(具体清单待确认)。

4.3 ACK 事件,避免重复投递

curl -i -X POST "{{apiBase}}/v1/bot/events/{{event_id}}/ack" \
  -H "Authorization: Bearer {{botToken}}"
  1. 拉到 events;
  2. 处理业务(解析、回消息);
  3. 处理成功后再 ACK;
  4. 你的业务逻辑要按 `event_id` 做幂等(重复事件到来时不要重复回复)。

⚠️ 不要先 ACK 再处理——如果 ACK 后处理崩溃,消息就丢了。也不要长时间不 ACK——会重复投递,但不会丢。

4.4 回消息

{
  "channel_id": "<从事件里抄来的 channel_id>",
  "channel_type": <从事件里抄来的 channel_type>,
  "payload": {
    "type": "text",
    "content": "收到!你刚刚说的是:<原消息内容>"
  },
  "client_msg_no": "walkthrough-reply-001"
}

4.5 保持轮询循环

loop:
  resp = POST /v1/bot/events  (长轮询,阻塞)
  for evt in resp.events:
    if 已处理过 evt.event_id: continue
    处理 evt(业务逻辑 / 回消息 / 调其他接口)
    POST /v1/bot/events/{evt.event_id}/ack
  带上 resp.cursor 进入下一轮(具体传参方式待确认)

ℹ️ 路径①插件已经帮你写好这个循环了,包含单线程并发、ACK 去重、重试、错误处理、心跳保活。


Step 5:验证收发成功

检查项 如何验证
✅ 鉴权通 Step 2 heartbeat 返回 200
✅ 能拿到群列表/入站事件 GET /v1/bot/groups 或 events 能看到 Bot 所在群
✅ 发出的消息对端可见 普通用户账号能在群里看到 Bot 发的消息
✅ 能收到 @ 消息 普通用户 @Bot 后,events 长轮询拿到 message 事件
✅ 能回复 Bot 回的消息普通用户能看到
✅ ACK 生效 下一次轮询不重复收到已 ACK 的事件
✅ 心跳保活 跑 5 分钟不重启,Bot 仍能收到新事件

常见报错速查

现象 可能原因 排查
heartbeat 返回 401 botToken 错/过期/被吊销 核对 token,联系管理员
sendMessage 返回 403 Bot 不在目标群,或对会话无权 把 Bot 拉进群;核对 channel_id
sendMessage 返回 400 字段缺失/类型错/双传 card+template/伪造 catalog_provenance 对照 059 第 4 章 10 条禁止项
sendMessage 返回 429 触发限流 等 Retry-After,检查是否同 IP 其他 Bot 挤占(2026-08-05 事故)
events 一直空转无消息 Bot 没在群里、没人 @Bot、长轮询 cursor 未正确续传 换种方式触发(手动 @);核对 cursor 传参
事件重复投递 没 ACK / ACK 前崩溃 确保处理成功后 ACK;做 event_id 幂等
发卡片显示纯文本 客户端版本过旧,或 Bot profile 没开对应 surface 先 GET card/profile 看支持情况
客户端看不到 Bot 消息 channel_id/channel_type 错;WuKongIM 长连接抖动 用入站事件的 channel_id/channel_type 回发,不要自己拼

进阶:发你的第一张卡片(可选)

curl -X POST "{{apiBase}}/v1/bot/sendMessage" \
  -H "Authorization: Bearer {{botToken}}" \
  -H "Content-Type: application/json" \
  -d '{
    "channel_id": "{{channelId}}",
    "channel_type": {{channelType}},
    "payload": {
      "type": 17,
      "profile": "octo-chat/v1",
      "card_version": 1,
      "card": {
        "type": "AdaptiveCard",
        "version": "1.5",
        "body": [
          { "type": "TextBlock", "text": "👋 第一张卡片", "weight": "Bolder" },
          { "type": "TextBlock", "text": "Hello from Bot walkthrough!" }
        ]
      }
    },
    "client_msg_no": "walkthrough-card-001"
  }'

待确认项(等 061 提问稿回答后回填)

# 待确认项 对应 061 问题 / onboarding 编号
W1 Bot 创建入口页面/URL、所需角色权限 P0-API与鉴权 Q2 / K1
W2 botToken 形态/生命周期/吊销/重置方式 P0-API与鉴权 Q2 / K2
W3 {{apiBase}} 标准 URL 前缀(/v1 vs /api/v1) P0-API与鉴权 Q1 / K3 / A1
W4 channel_type 数字常量值(群/私聊/Thread) P0-API与鉴权 Q3 / A3
W5 payload.type 文本消息类型字符串/数字 P0-API与鉴权 Q3 / A4
W6 sendMessage 完整响应字段(message_id/ts 等) P0-API与鉴权 Q3 / A5
W7 register 完整请求/响应、何时需要调用 P0-API与鉴权 Q2 / A6
W8 heartbeat 推荐间隔、超时/401 处理策略 P0-API与鉴权 Q2 / A7
W9 events 完整 JSON schema、事件类型枚举、cursor 传参、长轮询超时、ACK 语义细节 P0-API与鉴权 Q3 / A8
W10 完整错误码表与 429 Retry-After 行为 P0-API与鉴权 Q2-Q3 / A8
W11 文件上传/图片/语音/表情/引用回复/@mention 等消息类型的 payload 格式 P0-API与鉴权 Q3 / K9
W12 Bot 建群/改群信息/加人/Thread 创建等写操作 API 是否开放 P0-API与鉴权 Q4 / K6

相关文档

  • [01-bot-developer-onboarding.md](01-bot-developer-onboarding.md) v0.1 —— 动手前的概念、鉴权机制、常见坑
  • [06-api-integration/OCTO-Bot-API接入快速上手指南-v0.1.md](../04-api-integration/OCTO-Bot-API接入快速上手指南-v0.1.md) v0.1 —— 三种接入路径与完整 curl 骨架
  • [06-api-integration/Bot-API概览.md](../04-api-integration/Bot-API概览.md) v0.1 —— 9 组 40+ 端点分组
  • [02-architecture/OCTO总体架构.md](../02-architecture/OCTO总体架构.md) v1.0 —— 请求 5 步 / ACK / at-least-once
  • [03-deployment-ops/OCTO部署形态总览.md](../03-deployment-ops/OCTO部署形态总览.md) v1.0 —— apiBase/端口/HTTPS
  • [售前交付/openclaw-channel-octo卡片能力正式口径-v1.0.md](../08-presales-delivery/openclaw-channel-octo卡片能力正式口径-v1.0.md) —— 卡片能力边界与红线

v0.1 骨架边界说明

  • 本文"发送文本消息"的请求体字段(channel_id/channel_type/payload/content/client_msg_no)有 openclaw-channel-octo api-fetch.ts:381-393 直接源码证据。
  • register/heartbeat/events/ack/card/profile 端点路径有 api-fetch.ts 与 bot_api.go 路由双重证据。
  • 卡片四必填/raw+template XOR/catalog_provenance 拒卡/render_profile 覆盖 有 044A/044C/046 源码验证事实。
  • UI 入口、URL 前缀、channel_type 常量、payload.type 字符串、响应字段、错误码、心跳间隔、事件 schema 未在当前源码精读范围直接验证的项一律用 {{占位}} 或 ⚠️ 标注,不编造可运行值。
  • 本文不是官方 API 参考文档;等 061 产品管家回答回填 + 官方 API 文档开放后升 v0.2。