NovaBot
GitHub

第 14 章 遇到问题怎么办

出了问题最难受的一点是:多数故障的表现完全一样——什么都没发生。 没有报错、没有提示,只是消息没到。这一章按从后往前的顺序帮你逐段确认, 先分清问题出在哪一环,再对症处理;最后一节讲提问前要准备什么。 先看首页的健康自检,点「查看全部检查项」展开,每一项异常都会给出具体的修复建议,而不只是「异常」二字。

先分清问题出在哪一段

哔哩哔哩 ──①──▶ NovaBot ──②──▶ OneBot 实现 ──③──▶ QQ 群
  1. 先在连接页发送测试消息。收到 → ②③ 段是通的,问题在采集侧;收不到 → 问题在投递侧
  2. 收不到时看「发送测试消息」回显的接口原始响应,它会直接告诉你是口令不对、群号不存在还是没在群里
  3. 采集侧的问题看连接页「哔哩哔哩」卡的登录状态,与首页健康自检里的「直播间连接」探针

查之前先打开这两个开关

默认是关的,因为它们是排障工具而不是运行必需。设置页点左边分组菜单最底下的「高级选项」后出来的 「日志与调试」组有这两项:

  • 事件日志:每条直播事件写进 EventDebug/,查事件收到了没有、字段对不对
  • 网络日志:每次接口请求写进 NetworkDebug/,查慢在哪一跳

改完要重启。网络日志尤其值得一提:本机回环调用比走公网还慢这种事, 就是靠它把每一跳的耗时摆出来才看清的——只看应用日志看不出来。

注意查完记得关掉。两个目录留多久见第 11 章,
实测三个房间约 3.8 MB/日。而 EventDebug/ 写的是完整事件转储,含观众昵称与 uid,
保留期内会把他人的个人信息留在磁盘上。

配置界面打不开

控制台一直停在「正在加载…」 — 先强制刷新页面:Mac 按 Cmd+Shift+R,Windows 按 Ctrl+F5。

日志里的地址打不开 — 界面默认只监听 127.0.0.1。装在远程服务器上时要先建隧道, 做法见第 4 章。不要改成 0.0.0.0 暴露到公网 (为什么不能,见附录 C)。

提示令牌无效 — 令牌默认每次启动重新生成,重启后要用新日志里的地址。 想固定下来就在设置页搜「控制台访问令牌」填一个随机串 (别用 test、123456 这类弱口令,启动会告警),改完要重启。

程序压根没起来 — 见下一节。

NapCat 的面板白屏,NovaBot 日志里一条 WebUI 请求都没有 — 先把这句话读对: 日志里没有请求,不等于浏览器没发请求。 更常见的情况是浏览器压根没把请求送到 服务器上——它在本地就自己答了。链路上有两层缓存,只清中间那层等于没清: 第一层是 Service Worker,它在浏览器里拦截请求、直接回自己存的那一份, 请求根本到不了 NovaBot;第二层是 HTTP 磁盘缓存,只注销 Service Worker 的话 当场会好、换个新标签页又坏——Service Worker 重装时从仍然陈旧的磁盘缓存取货, 把旧的那份原样缓存回来。(NovaBot 的配置界面自己不注册 Service Worker, 这个是 NapCat 的 WebUI 带来的。)

两层一起清。在面板所在的那个源上打开 devtools 控制台跑:

(async () => {
  for (const r of await navigator.serviceWorker.getRegistrations()) await r.unregister();
  for (const k of await caches.keys()) await caches.delete(k);
  await fetch(location.pathname, { cache: 'reload' });  // 穿透 HTTP 磁盘缓存重取
  location.reload();
})();

第三行才是关键:前两行只清到 CacheStorage 那一层,{ cache: 'reload' } 才穿透 浏览器的 HTTP 磁盘缓存。清完在新标签页里再看一次——「清了、现在好了」只说明这一次好, 证明不了不复发。

