海豹骰子接 QQ 官方机器人 Webhook 的网关。
QQ 官方的 WebSocket 模式有个绕不开的问题:会话大约每小时会被强制重连一次,重连的那几秒连接是断的,期间消息可能丢失。如果反复重连失败还会雪崩,出现长时间收不到消息的情况。
另外,官方被动回复有时效窗口,回复稍慢就会报「回复消息 msg_id 已过期」(错误码 40034005),表现出来就是后台已经生成了回复,QQ 里却发不出来。
这个网关把链路切到 Webhook 模式,并且补上了海豹 1.6.1 缺少的部分。海豹自带的 Webhook 只做事件验签,没有实现 QQ 官方的 op=13 回调地址校验,所以直接在开放平台填海豹地址会提示「签名校验不通过」,保存不了。
网关做的事情:
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 就够了。
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
海豹 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"
}
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
说明请求没走到网关。按顺序查:
看海豹日志有没有「收到群的消息」。没有就说明消息没进来,查回调地址和隧道;有就说明消息进来了,是海豹那边的问题。
这是出站问题,和本网关无关。常见原因是「回复消息 msg_id 已过期」(错误码 40034005),也就是回复太慢超过了 QQ 的被动回复有效期。本网关先 ACK 再转发,已经大幅降低这个概率。
本网关已按 event id 去重。若仍重复,检查是否有第二个程序也在收同一个 AppID 的回调,比如旧的 webhook 桥还开着;再检查是否同一个 QQ 号被两个骰子同时挂着;以及开放平台里回调地址是否只填了一个。
不需要。用海豹里已有的 AppID 和 AppSecret 就行,它们本来就在 serve.yaml 里,不需要额外申请或登录。
留言