Skip to content

机器人(Bot)开发文档

概述

Bot(机器人)是 miniApp 内置的自动化消息账号,只能私聊(一对一)给本应用的关注用户(miniAppUser)发送消息,也能通过 Webhook 接收用户消息并做出响应。不支持群发、不支持给非本应用用户发消息。适用于:客服机器人、消息通知推送、自动回复、第三方系统集成等场景。

关注关系

用户通过 JSSDK 在本 miniApp 完成授权(jssdk/init)后,即成为该应用的关注用户(miniAppUser),此时 Bot 才能向其私聊发送消息。Bot 只能触达自己所属 miniApp 的用户,无法跨应用发送。

整体架构

text
用户/群组
    │  发消息给 Bot

平台 WebSocket 服务(Pitaya Frontend)
    │  消息路由

平台消息队列(NATS / Kafka)
    │  Webhook 回调(HTTP POST)

你的 Bot 服务器
    │  处理逻辑后回复
    ▼  POST /api/mini/bot/send
平台 HTTP API(apiGw :9050)
    │  写入 MQ

平台消息队列 → 私聊推送给目标关注用户(miniAppUser)

创建机器人

在开发者中心「机器人管理」页面创建,或通过管理 API 创建:

http
# 管理 API(需管理员 token)
POST /api/miniAdmin/bots/create
Content-Type: application/json

{
  "miniAppId": 1,
  "name": "客服机器人",
  "webhookUrl": "https://your-server.com/bot/webhook"
}

# 响应
{
  "code": 200,
  "data": {
    "id": 10,
    "botKey": "bf37f92b4da604753f1ae787f7f36465",
    "botSecret": "5c91c4f14dec40f5434bd39324a77f4f"
  }
}

Bot 基本属性

字段类型说明
botKeystring机器人公钥,用于换取 access_token(等同 AppKey)
botSecretstring机器人密钥,需保存在服务端(等同 AppSecret)
userIdint64对应 mem_user.id,IsAi=1 的特殊用户
miniAppIdint64所属 miniApp,Bot 归属于某个应用
webhookUrlstring接收用户消息回调的地址(可选)
statusint0-禁用 1-启用

安全提示

botSecret 务必保存在服务端,不要暴露在前端代码或客户端中。

鉴权:获取 access_token

所有 Bot API 都需要在请求头携带 Authorization: Bearer <access_token>。access_token 通过 botKey + botSecret 换取,有效期 7200 秒(2 小时)

POST /api/mini/bot/token

参数类型必填说明
botKeystring在机器人管理页获取
botSecretstring在机器人管理页获取,务必保存在服务端
http
POST /api/mini/bot/token
Content-Type: application/json

{
  "botKey": "bf37f92b4da604753f1ae787f7f36465",
  "botSecret": "5c91c4f14dec40f5434bd39324a77f4f"
}

响应:

json
{
  "code": 200,
  "data": {
    "access_token": "a1b2c3d4e5f6...",
    "expires_in": 7200
  }
}

TIP

建议在 token 过期前 5 分钟提前刷新,避免发送消息时报 401 错误。

发送消息

POST /api/mini/bot/send

向本应用的一个关注用户(miniAppUser)私聊发送一条消息。仅支持私聊,不支持群发。

请求参数

参数类型必填说明
openIdstring私聊目标,本应用关注用户的 miniAppUser.openId
msgTypeint消息类型,默认 0(文本),范围 0~11
msgstring消息内容

消息类型(msgType)

类型说明
0文本msg 为纯文本字符串
1图片msg 为图片 URL
2视频msg 为视频 URL
3音频msg 为音频 URL
10自定义msg 为 JSON 字符串,由客户端自行解析
11Markdownmsg 为 Markdown 文本,客户端渲染为富文本卡片

私聊示例

http
POST /api/mini/bot/send
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "openId": "3f2a9c8b7e6d5a4b3c2d1e0f9a8b7c6d",
  "msgType": 0,
  "msg": "你好,这是一条来自机器人的消息"
}

响应

json
{
  "code": 200,
  "data": { "ok": true }
}

WARNING

目标 openId 必须是本 miniApp 下、status=1 的关注用户。该 openId 由「应用用户」接口返回,或在用户 JSSDK 授权后通过 Webhook 获取。Bot 只能触达自己所属 miniApp 的用户。

获取应用用户列表

GET /api/mini/bot/userList