说明看 devtools 时留个心眼:响应 200 不等于服务器做了事。
从缓存回放的响应也是 200,Network 面板里要看 Size 那一列有没有写着
(disk cache) 或 (ServiceWorker)——写了就说明这一发根本没出门。
具体到换取凭据那一步,看到 200 也不等于花掉了一个额度;要判断它到底走没走到服务器,
看 NovaBot 那一侧的日志。反过来也成立:真撞上速率闸时页面会给一句
「换取凭据过于频繁,请稍等一会儿再试」并带上额度大约多少秒回一个,
看到这句反而说明请求确实到了服务器——按它说的秒数等一等,等过了还不行,
就不是闸的问题,往「配置本来就不对」那边查。

这种白屏通常出现在改过反向代理或换过地址之后:浏览器里还留着旧的 Service Worker 和旧的磁盘缓存。两层都清过、换个新标签页还是白的,就不是缓存的事了,回到 NovaBot 的日志里看那一刻有没有请求进来。

程序起不来

配置文件写坏了 — NovaBot 会自动进入安全模式:一个只提供「看 / 改 application.yml」的极简页面,地址与令牌同样打印在日志里。在页面上把写坏的地方改回来; 要退回某一份备份,到服务器上把那份备份改回原名再重启。 安全模式是独立于主程序的最小实现,所以主程序起不来时它仍然能用。

日志里有「这个目录已有一份 NovaBot 在运行」 — 同一个安装目录里另一个版本 还在跑。这个目录同时只跑一份,先停掉它再起。用换版工具换版碰不到这条:热交接 本来就是新版本先起来、在门口等旧版本退出,普通重启则先停旧版本再起新版本; 只有自己手动去起第二份时才会撞上。

端口被占用 — 程序起不来时进不了设置页,直接改配置文件里的 server.port (能进控制台时就是设置页的「服务端口」), 或找出占用进程:

lsof -i :7827

Java 版本不对 — 需要 JDK 17。本工程只在 17 上验过。没有 Java 或低于 17 时, 一键安装会装 17;装着比 17 新的,一键安装不停,会另找或另装一份 JDK 17、只给这一次 构建用,机器默认的 Java 不动。手动跑 ./build.sh 构建才会停下,要先把 JDK 17 排在 PATH 最前:

java -version

升级或换版没成

先看换版工具的最后一行——它写明机器上现在跑的是哪一版(四种说法各是什么 意思,见第 13 章)。换版没做成时工具会把旧版本起 回来,多数情况下机器仍在跑旧版。再按它印出的话分这几类处理:

  • 它说这次什么都没改、拒绝换版:先清掉它点名的障碍——不止一份在跑的,停掉多余那份;目标版本正在反复重启的,看下面的日志;旧单元还在跑的,照第 13 章「老用户第一次升级」先手动停掉它。清完重跑一次换版命令。
  • 它说没换成、正在把旧版起回来:新版本没起来。看日志找原因,修好再重跑换版命令。
  • 它说「旧版 5.7.8 没能起来。」:新旧两版都没在跑。两份日志都看,先把旧版救活(直播要紧),再查新版本为什么起不来。
  • 它印「请手动执行」:新版本已经在跑,只是开机自启没切过去。要关的每个版本、以及要开的目标版本,各自出错各印一句,可能只有一句。要关的不止旧版一个时,每个各印一句。原话是「请手动执行:systemctl disable novabot@5.7.8」或「请手动执行:systemctl enable novabot@5.7.9」。命令不带 sudo,前面加上 sudo 再执行。重新加载服务配置失败时,印「重新加载服务配置没有成功。」。这句里没有「请手动执行」;该关的都关掉、目标也开上时,一句「请手动执行」都没有。同样退码是 1,最后一行照第 13 章再带开机自启那一截:两个版本开着时用「和」连,三个或更多时前面用顿号、最后一个用「和」。

