跳到主要内容

DeBox 机器人 Go SDK Webhook 使用文档

源码仓库:

本文面向后端开发者,说明如何在 Go 服务中以生产可用的方式接入 DeBox Bot Webhook,包括回调鉴权、消息结构、媒体消息解析、入群事件处理,以及使用 Go SDK 回发消息。

1. Webhook 与 Long Polling(严格互斥)

本文实现 Webhook 模式。只要 BotMother 中配置了 Webhook URL,Webhook 就是唯一有效的收消息方式,Long Polling 无法收取消息。若要切回 Long Polling,必须先清空 Webhook 设置。

完整的模式选择和切换流程请查看DeBox 机器人开发总览,Long Polling 实现请查看DeBox 机器人 Go SDK

2. 什么时候使用 Webhook

建议在以下场景使用:

  • 服务已部署在公网 HTTP/HTTPS。
  • 需要比轮询更低的消息延迟。
  • 希望采用事件推送模型。
  • 需要处理图片、视频、文件消息或入群事件。

3. BotMother Webhook 配置

在 BotMother 的 Webhook 设置页配置 Webhook URLWebhook Key

Webhook URL 示例:

  • https://your-domain.com/bot/webhook

同时请注意:

  • Webhook URL 必须使用 HTTPS,并且是公网可访问、长期稳定的地址。
  • Bot 后端必须校验每次回调请求头中的 X-API-KEY 是否等于配置的 Webhook Key
  • 如果修改了 Webhook Key,Bot 后端用于校验回调的值也必须同步更新。

4. 回调协议说明

4.1 HTTP 请求规范

DeBox 会发送:

  • 方法:POST
  • Content-Type:application/json
  • 请求头:X-API-KEY: <webhook-key>

推荐做法:

  • 缺少或校验失败的 X-API-KEY 直接拒绝。
  • 仅在服务端成功接收事件后返回 200 OK
  • 如业务要求幂等,请在你自己的系统中实现事件去重。

4.2 顶层回调字段

Webhook 回调使用扁平化 JSON 结构。开发者最常用的字段如下:

字段类型必填说明
from_user_idstring当前事件发送者的 DeBox user_id 值,与发送者的 uidinvite_code 相同。私聊场景是对方用户,群聊场景是发言用户;当前入群事件实现中,这里是新入群成员。
to_user_idstring当前目标 Bot 账号的 DeBox user_id 值,与该账号的 uidinvite_code 相同。
namestringfrom_user_id 的显示名。
picstringfrom_user_id 的头像 URL。
addressstringfrom_user_id 的钱包地址。
languagestring用户语言信息,存在则返回。
group_idstringDeBox 群组 ID 或语聊房 ID。私聊时为空字符串。
parse_modestring当前回调内容类型,例如 textimagevideofilelinkevent:joinGroup
messagestring归一化后的消息内容。具体语义由 parse_mode 决定。
message_rawstringDeBox 解析后的原始内容。具体语义由 parse_mode 决定。
mention_usersarray<object>群文本消息中被提及的用户列表。仅在有 @ 时返回。

mention_users 单项结构:

字段类型说明
user_idstring被提及用户的 DeBox user_id 值,与该用户的 uidinvite_code 相同
namestring显示名
picstring头像 URL
addressstring钱包地址

4.3 parse_mode 类型说明

parse_mode含义message / message_raw 的内容
text文本或命令消息消息文本。message 会移除 @,message_raw 保留原始文本。
image图片消息图片 URL
video视频消息视频 URL
file文件消息文件 URL
link分享链接或 DApp 分享链接 URL
event:joinGroup入群事件当前实现中为新成员的 user_id

关键说明:

  • imagevideofile 回调的是 URL,不是二进制文件流。
  • text 场景中,message 适合做业务解析,message_raw 适合保留原始输入。
  • event:joinGroup 是事件型回调,不是普通会话消息:
    • group_id 表示目标群组。
    • 当前实现中 from_user_id 为新入群成员。
    • 当前实现中 message 承载的标识值与新入群成员的 user_id 相同。

5. 按类型查看回调示例

