机器人开放平台 · 开发者文档

SoChat 开放平台

使用 Bot Token 调用 REST API、接收 Webhook Update。P1 已扩展到多媒体消息(图片 / 文件 / 视频 / 语音 / 位置)、消息编辑与删除、双层速率配额、投递监控与死信重投;心智对齐 Telegram Bot API,并强化企业租户与审计。

Base URL: https://www.sochatlive.com 鉴权: Authorization: Bearer sbot_<token> 当前版本: P1 · v0.9
正在加载开发者控制台…

产品定位与接入边界

SoChat 开放平台让企业在自有基础设施中部署机器人服务,平台负责身份、路由、投递与安全边界,业务逻辑仍由客户掌控。

开放申请

任何符合条件的用户都可提交机器人创建申请,但必须在平台 Admin 审核通过后才能拿到 Token。

不托管业务逻辑

客户代码运行在客户基础设施内,SoChat 不提供机器人代码托管或 Serverless 运行时。

Bot API 独立鉴权

开发者请求统一用 Bot Token 调用,和 App 用户 JWT、Admin JWT 完全分离。

异步投递

Webhook 更新通过异步队列投递,失败可重试,不阻塞消息主链路。

当前能力(P0 + P1)

  • 开放申请与平台 Admin 审核通过后签发 Token(P0)
  • Bot Token 哈希存储、轮换与吊销(P0)
  • Webhook 配置、签名、重试、死信与投递日志(P0 + P1)
  • 文本 / 图片 / 文件 / 视频 / 语音 / 位置消息发送(P1)
  • `editMessage` / `deleteMessage` 与 `edited_message`、`message_deleted` 事件(P1)
  • Bot 与企业两级速率配额,429 + `Retry-After` 响应(P1)
  • Prometheus 指标、投递监控看板与开发者投递日志(P1)
  • Admin 控制台中的机器人审核、监管、配额与监控能力(P0 + P1)

快速开始

从提交申请到收到第一条 Update,按下面四步完成最短链路。

STEP 01

提交申请

直接在本页「开发者中心」登录后提交,或在 SoChat App 内进入「我的机器人」填写信息。

  • 任何登录用户均可申请
  • 可选绑定企业 / 租户
  • 提交后状态为 pending
STEP 02

等待审核

平台 Admin 在机器人管理后台通过或拒绝,通过后生成系统账号与 Bot Token。

  • 通过后 Token 仅首次展示
  • 数据库只保存 Token 哈希
  • 可随时吊销、重签
STEP 03

配置 Webhook

回到本页「开发者中心」选中机器人,保存一次性 Token 并登记 HTTPS 回调与 secret_token。

  • URL 必须为 https://
  • secret_token 用于 HMAC-SHA256
  • 重试保持同一 update_id
STEP 04

开始收发

把机器人加入群聊,群主 @bot 或 / 命令即可收到 Update;用 sendMessage / sendPhoto 等接口回消息,支持编辑与撤回。

  • 群默认 Privacy Mode ON
  • 私聊全部投递
  • 支持文本、图片、文件、视频、语音、位置
  • 可通过 editMessage / deleteMessage 修改或撤回

接入流程

1

创建机器人申请

在本页「开发者中心」或 SoChat App 内「我的机器人」登录后提交名称、用户名、描述与用途说明。

2

平台 Admin 审核

Admin 在后台机器人管理执行通过 / 拒绝,审核动作写入审计日志(`BOT.APPROVE` / `BOT.REJECT` 等)。

3

保存 Token 与 Webhook

审核通过后回到「开发者中心」即可读取一次性 Token,并直接登记 HTTPS Webhook 与 `secret_token`。

4

加入会话并联调

群主在「添加成员」中搜索机器人拉入群,命令或 @bot 触发 Update;`sendMessage` 回消息。

最小闭环清单

Bot Token

鉴权

前缀 `sbot_`,仅首次返回明文;之后库内只存哈希。

Webhook

入站

平台向客户 HTTPS 端点投递 JSON;带 `X-SoChat-Signature` 与 `X-SoChat-Update-Id`,失败重试并落投递日志。

消息收发

出站

支持 `sendMessage` 文本以及 `sendPhoto / sendDocument / sendVideo / sendAudio / sendLocation` 多媒体;发送后可 `editMessage`、`deleteMessage`。

Privacy Mode

可见性

群内仅 `/` 命令、`@username`、回复机器人消息或 @mention 会投递。

速率配额

治理

Bot + 企业双层配额;超限返回 429 并带 `Retry-After`、`X-BotRateLimit-Remaining`。

HTTP API 参考

