☀️
EN
← 返回首页

QQ Webhook Gateway

海豹骰子接 QQ 官方机器人 Webhook 的网关。

项目简介

QQ 官方的 WebSocket 模式有个绕不开的问题:会话大约每小时会被强制重连一次,重连的那几秒连接是断的,期间消息可能丢失。如果反复重连失败还会雪崩,出现长时间收不到消息的情况。

另外,官方被动回复有时效窗口,回复稍慢就会报「回复消息 msg_id 已过期」(错误码 40034005),表现出来就是后台已经生成了回复,QQ 里却发不出来。

这个网关把链路切到 Webhook 模式,并且补上了海豹 1.6.1 缺少的部分。海豹自带的 Webhook 只做事件验签,没有实现 QQ 官方的 op=13 回调地址校验,所以直接在开放平台填海豹地址会提示「签名校验不通过」,保存不了。

网关做的事情:

下载

直接下载(含 Caddy / frpc / 源码 / 文档) GitHub 仓库 GitHub Release

架构

QQ 官方
  |  HTTPS
Caddy(公网服务器,443)
  |  反代到 127.0.0.1:19092
frps(公网服务器)
  |  frp 隧道
frpc(你的服务器)
  |  127.0.0.1:9092
QQWebhookGateway.exe(验签 + op=13 + 去重 + ACK)
  |  127.0.0.1:8099/qq/callback
海豹 sealdice-core(Webhook 模式)

如果海豹本身就跑在外网服务器上,可以省掉 frps 和 frpc,直接 Caddy 到网关再到海豹。

包里有什么

源码仓库和 Release 都在 GitHub:github.com/ternatapinellia/qq-webhook-gateway

开始之前:准备一个域名

QQ 官方的网页回调只接受 HTTPS 地址,所以需要一个域名。两种办法,选一个:

免费域名也可以,例如 DuckDNS。Caddy 的写法都在「Caddyfile-示例.txt」里。

怎么选部署方式

唯一必须改的配置

打开 config.json,只要改这一项:

"appSecret": "填你机器人的AppSecret"

AppSecret 就在海豹目录 data/default/serve.yaml 里,找 appSecret 那一行,复制过来即可。

不需要重新申请机器人,也不用填 QQ 号,用海豹里已有的 AppID 和 AppSecret 就够了。

三步上手(方式 B:外网服务器中转)

1. 外网服务器:装 frps 和 Caddy

frps 只需要在外网服务器上装一次,之后不管你开多少个骰子都用同一个,不用再动它。它只做一件事:开 7000 端口等 frpc 连上来。

# /opt/frp/frps.toml
bindPort = 7000
auth.method = "token"
auth.token = "换成你自己的强口令"

Caddy 把域名转给隧道端口,端口要和 config.json 的 frp.remotePort 一致:

qq1.example.com {
    reverse_proxy /qq/callback* 127.0.0.1:19092 {
        transport http {
            dial_timeout 10s
            response_header_timeout 30s
        }
    }
}
systemctl reload caddy

2. 你的服务器:改海豹和 config.json

海豹 data/default/serve.yaml 里找到官方 QQ 端点那段:

adapter:
  appID: "你的AppID"
  appSecret: 你的AppSecret
  useWebhook: false        # 改成 true
  webhookPath: ""          # 改成 /qq/callback
  webhookPort: 0           # 改成 8099

重启海豹,日志出现这两行才算成功:

official qq webhook: 监听地址 :8099/qq/callback
official qq webhook模式启动成功

config.json 里填隧道信息,网关启动时会自动把 frpc 一起拉起来:

"frp": {
  "enabled": true,
  "serverAddr": "外网服务器IP",
  "serverPort": 7000,
  "token": "和 frps.toml 里一致",
  "remotePort": 19092,
  "localPort": 9092,
  "name": "qqbridge"
}

3. 填开放平台并自检

QQWebhookGateway.exe -config config.json

回调地址填:

https://qq1.example.com/qq/callback

然后跑自检:

QQWebhookVerify.exe -secret 你的AppSecret -url https://qq1.example.com/qq/callback

期望看到:

[1] op=13  callback validation : status=200 ok=true
[2.1] event delivery #1        : status=200
[2.2] event delivery #2        : status=200
[2.3] event delivery #3        : status=200
RESULT: PASS  (dedup working, duplicates >= 2)

含义是同一条事件发 3 次,网关只转发 1 次、拦下 2 次。

压力测试结果

用 QQWebhookLoadTest.exe 模拟 500 条唯一事件加 20% 重复,三台实际部署的机器实测:

sent        : 600  (unique=500 duplicated=100)
http 200    : 600
http failed : 0
error rate  : 0.00%
forwarded   : 500
duplicates  : 100
failed      : 0

错误率 0%,转发数与唯一事件数完全一致。

看运行状态

http://127.0.0.1:9092/health

常见问题

后台保存提示「签名校验不通过」

说明请求没走到网关。按顺序查:

  1. 网关在跑吗?浏览器打开 http://127.0.0.1:9092/health
  2. 公网能通吗?浏览器打开 https://你的域名/health
  3. Caddy 的端口和 config.json 的 frp.remotePort 对得上吗
  4. 方式 B 还要看隧道连没连上,看网关目录下的 frpc.log

消息收不到

看海豹日志有没有「收到群的消息」。没有就说明消息没进来,查回调地址和隧道;有就说明消息进来了,是海豹那边的问题。

海豹回复了,但 QQ 里看不到

这是出站问题,和本网关无关。常见原因是「回复消息 msg_id 已过期」(错误码 40034005),也就是回复太慢超过了 QQ 的被动回复有效期。本网关先 ACK 再转发,已经大幅降低这个概率。

一条指令回复好几次

本网关已按 event id 去重。若仍重复,检查是否有第二个程序也在收同一个 AppID 的回调,比如旧的 webhook 桥还开着;再检查是否同一个 QQ 号被两个骰子同时挂着;以及开放平台里回调地址是否只填了一个。

需要重新申请机器人密钥吗

不需要。用海豹里已有的 AppID 和 AppSecret 就行,它们本来就在 serve.yaml 里,不需要额外申请或登录。

安全提醒

🎵 当前音乐: 未播放

留言