5.1 文本消息

{
"from_user_id": "u_alice",
"to_user_id": "u_bot",
"name": "Alice",
"pic": "https://cdn.example.com/alice.png",
"address": "0x1234",
"language": "zh",
"group_id": "cc0onr82",
"parse_mode": "text",
"message": "hello bot",
"message_raw": "@MyBot hello bot",
"mention_users": [
{
"user_id": "u_bot",
"name": "MyBot",
"pic": "https://cdn.example.com/bot.png",
"address": ""
}
]
}

5.2 图片消息

{
"from_user_id": "u_alice",
"to_user_id": "u_bot",
"name": "Alice",
"language": "zh",
"group_id": "cc0onr82",
"parse_mode": "image",
"message": "https://cdn.example.com/image.png",
"message_raw": "https://cdn.example.com/image.png"
}

5.3 视频消息

{
"from_user_id": "u_alice",
"to_user_id": "u_bot",
"name": "Alice",
"language": "zh",
"group_id": "cc0onr82",
"parse_mode": "video",
"message": "https://cdn.example.com/video.mp4",
"message_raw": "https://cdn.example.com/video.mp4"
}

5.4 文件消息

{
"from_user_id": "u_alice",
"to_user_id": "u_bot",
"name": "Alice",
"language": "zh",
"group_id": "cc0onr82",
"parse_mode": "file",
"message": "https://cdn.example.com/report.pdf",
"message_raw": "https://cdn.example.com/report.pdf"
}

5.5 入群事件

{
"from_user_id": "u_new_member",
"to_user_id": "u_bot",
"name": "New Member",
"pic": "https://cdn.example.com/new-member.png",
"address": "0xabcd",
"language": "zh",
"group_id": "cc0onr82",
"parse_mode": "event:joinGroup",
"message": "u_new_member",
"message_raw": "u_new_member"
}

典型用途:

  • 新成员欢迎语。
  • 入群自动引导。
  • 成员事件记录与风控侧同步。

6. Go 服务接收设计

推荐服务端处理流程:

  1. 校验 X-API-KEY
  2. 解析 JSON 到强类型结构体。
  3. 根据 parse_mode 分发业务逻辑。
  4. 根据 group_id 判断私聊或群聊上下文。
  5. 使用 Go SDK 回发消息。

6.1 推荐结构体定义

type WebhookUser struct {
UserID string `json:"user_id"`
Name string `json:"name"`
Pic string `json:"pic"`
Address string `json:"address"`
}

type WebhookPayload struct {
FromUserID string `json:"from_user_id"`
ToUserID string `json:"to_user_id"`
Name string `json:"name"`
Pic string `json:"pic"`
Address string `json:"address"`
Language string `json:"language"`
GroupID string `json:"group_id"`
ParseMode string `json:"parse_mode"`
Message string `json:"message"`
MessageRaw string `json:"message_raw"`
MentionUsers []WebhookUser `json:"mention_users"`
}

6.2 完整示例(Gin + Go SDK)

package main

import (
"fmt"
"net/http"
"os"

"github.com/gin-gonic/gin"
boxbotapi "github.com/debox-pro/debox-chat-go-sdk/boxbotapi"
)

type WebhookUser struct {
UserID string `json:"user_id"`
Name string `json:"name"`
Pic string `json:"pic"`
Address string `json:"address"`
}

type WebhookPayload struct {
FromUserID string `json:"from_user_id"`
ToUserID string `json:"to_user_id"`
Name string `json:"name"`
Pic string `json:"pic"`
Address string `json:"address"`
Language string `json:"language"`
GroupID string `json:"group_id"`
ParseMode string `json:"parse_mode"`
Message string `json:"message"`
MessageRaw string `json:"message_raw"`
MentionUsers []WebhookUser `json:"mention_users"`
}