分两组鉴权:开发者端使用账号 JWT 管理自己申请的机器人;调用 Bot 业务接口使用机器人 Bot Token (Authorization: Bearer sbot_<token>)。所有路径以 /api/v1/bots/** 为前缀。

开发者端点(JWT)

以账号登录态调用,用于申请机器人、查看列表、读取一次性 Token、登记或删除 Webhook。

POST/api/v1/bots/applicationsJWT

提交机器人申请

登录用户提交创建申请,等待 Admin 审核。

  • ·必填:`name` (<=100)
  • ·可选:`username` (<=64,全局唯一)、`description`、`avatar`、`enterprise_id`、`scopes[]`
  • ·默认 `scopes = ["messages:send", "messages:receive"]`
  • ·返回:机器人对象(`review_status=pending`)
GET/api/v1/bots/myJWT

我的机器人列表

列出当前开发者提交的所有机器人,含状态与 Token 前缀。

  • ·返回:`items[]`,每项含 `review_status`、`status`、`has_token`、`token_prefix`
GET/api/v1/bots/my/:idJWT

机器人详情(含一次性 Token)

审核通过后首次读取会返回 `one_time_token`,读后即销毁(7 天有效)。

  • ·返回:机器人基础信息、`webhook`(脱敏)、`one_time_token?`
  • ·`one_time_token` 仅首次访问返回,请妥善保存
POST/api/v1/bots/my/:id/webhookJWT

登记 Webhook(开发者)

使用账号 JWT 为自己的机器人配置 Webhook,无需使用 Bot Token。

  • ·必填:`url` (必须以 `https://` 开头)
  • ·可选:`secret_token`、`allowed_updates[]`、`allowed_ips[]`
  • ·返回:`webhook`(脱敏,去除 `secret_token`)
DELETE/api/v1/bots/my/:id/webhookJWT

删除 Webhook(开发者)

移除当前机器人的 Webhook 配置,停止接收 Update。

  • ·返回:`bot_id`
GET/api/v1/bots/my/:id/deliveriesJWT

查看 Webhook 投递日志

分页查看机器人最近的 Webhook 投递记录,辅助排查失败或死信。

  • ·可选查询:`page`、`pageSize` (默认 20,最大 100)、`status` (`pending` / `delivering` / `success` / `failed` / `dead_letter`)
  • ·返回:`items[]`(含 `update_id`、`status`、`attempts`、`last_error`、`dead_letter_at`)、`total`、`page`、`pageSize`

Bot 业务端点(Bot Token)

使用机器人自己的 Token 调用。请求头 Authorization: Bearer sbot_<token>

GET/api/v1/bots/meBot Token

获取当前机器人信息

基于 Bot Token 解析并返回机器人基础信息。

  • ·返回:`id`、`name`、`username`、`review_status`、`status`、`scopes`、`system_user_id`
POST/api/v1/bots/sendMessageBot Token

发送文本消息

向指定会话发送文本消息,对外以机器人系统账号身份发送。

  • ·必填:`chat_id` (会话 ID)、`text` (文本内容)
  • ·返回:下游 message 服务返回的消息对象(含 `message_id`)
  • ·多媒体请使用 `sendPhoto / sendDocument / sendVideo / sendAudio`
POST/api/v1/bots/sendPhotoBot Token

发送图片消息

以图片形式发送已上传的文件,`file_id` 来源于 `/files/complete`。

  • ·必填:`chat_id`、`file_id`
  • ·可选:`caption` (图片说明文字)、`width` / `height` (像素,数值,>0 时写入 `metadata.width / height`)
  • ·`file_id` 对应 File 文档若已写入 `metadata.dimensions`,平台会自动补齐
  • ·返回:消息对象(`message_type=image`)
POST/api/v1/bots/sendDocumentBot Token

发送文件消息

以文档 / 附件形式发送已上传的文件。

  • ·必填:`chat_id`、`file_id`
  • ·可选:`caption`、`thumbnail_url` (文件缩略图 URL,写入 `metadata.thumbnailUrl`)
  • ·返回:消息对象(`message_type=file`)
POST/api/v1/bots/sendVideoBot Token

发送视频消息

以视频形式发送已上传的文件。

  • ·必填:`chat_id`、`file_id`
  • ·可选:`caption`、`duration` (秒)、`width` / `height` (像素)、`thumbnail_url`
  • ·所有可选字段 `>0` / 非空时才写入 `metadata.*`;未提供时客户端会在播放时自动读取元数据
  • ·返回:消息对象(`message_type=video`)
POST/api/v1/bots/sendAudioBot Token

发送语音消息

以音频 / 语音形式发送已上传的文件。

  • ·必填:`chat_id`、`file_id`
  • ·可选:`caption`、`duration` (秒,语音气泡显示用)、`performer` (表演者 / 上传者)、`title` (曲目 / 标题)
  • ·`performer` / `title` 主要面向音乐 / 播客场景,进入 `metadata` 与 Webhook Update `message.file`
  • ·返回:消息对象(`message_type=audio`)
POST/api/v1/bots/sendLocationBot Token

发送位置消息

发送一个包含经纬度和可选地名的位置消息。

  • ·必填:`chat_id`、`latitude`、`longitude`
  • ·可选:`name` (地点名)、`address` (结构化地址)
  • ·返回:消息对象(`message_type=location`)
POST/api/v1/bots/editMessageBot Token

编辑机器人自发消息

仅支持编辑由本机器人(同一 `system_user_id`)发送的文本消息;触发 `edited_message` Update。

  • ·必填:`message_id` (或 `messageId`)、`text` (或 `content`)
  • ·仅限本机器人发送的消息;其他消息返回 403
  • ·编辑成功后消息 `metadata.isEdited=true`,客户端会显示“已编辑”标记
POST/api/v1/bots/deleteMessageBot Token

删除(撤回)机器人自发消息

仅支持删除由本机器人发送的消息;触发 `message_deleted` Update。

  • ·必填:`message_id` (或 `messageId`)
  • ·仅限本机器人发送的消息;其他消息返回 403
  • ·删除后下游会广播 `message.deleted`,客户端从历史中移除
POST/api/v1/bots/files/upload-credentialsBot Token

申请 S3 直传凭证

为即将上传的多媒体文件获取 S3 预签名凭证;多媒体消息第一步。

  • ·必填:`fileName`、`fileSize` (字节)
  • ·可选:`fileType` (MIME)、`checksum`、`metadata`
  • ·返回:`uploadUrl`、`key`、`headers`、`expiresAt` 等直传参数
POST/api/v1/bots/files/completeBot Token

S3 直传完成登记

客户端完成 PUT 上传后回调登记,获取后续 `sendPhoto / sendDocument` 需要的 `file_id`。

  • ·必填:`key`、`fileName`、`fileSize`
  • ·可选:`fileType`、`checksum`、`category` (默认 `bot`)、`isPublic`
  • ·返回:`file_id`、`url`、`thumbnail_url?` 等文件对象
POST/api/v1/bots/setWebhookBot Token

配置 Webhook

使用 Bot Token 直接配置 Webhook;与开发者 JWT 接口二选一。

  • ·必填:`url` (必须 HTTPS)
  • ·可选:`secret_token`、`allowed_updates[]` (默认 `["message"]`;可含 `edited_message` / `message_deleted`)、`allowed_ips[]`
  • ·返回:`webhook`(脱敏)
POST/api/v1/bots/deleteWebhookBot Token

删除 Webhook

移除当前 Webhook 配置,停止接收 Update。

  • ·返回:`bot_id`
POST/api/v1/bots/verify-token无(请求体带 token)

校验 Bot Token

调试用:使用请求体中的 `token` 参数校验有效性(公开端点)。

  • ·必填:`token`
  • ·返回:`bot` 对象或错误

请求示例:sendMessage

文本消息;内部映射为 SoChat 会话消息。

cURL
curl -X POST 'https://www.sochatlive.com/api/v1/bots/sendMessage' \
  -H 'Authorization: Bearer sbot_<token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "chat_id": "6530ab...9c1",
    "text": "Hello from SoChat bot"
  }'

请求示例:sendPhoto(多媒体 4 步法)

sendDocument / sendVideo / sendAudio 同构,仅接口路径不同。

cURL
# Step 1: 申请 S3 直传凭证
curl -X POST 'https://www.sochatlive.com/api/v1/bots/files/upload-credentials' \
  -H 'Authorization: Bearer sbot_<token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "fileName": "screenshot.png",
    "fileSize": 204800,
    "fileType": "image/png"
  }'
# => { "uploadUrl": "...", "key": "bots/xxx.png", "headers": {...} }

# Step 2: 使用返回的 uploadUrl / headers,客户端直传 S3
curl -X PUT "<uploadUrl>" -H 'Content-Type: image/png' --data-binary @screenshot.png

# Step 3: 登记上传结果,拿到 file_id
curl -X POST 'https://www.sochatlive.com/api/v1/bots/files/complete' \
  -H 'Authorization: Bearer sbot_<token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "key": "bots/xxx.png",
    "fileName": "screenshot.png",
    "fileSize": 204800,
    "fileType": "image/png"
  }'
# => { "file_id": "6530ab...f2", "url": "...", ... }

# Step 4: 发送图片消息(width / height 可选;不传时由 File 文档元数据自动补齐)
curl -X POST 'https://www.sochatlive.com/api/v1/bots/sendPhoto' \
  -H 'Authorization: Bearer sbot_<token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "chat_id": "6530ab...9c1",
    "file_id": "6530ab...f2",
    "caption": "部署成功截图",
    "width": 1920,
    "height": 1080
  }'

请求示例:editMessage / deleteMessage

仅限编辑 / 删除本机器人发送的消息,触发 edited_message / message_deleted Update。

cURL
# 编辑本机器人发送过的文本消息
curl -X POST 'https://www.sochatlive.com/api/v1/bots/editMessage' \
  -H 'Authorization: Bearer sbot_<token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "message_id": "6530ab12c9a0ff00123abc88",
    "text": "部署状态已更新:成功"
  }'

# 撤回一条自己发送的消息
curl -X POST 'https://www.sochatlive.com/api/v1/bots/deleteMessage' \
  -H 'Authorization: Bearer sbot_<token>' \
  -H 'Content-Type: application/json' \
  -d '{ "message_id": "6530ab12c9a0ff00123abc88" }'

请求示例:查看 Webhook 投递日志

使用账号 JWT 调用;用于排查投递失败与 dead_letter

cURL
# 开发者自助查看 Webhook 投递日志(需账号 JWT)
curl -G 'https://www.sochatlive.com/api/v1/bots/my/<bot_id>/deliveries' \
  -H 'Authorization: Bearer <jwt>' \
  --data-urlencode 'page=1' \
  --data-urlencode 'pageSize=20' \
  --data-urlencode 'status=dead_letter'
# => { "items": [ { "update_id": "...", "status": "dead_letter", "attempts": 5, "dead_letter_at": "...", "last_error": "..." } ], "total": 3 }

请求示例:setWebhook

建议填写 secret_token 便于验签;如果需要接收编辑 / 删除事件,请在 allowed_updates 显式声明。

cURL
curl -X POST 'https://www.sochatlive.com/api/v1/bots/setWebhook' \
  -H 'Authorization: Bearer sbot_<token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "url": "https://example.com/webhook/sochat",
    "secret_token": "replace-with-your-secret",
    "allowed_updates": ["message", "edited_message", "message_deleted"],
    "allowed_ips": []
  }'

Webhook 与 Update

Update 由平台异步投递,不阻塞消息主链路。当前支持 message(新消息)、edited_message(消息被编辑)、message_deleted(消息被撤回)三种类型;群聊默认 Privacy Mode 开启,仅推送命令、@机器人、回复机器人与服务类事件等。

  • 异步出站消息落库后由 `BotUpdateDispatcher` 匹配可见机器人,生成 `bot_update_delivery` 并投 Redis Stream `bot:webhook:delivery`。
  • Update 类型`message` / `edited_message` / `message_deleted`;消息内部根据载体区分 `text / photo / document / video / audio / location`。
  • 群 Privacy Mode群聊仅投递:`/` 开头命令、文本含 `@username`、回复机器人消息、mentions 命中机器人 userId/username;私聊全部投递。
  • 稳定幂等键平台重试保持同一 `update_id` 与 `X-SoChat-Update-Id`;客户侧以此去重。
  • 重试策略失败按固定节奏 1min / 5min / 15min / 1h 重试;首次 + 4 次重试共 5 次尝试,仍失败则落为 `dead_letter`(终态)。
  • 签名摘要HMAC-SHA256(body, secret_token);若 `secret_token` 未配置,将投递失败(必须配置)。
  • 租户上下文每条 Update 含 `enterprise_id`,便于隔离和审计。

请求头

平台 → 客户 Webhook 的固定请求头。

名称示例值说明
Content-Typeapplication/json始终 UTF-8 JSON 编码。
X-SoChat-Signaturesha256=<hex>HMAC-SHA256(raw_body, secret_token);请使用原始请求字节校验。
X-SoChat-Update-Id<uuid>与 payload.update_id 相同;作为幂等键。

平台要求客户端在 15 秒 内返回 2xx;否则视为失败并按固定节奏(1min / 5min / 15min / 1h)重试,首次加 4 次共 5 次尝试,仍失败则落为 dead_letter,Admin 可在控制台手动重投。

Privacy Mode 规则

群聊默认仅投递以下情形;私聊不做过滤。

场景是否投递说明
私聊全部投递机器人在私聊里可收到用户发送的全部消息。
群聊:`/` 命令投递如 `/deploy`、`/help`;空格前的 token 要以 `/` 开头。
群聊:`@username`投递文本中出现 `@<bot_username>`(大小写不敏感)。
群聊:回复机器人消息投递Quote / Reply 到机器人发送的消息上。
群聊:mentions 命中投递mentions 列表含机器人 `userId` 或 `username`。
群聊:其它普通消息不投递默认不会把群内普通聊天推给机器人。

Update 载荷示例(JSON)

字段风格贴近 Telegram,便于对照迁移;下面顺序展示文本、图片、消息编辑与撤回四种类型。

JSON
// 1) 文本消息(type=message)
{
  "update_id": "3fb4e65c-4d6b-4b0d-9d9a-3a1b9c4f0e12",
  "type": "message",
  "bot_id": "6530ab12c9a0ff00123abc01",
  "enterprise_id": null,
  "message": {
    "message_id": "6530ab12c9a0ff00123abc88",
    "from": { "id": "6530ab12c9a0ff00123ab801", "username": "alice", "is_bot": false },
    "chat": { "id": "6530ab12c9a0ff00123abc55", "type": "group", "title": "研发协作群" },
    "text": "/deploy status",
    "date": 1735689600
  }
}

// 2) 图片消息(type=photo;文件元数据在 message.file;width / height 等元数据若已知会一并带出)
{
  "update_id": "5ac9d1d4-3f9e-4a17-8dd6-7b0a1a3a8a01",
  "type": "photo",
  "bot_id": "6530ab12c9a0ff00123abc01",
  "enterprise_id": null,
  "message": {
    "message_id": "6530ab12c9a0ff00123abc91",
    "from": { "id": "6530ab12c9a0ff00123ab801", "username": "alice", "is_bot": false },
    "chat": { "id": "6530ab12c9a0ff00123abc55", "type": "group", "title": "研发协作群" },
    "text": "看下这张截图 @bot",
    "file": {
      "file_id": "6530ab12c9a0ff00123abcf2",
      "file_name": "screenshot.png",
      "mime_type": "image/png",
      "file_size": 204800,
      "url": "https://s3.example.com/bots/6530.../screenshot.png",
      "thumbnail_url": "https://s3.example.com/bots/6530.../screenshot_thumb.png",
      "width": 1920,
      "height": 1080
    },
    "date": 1735692000
  }
}

// 3) 消息编辑(type=edited_message,由 editMessage 触发)
{
  "update_id": "7c33b8f2-7d24-4f4b-8a2c-1f4b2e0e10bd",
  "type": "edited_message",
  "bot_id": "6530ab12c9a0ff00123abc01",
  "enterprise_id": null,
  "message": {
    "message_id": "6530ab12c9a0ff00123abc88",
    "chat": { "id": "6530ab12c9a0ff00123abc55" },
    "text": "/deploy status — 已更新",
    "date": 1735693500
  }
}

// 4) 消息撤回(type=message_deleted,由 deleteMessage 触发)
{
  "update_id": "91dd6d01-3c52-4c98-907e-6b8e8a01dd3a",
  "type": "message_deleted",
  "bot_id": "6530ab12c9a0ff00123abc01",
  "enterprise_id": null,
  "message": {
    "message_id": "6530ab12c9a0ff00123abc88",
    "chat": { "id": "6530ab12c9a0ff00123abc55" },
    "date": 1735695000
  }
}

官方 SDK

SDK 把 Bot API、HMAC 验签、update_id 去重、命令路由打包成统一接口,几行代码即可完成消息收发与 Webhook 接入。 目前提供 Node.js / TypeScriptJava 两种语言。

Node.js · TypeScript已发布
Node ≥ 18

@sochatlive/bot-sdk

ESM + CJS 双格式 / 自带类型声明 · 内置 Express/原生 HTTP 中间件 · fetch 原生实现,无运行时依赖。

Java即将发布
JDK ≥ 17

com.sochat:bot-sdk

JDK 自带 HttpClient + jdk.httpserver · 仅依赖 Jackson Databind · 兼容 Spring Boot / Servlet / Micronaut。

Maven Central(待发布)·README·示例

两套 SDK 行为完全一致:Authorization: Bearer <bot_token> 调用 Bot API、X-SoChat-Signature HMAC-SHA256 验签、update_id LRU 去重、平台重试节奏 1m/5m/15m/1h。其他语言(Python、Go)规划中。

1. 安装

Node 18+;ESM/CJS 双格式发布。

bash
# 任选包管理器
npm install @sochatlive/bot-sdk
pnpm add @sochatlive/bot-sdk
yarn add @sochatlive/bot-sdk

# 或在 monorepo 中开发:仓库根
pnpm install
pnpm --filter @sochatlive/bot-sdk run build

2. 快速开始:Echo Bot

高阶 SoChatBot 内置 HTTP 服务、验签、命令路由与去重,几行代码上线。

TypeScript
import { SoChatBot } from '@sochatlive/bot-sdk'

const bot = new SoChatBot({
  token: process.env.SOCHAT_BOT_TOKEN!,
  secretToken: process.env.SOCHAT_WEBHOOK_SECRET!,
  baseUrl: process.env.SOCHAT_API_BASE, // 例: https://gateway.example.com/api/v1
})

// 命令路由:当用户发送 /start 时回复
bot.command('start', (ctx) => ctx.reply('你好,我是 SoChat 机器人 👋'))

// 通用消息:回声
bot.on('message', async (ctx) => {
  if (ctx.message.text) {
    await ctx.reply('你说: ' + ctx.message.text)
  }
})

// 启动内置 HTTP 服务(自动验签 / 去重 / 路由分发)
await bot.start({ port: 8787, path: '/webhook' })
console.log('Bot is up on :8787/webhook')

将公网 HTTPS 反代到 http://<host>:8787/webhook,在开发者控制台把 Webhook URL 设为反代后的 https://你的域名/webhook

3. 仅调用 API:低阶客户端

不接 Webhook、只主动推送时使用 SoChatBotClient

TypeScript
import { SoChatBotClient } from '@sochatlive/bot-sdk'

const client = new SoChatBotClient({
  token: process.env.SOCHAT_BOT_TOKEN!,
  baseUrl: process.env.SOCHAT_API_BASE,
})

// 配置 Webhook
await client.setWebhook({
  url: 'https://example.com/webhook',
  secret_token: 'your-secret',
  allowed_updates: ['message'],
})

// 主动推送
const me = await client.getMe()
await client.sendMessage({
  chat_id: '<conversationId>',
  text: '系统通知:今晚 22:00 例行维护',
})

// 编辑、撤回(仅本机器人发送的消息)
const sent = await client.sendMessage({ chat_id, text: '加载中…' })
await client.editMessage({ chat_id, message_id: sent.message_id, text: '已完成 ✅' })
await client.deleteMessage({ chat_id, message_id: sent.message_id })

4. 与 Express 集成

  • bot.webhookCallback() 返回 Express 兼容中间件,自动验签、去重、分发事件。
  • 务必使用 express.raw({ type: 'application/json' }),否则原始字节被丢弃,签名无法对齐。
TypeScript
import express from 'express'
import { SoChatBot } from '@sochatlive/bot-sdk'

const app = express()

const bot = new SoChatBot({
  token: process.env.SOCHAT_BOT_TOKEN!,
  secretToken: process.env.SOCHAT_WEBHOOK_SECRET!,
})

bot.on('message', async (ctx) => {
  if (ctx.message.text === 'ping') await ctx.reply('pong')
})

// 关键:用 express.raw 保留原始字节用于 HMAC 验签
app.post(
  '/webhook',
  express.raw({ type: 'application/json' }),
  bot.webhookCallback(),
)

// 其他路由可以照常使用 express.json()
app.use(express.json())
app.get('/health', (_req, res) => res.json({ ok: true }))

app.listen(8787)

5. 多媒体一步法

SDK 自动完成 申请凭证 → S3 直传 → complete 登记 → sendPhoto/sendDocument,开发者只需提供本地路径或 Buffer。

TypeScript
import { SoChatBotClient, sendPhotoFromBuffer, sendPhotoFromUrl } from '@sochatlive/bot-sdk'
import { readFile } from 'node:fs/promises'

const client = new SoChatBotClient({
  token: process.env.SOCHAT_BOT_TOKEN!,
  baseUrl: process.env.SOCHAT_API_BASE,
})

// 1) 本地路径(最常见)
await client.sendPhotoFromFile(chatId, './chart.png', { caption: '今日 PV 趋势' })

// 2) 内存 Buffer
const buf = await readFile('./chart.png')
await sendPhotoFromBuffer(client, chatId, buf, {
  fileName: 'chart.png',
  fileType: 'image/png',
})

// 3) 远端 URL(SDK 帮你下载 → 上传 → 发送)
await sendPhotoFromUrl(client, chatId, 'https://cdn.example.com/p.png', {
  fileName: 'p.png',
})

6. 错误处理

API 错误统一抛 SoChatApiError;签名错误抛 SoChatSignatureError,对应返回 401

TypeScript
import { SoChatApiError, SoChatSignatureError } from '@sochatlive/bot-sdk'

try {
  await client.sendMessage({ chat_id, text: 'hello' })
} catch (err) {
  if (err instanceof SoChatApiError) {
    // err.statusCode / err.code / err.message
    if (err.statusCode === 429) {
      // 触发配额,按 Retry-After 延后再试
    }
    if (err.code === 'invalid_token') {
      // Token 失效:刷新 / 重新申请
    }
  }
  throw err
}

// Webhook 中:自定义中间件捕获签名错误
try {
  await bot.processWebhook(rawBody, signatureHeader, updateIdHeader)
} catch (err) {
  if (err instanceof SoChatSignatureError) {
    return res.status(401).end()
  }
  throw err
}

主要方法对照

两套 SDK 命名风格保持一致;返回值字段以 Bot API 为准。

能力Node.jsJavaBot API 端点
获取自身 / 校验 Tokenclient.getMe() · client.verifyToken()client.getMe() · client.verifyToken()GET /bots/me
发送文本client.sendMessage({chat_id,text})client.sendMessage(chatId, text)POST /bots/sendMessage
发送图片 / 文件 / 视频 / 语音client.sendPhoto / sendDocument / sendVideo / sendAudioclient.sendPhoto / sendDocument / sendVideo / sendAudioPOST /bots/send{Photo|Document|...}
发送位置client.sendLocationclient.sendLocationPOST /bots/sendLocation
编辑 / 撤回client.editMessage · client.deleteMessageclient.editMessage · client.deleteMessagePOST /bots/{editMessage|deleteMessage}
Webhook 配置client.setWebhook · client.deleteWebhookclient.setWebhook · client.deleteWebhookPOST /bots/{setWebhook|deleteWebhook}
投递日志 & 重投client.getWebhookDeliveries · client.retryDeliveryclient.getWebhookDeliveries · client.retryDeliveryGET /bots/my/:id/webhook-deliveries
S3 上传两步client.issueUploadCredentials · client.completeUploadclient.issueUploadCredentials · client.completeUploadPOST /bots/{issueUploadCredentials|completeUpload}
本地一步上传并发送client.sendPhotoFromFile · sendPhotoFromBuffer · sendPhotoFromUrlclient.sendPhotoFromFile · sendDocumentFromFile · uploadFile内部组合调用
高阶事件路由new SoChatBot(...) · bot.on() · bot.command() · bot.start()SoChatBot.builder() · bot.on() · bot.command() · bot.start()内置 Webhook server

完整 API 列表与高级用法(自定义 DedupeStoreallowed_updates 过滤、getWebhookDeliveries 重投等)见两个 SDK 的 README 与 examples/ 目录。

会话与字段映射

Bot API 与 Update 标识符说明。首期 chat_id 即 SoChat 会话 ID。

字段含义说明
enterprise_id企业 / 租户标识机器人与 Update 均绑定租户;个人场景可为 `null`。
bot_id机器人实例 IDMongoDB ObjectId;与系统账号 `system_user_id` 一对一。
chat_id会话标识对应 SoChat `conversationId` (ObjectId),私聊、群聊同一口径。
message_id平台消息 ID站内消息 ObjectId;`sendMessage` / `editMessage` / `deleteMessage` 均使用该字段。
update_idUpdate 唯一 IDUUID v4;与 `X-SoChat-Update-Id` 相同,重试保持不变。
system_user_id机器人系统账号审核通过后由平台自动创建,机器人在会话中以该账号身份发言。
file_id机器人文件 ID由 `/files/complete` 返回,`sendPhoto / sendDocument / sendVideo / sendAudio` 均使用该字段。
file.width / height图片 / 视频尺寸像素;`sendPhoto` / `sendVideo` 可选,或由 File 文档 `metadata.dimensions` 自动补齐。
file.duration音视频时长秒;`sendAudio` / `sendVideo` 可选,或由 File 文档 `metadata.duration` 自动补齐。
file.performer / title音乐元信息`sendAudio` 可选;适合音乐 / 播客;Webhook Update 载荷同步带出。
rate_limitsBot 级速率配额`send_per_sec / send_per_min / send_per_day`;未设置则回退到企业 / 全局默认值。
metadata.isEdited编辑标记`editMessage` 成功后会写入,客户端据此显示“已编辑”标签。
dead_letter_at投递死信时间超过 5 次尝试后落为 `dead_letter`,该字段记录时间点;Admin 可手动重试。

内部序列号、附件等由 Bot 服务适配;对外 DTO 稳定,变更见 OpenAPI 说明。

错误码

业务错误码;HTTP 状态以网关与正式 OpenAPI 为准。

code典型 HTTP说明处理建议
BOT_TOKEN_REQUIRED401请求缺少 `Authorization: Bearer <token>`检查请求头或请求体 `token` 字段。
INVALID_BOT_TOKEN401Token 不存在、已被吊销或与 Bot 不匹配确认 Token 未轮换 / 吊销;使用最新一次签发的明文值。
BOT_NOT_AVAILABLE403机器人未审核通过或已停用 (`review_status != approved` 或 `status != enabled`)到机器人详情查看审核状态;被停用请联系平台管理员。
VALIDATION400参数不合法,如 `chat_id` / `text` 缺失、Webhook URL 非 HTTPS、`file_id` 缺失等按接口字段要求补齐;URL 必须以 `https://` 开头。
FORBIDDEN403仅限编辑 / 删除本机器人(同一 `system_user_id`)发送的消息确认 `message_id` 属于当前机器人发送的消息。
RATE_LIMITED429触发网关通用路由级速率限制按 `Retry-After` 退避重试;具体配额以网关路由为准。
BOT_RATE_LIMIT429触发 Bot / 企业级双层配额(`send_per_sec / min / day`)读取响应头 `Retry-After`、`X-BotRateLimit-Remaining`;必要时联系 Admin 调整配额。
WEBHOOK_DELIVER_FAILED投递日志中的终态;首次 + 4 次重试(1min / 5min / 15min / 1h)仍失败后落为 `dead_letter`检查客户服务 2xx 返回、超时(15s)与 HMAC 验签实现;Admin 可手动触发重投。

与 Telegram Bot 的差异

心智接近 TG,下列为企业场景下的有意差异。

维度Telegram(BotFather)SoChat 开放平台
谁能申请用户通过 BotFather 创建 Bot平台用户提交创建申请,可绑定企业 / 租户上下文
何时获得 Token创建后立即获得 bot_token平台 Admin 审核通过后方可首次生成并展示 Token
审核与责任无单独人工审核流程Admin 在「机器人管理」中审核,操作入审计日志
租户与合规无企业租户语义`enterprise_id` 贯穿 Bot 与 Update,便于私有化与追溯
当前能力完整 Bot API 与生态(含按钮回调、富交互)P0 + P1:文本 / 多媒体消息、编辑撤回、Webhook 死信、双层配额与监控;按钮回调等见 P2 路线图
速率配额平台统一限流,单侧不可配置Bot 级 + 企业级双层配额,支持 Admin 热更新,超限返回 `BOT_RATE_LIMIT` + `Retry-After`
监控与排障无官方监控面板近 24h 投递监控看板、开发者可自助查看 Webhook 投递日志,失败 / dead_letter 可手动重投

示例代码(Node.js)

签名头、算法与正文编码以正式发布 OpenAPI 为准。

HMAC 验签

使用原始 body 字节;timingSafeEqual 防时序攻击。

JavaScript
import crypto from 'node:crypto'

/**
 * @param rawBody - 与平台计算签名时一致的原始字节
 * @param signatureHeader - 如 X-SoChat-Signature: sha256=...
 * @param secret - setWebhook 时配置的 secret_token
 */