分页返回本 miniApp 的关注用户(miniAppUser)列表,用于获取可私聊的目标 openId

请求参数(Query)

参数类型必填说明
pageint页码,从 1 开始,默认 1
pageSizeint每页条数,默认 20,最大 200
http
GET /api/mini/bot/userList?page=1&pageSize=20
Authorization: Bearer <access_token>

# 响应
{
  "code": 200,
  "data": {
    "list": [
      {
        "openId":   "3f2a9c8b7e6d5a4b3c2d1e0f9a8b7c6d",
        "userId":   1001,
        "guid":     "550e8400-...",
        "nickname": "张三",
        "avatar":   "https://cdn.example.com/avatar.jpg",
        "faceUrl":  "",
        "online":   1
      }
    ],
    "total": 1
  }
}

响应字段

字段类型说明
openIdstring关注用户 openId,发私聊消息时使用
userIdint64系统用户 ID(mem_user.id)
guidstring用户全局唯一 ID
nicknamestring用户昵称
avatarstring用户头像 URL
faceUrlstringIM 自定义头像 URL
onlineint是否在线:1-在线 0-离线

Webhook 消息回调

在机器人管理页设置 webhookUrl 后,当用户给 Bot 发消息时,平台会向该 URL 发送一个 HTTP POST 请求,携带消息内容。

回调请求格式

http
POST https://your-server.com/bot/webhook
Content-Type: application/json

{
  "event":        "message",
  "openId":       "3f2a9c8b7e6d5a4b3c2d1e0f9a8b7c6d",
  "fromUserGuid": "550e8400-e29b-41d4-a716-446655440000",
  "fromUserName": "张三",
  "chatType":     "private",
  "msgType":      0,
  "msg":          "你好,机器人",
  "timestamp":    1717000000
}

Payload 字段

字段类型说明
eventstring事件类型,目前为 "message"
openIdstring消息发送者在本应用的 openId,回复时直接用于 /bot/send
fromUserGuidstring消息发送者 guid
fromUserNamestring消息发送者昵称
chatTypestring固定为 "private"(Bot 仅支持私聊)
msgTypeint消息类型(同发送接口)
msgstring消息内容
timestampint64消息时间戳(Unix 秒)

你的 Webhook 服务需要做

  1. 接收 POST 请求,解析 JSON body
  2. 5 秒内响应 HTTP 200(否则平台会重试)
  3. 根据 openId / msg 决定是否回复
  4. 调用 POST /api/mini/bot/send(携带 openId)进行私聊回复

演示 Webhook 接口

平台内置了演示用 Webhook,方便调试:

text
# 演示接收端(无需鉴权,仅用于调试)
POST /api/mini/webhook   ← 接收并暂存最近 100 条

GET  /api/mini/webhook   ← 查看已收到的消息列表

Webhook 接收示例(Go)

go
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
)

type WebhookMsg struct {
    Event        string `json:"event"`
    OpenId       string `json:"openId"`
    FromUserGuid string `json:"fromUserGuid"`
    FromUserName string `json:"fromUserName"`
    ChatType     string `json:"chatType"`
    MsgType      int    `json:"msgType"`
    Msg          string `json:"msg"`
    Timestamp    int64  `json:"timestamp"`
}

func webhookHandler(w http.ResponseWriter, r *http.Request) {
    var msg WebhookMsg
    json.NewDecoder(r.Body).Decode(&msg)

    fmt.Printf("收到消息 from=%s: %s\n", msg.FromUserName, msg.Msg)

    // 自动回复(私聊,使用 openId)
    if msg.Msg == "你好" {
        replyToUser(msg.OpenId, "你好!我是客服机器人,有什么可以帮助你?")
    }

    w.WriteHeader(http.StatusOK) // 必须在 5 秒内响应
}

func main() {
    http.HandleFunc("/bot/webhook", webhookHandler)
    http.ListenAndServe(":8080", nil)
}

完整示例(Go)

包含 token 缓存刷新、发送私聊消息、Webhook 自动回复:

go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
    "sync"
    "time"
)

const (
    baseURL   = "http://your-server.com"
    botKey    = "bf37f92b4da604753f1ae787f7f36465"
    botSecret = "5c91c4f14dec40f5434bd39324a77f4f"
)

var (
    tokenMu  sync.Mutex
    token    string
    tokenExp time.Time
)

