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 URL 和 Webhook 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_id | string | 是 | 当前事件发送者的 DeBox user_id 值,与发送者的 uid 和 invite_code 相同。私聊场景是对方用户,群聊场景是发言用户;当前入群事件实现中,这里是新入群成员。 |
to_user_id | string | 是 | 当前目标 Bot 账号的 DeBox user_id 值,与该账号的 uid 和 invite_code 相同。 |
name | string | 否 | from_user_id 的显示名。 |
pic | string | 否 | from_user_id 的头像 URL。 |
address | string | 否 | from_user_id 的钱包地址。 |
language | string | 否 | 用户语言信息,存在则返回。 |
group_id | string | 否 | DeBox 群组 ID 或语聊房 ID。私聊时为空字符串。 |
parse_mode | string | 是 | 当前回调内容类型,例如 text、image、video、file、link、event:joinGroup。 |
message | string | 是 | 归一化后的消息内容。具体语义由 parse_mode 决定。 |
message_raw | string | 是 | DeBox 解析后的原始内容。具体语义由 parse_mode 决定。 |
mention_users | array<object> | 否 | 群文本消息中被提及的用户列表。仅在有 @ 时返回。 |
mention_users 单项结构:
| 字段 | 类型 | 说明 |
|---|---|---|
user_id | string | 被提及用户的 DeBox user_id 值,与该用户的 uid 和 invite_code 相同 |
name | string | 显示名 |
pic | string | 头像 URL |
address | string | 钱包地址 |
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 值 |
关键说明:
image、video、file回调的是 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 服务接收设计
推荐服务端处理流程:
- 校验
X-API-KEY。 - 解析 JSON 到强类型结构体。
- 根据
parse_mode分发业务逻辑。 - 根据
group_id判断私聊或群聊上下文。 - 使用 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_mode、from_user_id、to_user_id、group_id。 - 媒体 URL 不应被长期信任,建议按你的留存策略拉取、转存或校验。
- 群机器人应按
group_id做路由,不要假设一个 Bot 只服务一个群。 - 如下游处理耗时较长,建议异步化,避免回调阻塞。
9. 常见问题
- 配了 Webhook 但收不到消息:
- 先检查 URL 可达性、TLS、服务日志和
X-API-KEY校验逻辑。
- 先检查 URL 可达性、TLS、服务日志和
- 同时开 Webhook 和 Long Polling:
- 两者必须二选一。开启 Webhook 后,Polling 不再是有效收消息链路。
- 把媒体消息当成文件上传流处理:
- Webhook 返回的是媒体 URL,不是文件流。
- 只判断
message不判断parse_mode:- 必须优先根据
parse_mode做分发。
- 必须优先根据
- 本地开发没有公网地址:
- 用隧道工具临时暴露本地端口联调。
10. 相关文档
- Long Polling 接入文档:DeBox 机器人 Go SDK