export function verifyWebhookSignature(rawBody, signatureHeader, secret) {
  if (!signatureHeader || !secret) return false
  const body = typeof rawBody === 'string' ? Buffer.from(rawBody) : rawBody
  const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(body).digest('hex')
  const a = Buffer.from(expected)
  const b = Buffer.from(String(signatureHeader).trim())
  return a.length === b.length && crypto.timingSafeEqual(a, b)
}

幂等去重

  • update_id 作幂等键;平台重试时不变。
  • Webhook 尽快返回 2xx;重活放异步。
JavaScript
const seen = new Set() // 生产环境请换 Redis / DB

function isDuplicateUpdate(updateId) {
  if (seen.has(updateId)) return true
  seen.add(updateId)
  return false
}

Echo Bot(Express)

验签后解析 Update,回发 sendMessage

JavaScript
// POST /webhook — 验签通过后解析 Update,Echo 文本消息
app.post('/webhook/sochat', express.raw({ type: '*/*' }), async (req, res) => {
  const raw = req.body
  const sig = req.get('X-SoChat-Signature') || ''
  if (!verifyWebhookSignature(raw, sig, process.env.WEBHOOK_SECRET)) {
    return res.status(401).end()
  }
  const update = JSON.parse(raw.toString('utf8'))
  if (isDuplicateUpdate(update.update_id)) {
    return res.status(200).json({ ok: true })
  }
  const text = update.message?.text
  const chatId = update.message?.chat?.id
  if (update.type === 'message' && text && chatId) {
    await fetch('https://www.sochatlive.com/api/v1/bots/sendMessage', {
      method: 'POST',
      headers: {
        Authorization: 'Bearer ' + process.env.BOT_TOKEN,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ chat_id: chatId, text }),
    })
  }
  res.status(200).json({ ok: true })
})