func main() {
webhookKey := os.Getenv("DEBOX_WEBHOOK_KEY")
apiKey := os.Getenv("DEBOX_BOT_API_KEY")
apiSecret := os.Getenv("DEBOX_BOT_API_SECRET")

if webhookKey == "" || apiKey == "" || apiSecret == "" {
panic("缺少必要环境变量:DEBOX_WEBHOOK_KEY / DEBOX_BOT_API_KEY / DEBOX_BOT_API_SECRET")
}

bot, err := boxbotapi.NewBotAPI(apiKey, apiSecret)
if err != nil {
panic(err)
}

r := gin.Default()
r.POST("/bot/webhook", func(c *gin.Context) {
if c.GetHeader("X-API-KEY") != webhookKey {
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid webhook key"})
return
}

var payload WebhookPayload
if err := c.ShouldBindJSON(&payload); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

chatType := "private"
chatID := payload.FromUserID
if payload.GroupID != "" {
chatType = "group"
chatID = payload.GroupID
}

switch payload.ParseMode {
case "text":
reply := boxbotapi.NewMessage(chatID, chatType, "已收到文本消息:"+payload.Message)
reply.ParseMode = boxbotapi.ModeText
_, err = bot.Send(reply)

case "image":
reply := boxbotapi.NewMessage(chatID, chatType, "已收到图片:"+payload.Message)
reply.ParseMode = boxbotapi.ModeText
_, err = bot.Send(reply)

case "video":
reply := boxbotapi.NewMessage(chatID, chatType, "已收到视频:"+payload.Message)
reply.ParseMode = boxbotapi.ModeText
_, err = bot.Send(reply)

case "file":
reply := boxbotapi.NewMessage(chatID, chatType, "已收到文件:"+payload.Message)
reply.ParseMode = boxbotapi.ModeText
_, err = bot.Send(reply)

case "event:joinGroup":
welcome := fmt.Sprintf("欢迎 %s 加入群组", payload.FromUserID)
reply := boxbotapi.NewMessage(chatID, chatType, welcome)
reply.ParseMode = boxbotapi.ModeText
_, err = bot.Send(reply)

default:
reply := boxbotapi.NewMessage(chatID, chatType, "暂不支持的回调类型:"+payload.ParseMode)
reply.ParseMode = boxbotapi.ModeText
_, err = bot.Send(reply)
}

if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}

c.JSON(http.StatusOK, gin.H{"ok": true})
})

_ = r.Run(":8080")
}

7. 使用 Go SDK 回发图片、视频、文件

出站消息的完整契约请查看 OpenAPI 消息字段说明。以下内容只演示 如何使用 Go SDK 回发媒体。

image := boxbotapi.NewMessage("cc0onr82", "group", "https://cdn.example.com/welcome.png")
image.ParseMode = boxbotapi.ModeImage
_, _ = bot.Send(image)

video := boxbotapi.NewMessage("cc0onr82", "group", "https://cdn.example.com/intro.mp4")
video.ParseMode = boxbotapi.ModeVideo
_, _ = bot.Send(video)

file := boxbotapi.NewMessage("cc0onr82", "group", "https://cdn.example.com/guide.pdf")
file.ParseMode = boxbotapi.ModeFile
_, _ = bot.Send(file)

8. 生产环境建议

  • 将 Webhook 鉴权和 JSON 解析封装为中间件或统一适配层。
  • 业务日志至少记录 parse_modefrom_user_idto_user_idgroup_id
  • 媒体 URL 不应被长期信任,建议按你的留存策略拉取、转存或校验。
  • 群机器人应按 group_id 做路由,不要假设一个 Bot 只服务一个群。
  • 如下游处理耗时较长,建议异步化,避免回调阻塞。

9. 常见问题

  • 配了 Webhook 但收不到消息:
    • 先检查 URL 可达性、TLS、服务日志和 X-API-KEY 校验逻辑。
  • 同时开 Webhook 和 Long Polling:
    • 两者必须二选一。开启 Webhook 后,Polling 不再是有效收消息链路。
  • 把媒体消息当成文件上传流处理:
    • Webhook 返回的是媒体 URL,不是文件流。
  • 只判断 message 不判断 parse_mode
    • 必须优先根据 parse_mode 做分发。
  • 本地开发没有公网地址:
    • 用隧道工具临时暴露本地端口联调。

10. 相关文档