// getToken 获取或刷新 access_token
func getToken() (string, error) {
    tokenMu.Lock()
    defer tokenMu.Unlock()

    if token != "" && time.Now().Before(tokenExp) {
        return token, nil
    }

    body, _ := json.Marshal(map[string]string{
        "botKey": botKey, "botSecret": botSecret,
    })
    resp, err := http.Post(baseURL+"/api/mini/bot/token",
        "application/json", bytes.NewReader(body))
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()

    var result struct {
        Code int `json:"code"`
        Data struct {
            AccessToken string `json:"access_token"`
            ExpiresIn   int64  `json:"expires_in"`
        } `json:"data"`
    }
    json.NewDecoder(resp.Body).Decode(&result)
    token = result.Data.AccessToken
    tokenExp = time.Now().Add(time.Duration(result.Data.ExpiresIn-300) * time.Second)
    return token, nil
}

// sendMsg 发送私聊消息(openId 为本应用关注用户)
func sendMsg(openId, msg string) error {
    tk, err := getToken()
    if err != nil {
        return err
    }
    body, _ := json.Marshal(map[string]interface{}{
        "openId": openId, "msgType": 0, "msg": msg,
    })
    req, _ := http.NewRequest("POST", baseURL+"/api/mini/bot/send",
        bytes.NewReader(body))
    req.Header.Set("Authorization", "Bearer "+tk)
    req.Header.Set("Content-Type", "application/json")

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return err
    }
    defer resp.Body.Close()
    fmt.Println("消息发送成功 →", openId)
    return nil
}

// webhookHandler Webhook 回调处理
func webhookHandler(w http.ResponseWriter, r *http.Request) {
    var payload struct {
        OpenId string `json:"openId"`
        Msg    string `json:"msg"`
    }
    json.NewDecoder(r.Body).Decode(&payload)

    go sendMsg(payload.OpenId, "收到你的消息:"+payload.Msg)
    w.WriteHeader(http.StatusOK)
}

func main() {
    http.HandleFunc("/bot/webhook", webhookHandler)
    fmt.Println("Bot 服务已启动 :8080")
    http.ListenAndServe(":8080", nil)
}

压测工具

平台提供压测工具 apisvc/test/minibot,用于验证 Bot 发送链路的吞吐与延迟:

bash
cd apisvc

# 基础用法
go run ./test/minibot -bot-key <botKey> -bot-secret <botSecret>

# 自定义参数
go run ./test/minibot \
  -url http://127.0.0.1:9050 \
  -bot-key bf37f92b4da604753f1ae787f7f36465 \
  -bot-secret 5c91c4f14dec40f5434bd39324a77f4f \
  -concurrency 20 \
  -total 500 \
  -msg "压测消息"

参数说明

参数默认值说明
-urlhttp://localhost:9050API 服务地址
-bot-key(必填)机器人 botKey
-bot-secret(必填)机器人 botSecret
-concurrency10并发协程数
-total200总发送消息数
-msg-type0消息类型(0=文本)
-msgload test消息内容前缀

输出示例

text
==> Authenticating bot at http://127.0.0.1:9050
==> Token: bf37f92...
==> Fetching contacts...
    contacts: 3
==> Fetching groups...
    groups: 2

==> Load test  concurrency=20  total=500
  [100/500] elapsed=1.234s qps=81.0
  [200/500] elapsed=2.456s qps=81.4
  [300/500] elapsed=3.678s qps=81.6
  [400/500] elapsed=4.891s qps=81.8
  [500/500] elapsed=6.100s qps=82.0

===== Load Test Report =====
Total:      500
Success:    498 (99.6%)
Failed:     2   (0.4%)
Elapsed:    6.100s
Throughput: 82.0 req/s

Latency (success only):
  Min:  8ms
  P50:  12ms
  P95:  28ms
  P99:  45ms
  Max:  98ms
============================

错误码

HTTPcodemessage说明
200200ok成功
200400botKey 和 botSecret 不能为空参数缺失,检查请求体
200400msg 不能为空msg 字段为空
200400openId 不能为空发送目标 openId 缺失
200400msgType 无效msgType 超出 0~11 范围
401401缺少 Authorization Bearer token请求头缺少 token
401401token 无效或已过期重新调用 /token 接口获取
200400botKey 不存在或已禁用botKey 错误或 Bot 被禁用
200400botSecret 错误botSecret 不匹配
200400目标用户不存在或未关注openId 不属于本应用,或用户未关注/已禁用
500500消息发送失败MQ 异常,检查服务端日志