看日志(取最近 200 行直接看;要盯着看把 -n 200 --no-pager 换成 -f):

sudo journalctl -u novabot@<目标版本> -n 200 --no-pager

安装脚本的收尾说「换版没有全部做成」时,同样先看上面工具的最后一行,收尾给的 两步就是重试换版与看日志。安装时 --dir 被拒,最常见是目录名里有空格,提示是 「错误: --dir 不能包含空格(生成的服务单元会按空白把路径拆开,服务起不来)」。 换个目录再装。

消息推不出去

按「发送测试消息」回显的原始响应对号入座:

现象原因怎么修
HTTP Token 不正确连接页「机器人(NapCat 等)」卡上填的「HTTP Token」与 OneBot 实现里配的不一致两边核对,注意首尾空格
HTTP 连不上OneBot 实现没启动,或地址端口填错确认 NapCat 在跑;确认填的不是 Websocket 端口
HTTP 服务异常OneBot 实现自身状态异常重启 NapCat
账号 已掉线(HTTP 正常)QQ 登录态失效,接口照常返回 200,消息却谁也收不到去 NapCat 的界面重新扫码登录
接口返回成功但群里没有机器人不在该群,或群号填错确认机器人已入群;核对群号
WS 连不上(HTTP 正常)只影响接收群内事件,不影响推送不急着修;需要弹幕类插件时再处理

私聊收不到但群聊正常 — 在控制台里加的通道不会出这个问题;手写过 datasource.json 的, 看目标类型是不是写错了,见附录 B。

@全体成员没生效 — 要求机器人是群主或管理员,且每天有次数限制, 用完了会怎样见第 10 章。

直播或动态不推送

开播了但没推 — 依次确认:

  1. 「推送」页里这位主播下有通道,且通道「推送内容」里的「开播通知」开着(没开的收在「没开的 n 种」里)
  2. 首页健康自检里的「直播间连接」探针正常
  3. 首页「今日」卡上的按钮写的是「暂停全部推送」——写着「恢复推送」就说明推送被暂停了,点它恢复
  4. 当前不在静音时段内(设置页「静音时段 · 开始」「静音时段 · 结束」;这条通道在「通道规则」里单独设过的,看那一档)

自检若点出某几间连续多分钟没收到弹幕礼物、进房照常,会写出主播名和房间号。 先去那几间看一眼是否确有人在发弹幕;确有人发而这里一直没恢复,再重启试;仍不行再查登录账号。

动态不推但直播正常 — 动态需要登录态,直播不需要。看连接页「哔哩哔哩」卡的登录状态, 掉登录时重新扫码即可。

动态推送延迟很大 — 动态是轮询的,延迟取决于轮询间隔。间隔调太小容易触发风控,不建议。

推了但图片是空的 — 动态图片是本地绘制的,看日志里绘图相关的报错。 若字体文件缺失,图片会渲染异常。

群里的命令不响应

机器人对命令毫无反应 — 按可能性从高到低:

  1. 这个群没配推送。机器人只在已配置推送目标的群里应答,其他群一律沉默——这是有意的
  2. 命令被人关掉了。群主或管理员发过 禁用命令 xxx。查数据、提醒订阅这几类按主播关,推送页那位主播这条通道的「群内命令」能看能改;第三方插件的命令按群关,在群里发 启用命令 xxx 开回
  3. 不是以命令名开头。「我想看看直播间数据」不会触发,直播间数据 才会
  4. 3 秒冷却。同一个群刚响应过一条命令,三秒内不再回第二条

第 2 条最容易漏:不看那一页无从分辨「坏了」还是「被关了」,两者表现完全一样。

发「禁用命令」被拒 — 只有群主、群管理员,或设置页「超级管理员」 里列出的号能用。机器人的主人未必是每个群的管理员,把自己的 QQ 号填进那份名单即可, 它跨群生效。私聊里一律不算管理员。

