- 平台:Cloudflare Workers + D1 + KV
- 域名:
https://bot.astroptis.dpdns.org(自定义域名,QQ 平台拒绝.workers.dev域名) - Webhook URL:
https://bot.astroptis.dpdns.org/api/webhook - QQ AppID:
1905493443 - AppSecret 存于 KV
config:qq_credentials(会变动,通过管理面板设置,不要硬编码) - 管理面板密码:
tianlu4.5(首次登录设置) - AI 后端:b.ai,endpoint
https://api.b.ai,模型deepseek-v4-flash(KV 中配置) - 联网搜索:AnySearch REST API
https://api.anysearch.com/v1/search(免费 1000次/天,无需 key,匿名可用)。请求POST {"query":"...","max_results":N},返回code:0+data.results[](title/url/snippet/content) - 搜索触发:
needsSearch()启发式关键词检测(天气/新闻/最新/股价/汇率/搜索等),命中后自动调用 AnySearch 并把结果注入 AI system prompt 上下文
- QQ access_token 端点:
https://api.bot.qq.com/app/getAppAccessToken(不是bots.qq.com,那是错的) - access_token 成功响应:
{"access_token":"...","expires_in":3815}— 无code字段;失败才是{"code":100002,"message":"internal err"}。判断成功用data.access_token是否存在,不要用code !== 0(成功时 code 是 undefined) - 发送消息 API:
https://api.bot.qq.com/v2/users/{openid}/messages,Authorization 头QQBot {token} - webhook 事件字段:事件类型用
t(不是type),数据在d(不是raw)d.content消息文本,d.author.user_openid发送者,d.id消息ID(ROBOT1.0_...)- 顶层
id是C2C_MESSAGE_CREATE:...,不能作为 msg_id 发送
- Webhook 回调验证:请求
{"d":{"plain_token":"...","event_ts":"..."},"op":13},返回{"plain_token":"...","signature":"..."} - Ed25519 签名:secret 重复填充到 32 字节作为种子,签名
event_ts + plain_token。必须用tweetnacl(Web Crypto 的 Ed25519 与 Go 签名不一致);Go 参考ed25519.GenerateKey(strings.NewReader(seed))+ed25519.Sign,tweetnacl 输出与 Go 完全一致
- 免费计划有冷启动:低活跃时首个请求慢 ~5s,cron
*/1 * * * *保活可缓解(已配置) wrangler kv key get/wrangler tail在本机 Windows 会崩溃(access violation),用管理面板 API 代替- PowerShell 不支持
&&,用; - 查询/删除 D1 远程库:
wrangler d1 execute qq-bot-db --remote --command "SQL"(单条语句,不能多条) - Windows curl 发送 JSON 到 QQ token 接口会报
100002,用 Node.js 或 Worker fetch 测试 - webhook 必须用
ctx.waitUntil()异步处理消息:如果同步等待 AI fetch(3-5秒+偶发挂起)再返回,Cloudflare 会强制终止 Worker 导致 AI 请求中断、消息丢失。先返回OK再ctx.waitUntil(processAsync())已解决 - 不要用 AbortController + fetch:Cloudflare Workers 存在兼容问题会导致请求挂起;改用
Promise.race([fetch, timeout])实现超时 - 给 AI 调用加超时 + 重试:
api.b.ai对 Cloudflare Worker 偶发不响应,fetchWithTimeout(15000)+ 二次重试提高可靠性 - AppSecret 重置后需等待几分钟:QQ 平台 secret 缓存有延迟,重置后立即保存 webhook 会报签名错误,等 5 分钟再试
- AppSecret 一致性:用户多次重置,每次重置后必须同步更新 KV
config:qq_credentials,否则签名验证失败 - deepseek-v4-flash 不支持稳定 function calling:b.ai 上它有时返回 tool_calls、有时只输出文字"我去查一下"(不稳定)。b.ai 的 mimo-v2.5 用非标准文本式
<tool_call>标记也不稳定。OpenCode Zen mimo-v2.5-free 支持标准 tool_calls 但免费限流极严(连续第1次就 429),不可用 - 实现"AI 主动搜索"用两阶段标记方案:第一次调用让 AI 只输出
SEARCH/NO(max_tokens=100,推理模型 max_tokens 太小会被截断在 reasoning 阶段导致 content 为空),若 SEARCH 则执行搜索并把结果注入 system prompt 再第二次调用生成回复 - 决策判断检查
content:deepseek 推理模型把思考放reasoning_content,content可能为空,需json()解析后读choices[0].message.content - AI 请求超时:用
Promise.race自定义超时(AbortController 在 CF Workers 有兼容问题) - CF→api.b.ai 网络偶发挂起:Worker 到 b.ai 的 fetch 偶发无响应(非代码问题),用超时+重试兜底
- b.ai 模型列表:
deepseek-v4-flash(免费,可用)、mimo-v2.5、mimo-v2.5-pro、deepseek-v4-pro(付费,access_denied)、gpt-5.2等付费模型需充值
- 部署:
wrangler deploy(项目目录C:\Users\wang\qq-bot-worker) - GitHub 仓库:
Astroptis/WorkerBot - 管理面板:
https://bot.astroptis.dpdns.org/ - 测试消息:POST 到
/api/webhook,body 用t/d字段格式,openid 用46BD04B2B0B12C2485CCDB9FCF5F6394(真实用户,能触发真回复)