安全与合规

Bot Token 与用户 JWT 分离;Admin 审核;日志与投递脱敏、可审计。

鉴权隔离

  • Bot Token 不复用用户 JWT 中间件
  • 网关与 Bot 服务负责 Token 验证
  • 下游只接收 `bot_id` 与企业上下文

Webhook 安全

  • 默认要求 HTTPS
  • 支持 HMAC 签名头
  • 可扩展 IP 白名单与时间窗校验
  • 首次 + 4 次重试后落 `dead_letter`

Token 治理

  • 审核通过前不生成 Token
  • 首次明文仅展示一次
  • 轮换后旧 Token 立即失效

速率配额

  • Bot 级 + 企业级两层配额
  • Redis 分秒 / 分钟 / 日桶
  • 超限返回 `BOT_RATE_LIMIT` + `Retry-After`
  • Admin 可热更新阈值

审计与脱敏

  • 审核、吊销、停用、配额变更写入审计日志
  • 限流触发节流后写入 `BOT.RATELIMIT_HIT`
  • 日志只输出 Token 前缀与域名
  • 异常调用与失败投递可告警

可观测性

  • bot-service 暴露 `/metrics` Prometheus 指标
  • 监控 API 调用量、投递成功率、队列积压、限流命中
  • 近 24h 监控看板,Admin 可快速排障