带「总」的查法说「本机没开累计数据」 — 跨场次要另配累计存储。 设置页「累计数据 Redis · 地址」填上地址,填完即时生效,不用重启(见第 8 章)。

本场数据是对的,累计是 0 — 累计在下播那一刻才并入。这一场还没结束时, 累计里自然没有本场的份。

@全体成员 没发出来,正文倒是发了 — 机器人在那个群不是群主或管理员,或当日次数用完了。 这是程序主动摘掉的:QQ 服务端这一路不做客户端那道权限检查,硬发能成功, 但绕过权限强行 @ 全体有账号风控风险,代价是整条推送链路停摆。 摘掉之后发什么,看这个通道 @ 那一档选的是哪个:选「@全体成员,失败时改为 @订阅者」时, 有人订阅了提醒就改成 @ 这些人,没人订阅才只发正文(这一替补只有开播通知与动态通知有, 下播通知、下播报告、下播打赏播报不 @ 任何人);选「@全体成员」则只发正文。想让 @全体成员 照发,就把机器人 设为管理员。日志页时间线上那条「未 @ 全体」写明是哪一种原因、有没有改 @ 订阅的人; 次数上限与用完后的改法见第 10 章。

主播页的场次 / 趋势是空的 — 归档从 4.1.0 起才有,之前的直播不会追溯, 且只在下播时追加。播完一场之后就有内容了。若播完仍是空的, 看日志里有没有「没有记录到开播时间, 本场不归档」——程序在直播中途才启动时会这样, 一条没有起点的记录算不出时长,留着只会污染统计。

账号相关

过一段时间就掉登录 — 正常情况下凭据会自动续期、不该掉;怎么确认拿到的是可续期的凭据、 健康自检说「未取得刷新口令,无法自动续期」时怎么办,见第 13 章。

掉得比这频繁得多 — 那就不是凭据到期的问题:

  • 同一账号不要同时在多处登录 Web 端,会互相挤下线
  • 账号被风控时任何客户端都会掉,先去网页端确认账号状态正常
  • 日志里搜「登录态」,看复检是在哪一步判定失效的

每次重建容器都要重新扫码 — 卷没挂对。凭据写在工作目录下,卷必须挂在 /app, 挂到 /app/data 之类的子目录不会持久化任何东西。见第 3 章。

二维码扫不出来 — 终端里的字符画二维码受字体与终端宽度影响。 改用连接页的「哔哩哔哩」卡扫,那里是真图片。

想换账号 — 连接页「哔哩哔哩」卡点「退出登录并重新扫码」,再扫新账号的码。 或直接删掉 cookies.json 与 cookies.key 后重启。

内存与 CPU 占用

内存比文档写的高 — 先确认测量方式,再决定要不要调。怎么量才准、堆上限怎么改、 改了之后 systemd 的上限要跟着怎么调,都在附录 A。

CPU 占用高 — 通常来自直播间长连接的心跳与动态轮询。监听的主播多时属正常; 可在设置页关掉「直播间长连接」、改用备用推送来降低开销。

常见问答

要不要专门准备一个 QQ 小号? 要。发消息的是 OneBot 实现登录的那个 QQ, 用你的主号有被风控的风险。

哔哩哔哩账号必须登录吗? 只推直播的话不需要——直播状态是公开信息。 动态推送需要登录,不登录时弹幕礼物数据也会少得多。建议用小号,原因见第 2 章。

会不会被封号? 本项目只调用哔哩哔哩的公开接口,请求频率也做了限制。 但任何自动化都存在风控风险,所以建议用小号。

可以同时推给多个群吗? 可以。一位主播下可以加任意多个推送目标, 每个目标的事件与模板都能单独配置。

可以监听多位主播吗? 可以,数量没有硬限制。主播多时直播间长连接会更多, 内存与 CPU 会相应上升。

