Skip to content

Webhook 模式(HTTP 回调)

与 WebSocket 网关二选一bot.mode)。mode: webhook 时机器人不建立出站长连接,改为启动本地 HTTP 服务,由 QQ 开放平台把事件 POST 推送到回调地址

适合无法维持出站长连接的场景。回调地址需公网可达,开放平台通过 HTTPS 访问它(HTTPS 的提供方式见 部署要求,框架本身不强制任何方案)。

工作原理

开放平台 ──POST 回调地址(HTTPS)──> HTTPS 入口 ──> Bngeuit HttpServer(host:port/path)
                                        │(反向代理 / 内网穿透 / 直接监听,均可选)
                                        ├─ op=13 回调地址验证 → 返回签名完成验证
                                        └─ op=0  事件推送     → 分发到 EventBus + 回包 op=12 ACK

事件 payload 结构与 WebSocket DISPATCH 相同({id, op:0, d, s, t}),分发路径与 WebSocket 模式完全一致(同一 ConnectionStateEventBus)——插件无需感知接入方式差异

回调地址验证(op=13)

在开放平台配置回调地址时,平台发起验证请求。框架用 appSecret 确定性派生的 Ed25519 私钥对 d.event_ts + d.plain_token 计算签名,原样返回 plain_token 与签名完成验证(与官方 Go 示例输出逐字节一致,已用单测锁定)。

事件请求签名校验(X-Signature-Ed25519)

每个事件推送(op=0)默认校验:

  • 请求头 X-Signature-Ed25519(hex 64 字节签名) + X-Signature-Timestamp(时间戳)
  • 签名体 = 时间戳字符串 + 原始请求体字节
  • 公钥 = appSecret 派生的 Ed25519 公钥
  • 校验失败返回 401 拒绝处理

配置 webhook.verifySignature: false 可关闭(仅调试用,生产必须开启)。

配置

yaml
bot:
  mode: webhook              # 切换为 webhook 接入
  webhook:
    host: "0.0.0.0"          # 回调监听地址
    port: 8080               # 回调监听端口(官方限定 80/443/8080/8443)
    path: "/"                # 回调路径(必须以 / 开头)
    verifySignature: true    # 校验事件请求签名(默认 true)

部署要求

  • 端口:官方限定 80 / 443 / 8080 / 8443
  • HTTPS 必选:开放平台要求回调地址为 HTTPS。框架不关心 TLS 在哪一层终止——常用方式:
    • 反向代理(nginx / Caddy 等)把公网 HTTPS 转发到本机监听端口
    • 内网穿透(frp / cloudflared 等)把公网域名映射到本机
    • 直接让 Bngeuit 监听公网端口并提供证书(如监听 443)
  • 回调地址:开放平台填写的回调地址 = https://<公网域名或IP><path>,与 webhook.path 必须一致;配置一次即固定,重启无需重配
  • 安全建议:前面有本地反向代理/穿透时,host 可锁 127.0.0.1 只让本机入口访问;直接暴露公网端口时保持 0.0.0.0,依靠签名校验(verifySignature: true)防止伪造请求

与 WebSocket 模式的差异

维度WebSocketWebhook
连接方向出站长连接(主动连网关)入站 HTTP 回调(被动收推送)
分片(shard)支持多分片无分片(单一回调地址)
断线重连框架自动重连(RESUME 补消息)平台侧负责推送重试
就绪事件网关真实 READY 触发 ReadyEvent回调服务启动后触发 WebhookReadyEvent
接入鉴权access_token(IDENTIFY/RESUME)appSecret 派生 Ed25519 签名
回复消息走 REST API(需 access_token)同左——回复消息仍需 access_token

就绪信号(WebhookReadyEvent / onActive)

READY / RESUMED 是 QQ 开放平台 WebSocket 网关专属的系统事件(官方「通用数据结构」),Webhook 模式没有网关连接、平台也不会推送这两个事件。框架不会伪造 ReadyEvent,而是:

  • 事件形式:回调服务启动完成后分发一次 WebhookReadyEventevent/receive/,仅 Webhook 模式触发),事件监听器 / 脚本可用 @SubscribeEvent / bngeuit.on('WebhookReadyEvent', ...) 监听
  • 插件推荐方式:实现 Plugin.onActive() —— 该生命周期在两种接入模式统一触发一次(WS 全部分片就绪 / Webhook 回调服务就绪),做「机器人已上线」初始化无需关心接入方式
java
// 插件:两种模式通用的「机器人上线」时机
@Override
public void onActive() {
    startScheduledTasks(); // 启动定时推送等
}

实现要点(零第三方依赖)

  • HTTP 服务基于 JDK 内置 com.sun.net.httpserver.HttpServer,2 线程 daemon 回调池,事件处理不阻塞回调线程(EventBus 异步分发)
  • Ed25519 签名/校验使用纯 Java 实现(RFC 8032)与 JDK 内置实现,无第三方库