Admin 机器人管理

审核与监管在 Admin 控制台;开发者 Bot API 与 /api/v1/admin/bots/** 分域。

能力模块

  • 审核工作台 — 查看待审队列、申请详情、企业归属与用途说明,执行通过或拒绝。
  • 机器人目录 — 查看全量机器人状态、关联企业、创建时间与最近 Token 活动时间。
  • 详情与监管 — 查看审核历史、Webhook 摘要、调用概览,并支持停用、启用与吊销 Token。
  • 投递日志与重投 — 分页查看每个机器人的 Webhook 投递记录,失败 / dead_letter 可手动重试。
  • 配额管理 — 为单个机器人或企业租户设置每秒 / 每分 / 每日发送配额,支持热更新。
  • 监控看板 — 近 24 小时投递总量、成功率、状态分布与队列积压概览(Prometheus 指标从 bot-service `/metrics` 采集)。
  • 运营审计 — 记录审核、吊销、配额更新、限流触发等动作,满足企业级合规追溯要求。

路线图

  • P0 · 已上线最小可上线闭环— 完成申请、审核、Token、Webhook、Update 与 `sendMessage` 的最小商用链路。
  • P1 · 已上线消息能力增强— 多媒体消息、`editMessage` / `deleteMessage`、Bot + 企业双层速率配额、投递死信与监控看板。
  • P2 · 规划中交互能力开放— 补充按钮回调、富消息协议与客户端渲染能力。
  • P3 · 规划中生态能力完善— 探索 `getUpdates`、SDK、沙箱与更完整的开放平台生态。

运营侧 HTTP 接口

以下路径需使用 Admin 控制台会话(JWT)及对应 RBAC 权限,与开发者 /api/v1/bots/** 分域;具体权限点以种子与控制台为准。

方法路径说明权限点
GET/api/v1/admin/bots机器人列表;筛选 `review_status` / `status` / `enterprise_id` / `search` + 分页admin:bot:list
GET/api/v1/admin/bots/:id机器人详情:基础信息、审核历史、Webhook 摘要、`token_prefix`admin:bot:read
GET/api/v1/admin/bots/:id/deliveriesWebhook 投递日志(分页),支持按 `status` 过滤admin:bot:read
POST/api/v1/admin/bots/:id/deliveries/:deliveryId/retry重试指定投递;入队 `bot:webhook:delivery`admin:bot:update
POST/api/v1/admin/bots/:id/approve审核通过;签发 Bot Token(首次明文返回)admin:bot:review
POST/api/v1/admin/bots/:id/reject审核拒绝,记录 `review_note`admin:bot:review
POST/api/v1/admin/bots/:id/revoke-token吊销当前 Token,旧 Token 立即失效admin:bot:revoke
POST/api/v1/admin/bots/:id/toggle-status切换 `status` 为 `enabled` / `disabled`admin:bot:update
GET/api/v1/admin/bots/metrics/summary机器人监控汇总(近 24h 投递总量、成功率、队列积压、状态分布)admin:bot:metrics
GET/api/v1/admin/bots/:id/metrics单个机器人最近 24h 投递与 API 调用指标admin:bot:metrics
GET/api/v1/admin/bots/:id/quota获取机器人当前配额(Bot 级 + 企业级兜底)admin:bot:quota
PUT/api/v1/admin/bots/:id/quota更新机器人 Bot 级配额(`send_per_sec / min / day`)admin:bot:quota
GET/api/v1/admin/bots/quota/enterprises企业机器人配额列表;分页、关键字搜索admin:bot:quota
PUT/api/v1/admin/bots/quota/enterprises更新企业租户的 Bot 配额兜底值admin:bot:quota

常见问题

为什么不是申请后立即拿到 Token?
SoChat 面向企业与私有化场景,需要平台 Admin 在准入、责任归属和滥用风险上做统一审核,因此 Token 只有在审核通过后才会签发。
Webhook 为什么必须异步投递?
Webhook 属于出站 HTTP 调用,网络波动、超时与客户侧限流都很常见。异步队列可以避免阻塞消息主链路,并支持重试、死信和并发控制。
群里所有消息都会推给机器人吗?
不会。默认群隐私模式开启,只推送命令、@机器人、回复机器人消息以及服务事件(如 edited_message / message_deleted),避免机器人获取不必要的群聊内容。
现在开放平台能做哪些典型场景?
P0 + P1 已覆盖:审批 / 监控告警(文本 + 图片 + 文件)、智能助手(支持编辑与撤回自己的回答)、位置打卡、知识库问答等。富交互按钮、getUpdates 与 SDK 在 P2 / P3 路线图。
如何发送图片、文件等多媒体?
分两步:先调用 POST /api/v1/bots/files/upload-credentials 获取 S3 直传凭证,客户端直接 PUT 到 S3;上传完成后调用 POST /api/v1/bots/files/complete 登记,拿到 file_id,再调用 sendPhoto / sendDocument / sendVideo / sendAudio,对应参数填 file_id 即可。
editMessage / deleteMessage 有什么限制?
只能编辑或删除「由本机器人发送(同 system_user_id)」的消息,其他消息返回 403 FORBIDDEN;editMessage 目前仅支持文本替换。编辑 / 删除成功后会对所有订阅了 edited_message / message_deleted 的 Webhook 触发 Update。
收到 429 / BOT_RATE_LIMIT 怎么办?
平台有两层配额:Bot 级(可选)和企业级兜底。超限返回 HTTP 429,携带 Retry-After 与 X-BotRateLimit-Remaining 头,请按 Retry-After 退避。如果业务长期超限,请联系平台 Admin 在「机器人详情 / 配额」或「企业配额列表」中调整阈值。
Webhook 签名怎么算的?
平台以 setWebhook 时的 secret_token 为密钥,对即将发送的 JSON 请求体做 HMAC-SHA256,十六进制值写入 X-SoChat-Signature: sha256=<hex>。客户端请使用原始请求字节验签,避免 JSON 再序列化导致字节不一致。若未配置 secret_token,投递会失败(请在 setWebhook 中务必提供密钥)。
Webhook 失败会怎么重试?
平台要求在 15 秒内返回 2xx 才视为成功,否则按固定节奏 1min / 5min / 15min / 1h 做 4 次退避,首次加起来共 5 次尝试;仍失败则落为 dead_letter。所有重试保持同一 update_id;开发者可在本站「开发者控制台 / 投递日志」或调用 GET /api/v1/bots/my/:id/deliveries 查看详情,Admin 可在后台手动重投。
Bot Token 忘记保存了怎么办?
Bot Token 仅在审核通过后第一次进入「我的机器人 / 详情」时返回 one_time_token,读取后即从缓存销毁。如果遗失,请联系平台 Admin 在后台执行「吊销 Token」,旧 Token 立即失效;再次「审核通过」或在 Admin 侧重签后,会生成新的明文 Token。

下一步

完成申请与审核后,可用 Echo Bot 跑通 sendMessage 与 Webhook 闭环。遇到问题可通过支持渠道联系。