推送内容能自定义吗? 能。在「推送」页点开通道,「推送内容」里开着的那几种, 点那一行的「编辑」展开后就能改,占位符可拖可点,右侧实时预览。见第 6 章。

能不能只在特定时间推送? 设置页有「静音时段 · 开始」「静音时段 · 结束」, 支持跨零点(见第 10 章)。想临时全停就用首页「今日」卡的「暂停全部推送」,立即生效。

改了设置要重启吗? 看设置页的生效提醒:标「重启生效」的项、或整组提示需要重启的组,要重启; 其他项保存即起作用(见第 12 章)。首页的「暂停全部推送」按下即生效。

上游 StarBot 的插件能用吗? 不能直接装上就用,见附录 D。

升级会丢配置吗? 不会,application.yml 与 datasource.json 保留原样 (升级到金额显示、静音时段、命令开关按通道各设的版本时,程序会把旧设置抄进 datasource.json,抄之前先备份,见第 13 章); 从 5.3 及更早升上来要挪一下根键,见第 13 章。

直播数据会一直留着吗? 分三份。本场数据在下次开播时清零; 累计数据永久保留,但需要配置 Redis;每场的归档(sessions.jsonl)永久保留 且只增不删,是主播页总览及「直播场次」「趋势明细」抽屉的数据源。想做别的分析可以直接拿这个文件走, 每行一条 JSON。

能查历史某一场的详细数据吗? 主播页「趋势明细」抽屉按统计日/周/月汇总, 「直播场次」抽屉列出每一场、点进去看那一场的报告。单场的原始记录仍在 sessions.jsonl 里。 报告图不另存副本,控制台回看时按当场数据现画。

群成员的订阅名单、命令开关存在哪? 订阅名单在 state.json,可在控制台推送页那条通道的 「本群状态」里查看与清理;按主播的命令开关跟推送配置一起存在 datasource.json,在那条通道的「群内命令」里能看能改; 第三方插件的命令开关仍按群存在 state.json。state.json 与数据文件在同一目录,备数据时会一起带走 (见第 13 章)。绑定关系也存同一份文件, 但控制台不显示、也清不了——要清就直接编辑 state.json 里的 UserBindings 段。 先停服,改完再启动——程序只在启动时读这个文件,运行中会定时把内存里的整份状态 写回盘上,运行时改的会被下一次写回盖掉。

日志在哪? 控制台的「日志」页和从那里进的工程日志,见第 11 章。 程序起不来、控制台打不开时看启动输出:systemd 部署用 sudo journalctl -u novabot@<版本> -f,手动运行直接输出在终端。

怎么知道推送到底有没有成功? 推送页通道「本群状态」里有「本群最近推送」(好友通道为「该好友最近推送」,默认收着,点开看),涵盖推到该群或好友的所有主播 (本次启动以来各 5 条),列出时间、内容与结果;失败会写明原因。想看全部去日志页。

出了问题能主动通知我吗? 能,在设置页「告警」组配 QQ、Webhook、邮件三路里的至少一路, 见第 10 章。

日志里说「未推送心跳」? 是你的 OneBot 实现把心跳关了 (NapCat 在 onebot11_*.json 里的 heartInterval,设为 30000 即每 30 秒一次)。 机器人掉线靠心跳发现(见第 10 章), 关着的话这条线索不生效,掉线只能靠定期主动询问发现,最长要等一个检测周期(默认 5 分钟)。

和上游 StarBot 是什么关系? 见附录 D。

提问前请准备

自检和上面都没解决时,附上这些信息更容易得到帮助:

  1. 首页健康自检的截图
  2. 连接页「发送测试消息」后的原始响应
  3. 相关时间段的日志(记得抹掉口令、cookies、群号等敏感内容)
  4. NovaBot 版本与 OneBot 实现的名称版本

安全漏洞不要走公开渠道发,请按附录 C 说的方式私密报告。