Discord.js分片机器人随机重置初始状态问题排查
Discord.js机器人状态随机重置为"Starting..."的问题排查与解决
问题概述
基于Node.js开发的Discord.js机器人,通过discord-hybrid-sharding库启动。初始化时将机器人状态设为"Starting...",并在ClientReady事件中更新为显示服务器数量的运行状态。但机器人会随机恢复到初始"Starting..."状态,功能却完全正常。重启PM2进程后状态可正常显示一段时间,之后又会随机重置,且定时器中的状态重置检查未生效。
相关代码
分片管理器(app.js)
import { ClusterManager } from "discord-hybrid-sharding"; import { config } from "../config/config.js"; const manager = new ClusterManager("./src/bot.js", { totalShards: "auto", shardsPerClusters: 2, token: config.discord.bot_token, }); manager.on("clusterCreate", shard => console.log(`Launched shard ${shard.id}`)); manager.spawn({ timeout: -1 });
机器人核心逻辑(bot.js)
import { ClusterClient, getInfo } from "discord-hybrid-sharding"; import DiscordClient from "discord.js"; const client = new DiscordClient({ presence: { status: "dnd", activities: [{ name: "Starting...", type: ActivityType.Playing }], }, shards: getInfo().SHARD_LIST, shardCount: getInfo().TOTAL_SHARDS, }); client.cluster = new ClusterClient(client); client.on(Events.ClientReady, async() => { const guildCount = await client.guilds.fetch().then(guilds => guilds.size); client.user?.setActivity({ name: `Counting on ${guildCount} servers!`, type: ActivityType.Playing }); let lastGuildCount = guildCount; setInterval(async() => { const newGuildCount = await client.guilds.fetch().then(guilds => guilds.size); const statusHasReset = client.user?.presence.activities[0].name === "Starting..."; if (newGuildCount !== lastGuildCount || statusHasReset){ lastGuildCount = newGuildCount; client.user?.setActivity({ name: `Counting on ${newGuildCount} servers!`, type: ActivityType.Playing }); } }, 45 * 60 * 1000); client.user?.setStatus("online"); });
原因分析
- 重连/分片重启未触发状态更新:Discord.js的
ClientReady事件仅在客户端首次登录成功时触发,当分片因网络波动或意外断开重连时,会重新应用初始化时的presence配置(即"Starting..."),但不会再次触发ClientReady,导致状态未更新。 - 定时器在分片重启后丢失:如果分片崩溃重启,原有的定时器会被销毁,新启动的分片未重新创建定时器,状态检查逻辑失效,初始状态持续保留。
- 异步操作无错误处理:
client.guilds.fetch()等异步操作未捕获错误,一旦请求失败会导致定时器回调中断,后续状态检查无法执行。 - 状态检查逻辑存在漏洞:直接访问
client.user?.presence.activities[0]可能因activities为空或顺序变化导致判断失效,无法检测到状态重置。
解决方案
1. 监听重连/分片就绪事件,补充状态更新
Discord.js提供Resumed(重连成功)和ShardReady(分片就绪)事件,在这些事件中重新执行状态更新逻辑,避免仅依赖ClientReady。
2. 封装状态更新逻辑,复用代码
将获取服务器数量、设置状态的逻辑封装为独立函数,在多个事件和定时器中复用,确保状态更新逻辑一致。
3. 修复定时器与错误处理
确保分片重启后能重新创建定时器,并在所有异步操作中添加错误捕获,避免回调中断。
修改后的bot.js代码示例
import { ClusterClient, getInfo } from "discord-hybrid-sharding"; import { Client, Events, ActivityType } from "discord.js"; const client = new Client({ presence: { status: "dnd", activities: [{ name: "Starting...", type: ActivityType.Playing }], }, shards: getInfo().SHARD_LIST, shardCount: getInfo().TOTAL_SHARDS, }); client.cluster = new ClusterClient(client); // 封装状态更新函数,添加错误捕获 async function updateBotStatus() { try { const guilds = await client.guilds.fetch(); const guildCount = guilds.size; await client.user?.setActivity({ name: `Counting on ${guildCount} servers!`, type: ActivityType.Playing }); await client.user?.setStatus("online"); return guildCount; } catch (err) { console.error("更新状态失败:", err); return null; } } // 初始化状态检查定时器 function setupStatusTimer(initialCount) { let lastGuildCount = initialCount; setInterval(async () => { try { const guilds = await client.guilds.fetch(); const newGuildCount = guilds.size; const currentActivity = client.user?.presence.activities[0]; // 安全判断状态是否重置 const statusHasReset = currentActivity ? currentActivity.name === "Starting..." : false; if (newGuildCount !== lastGuildCount || statusHasReset) { lastGuildCount = newGuildCount; await updateBotStatus(); } } catch (err) { console.error("定时器状态检查失败:", err); } }, 45 * 60 * 1000); } // 首次登录就绪时初始化 client.on(Events.ClientReady, async () => { const initialCount = await updateBotStatus(); if (initialCount !== null) setupStatusTimer(initialCount); }); // 重连成功时更新状态 client.on(Events.Resumed, async () => { await updateBotStatus(); }); // 分片就绪时更新状态 client.on(Events.ShardReady, async (shardId) => { console.log(`分片 ${shardId} 就绪`); await updateBotStatus(); });
优化分片管理器(app.js),监控集群状态
添加集群退出与就绪的日志监控,便于排查分片崩溃问题:
import { ClusterManager } from "discord-hybrid-sharding"; import { config } from "../config/config.js"; const manager = new ClusterManager("./src/bot.js", { totalShards: "auto", shardsPerClusters: 2, token: config.discord.bot_token, }); manager.on("clusterCreate", cluster => { console.log(`启动集群 ${cluster.id}`); // 监控集群退出,自动重启 cluster.on("exit", (code, signal) => { console.warn(`集群 ${cluster.id} 退出,代码: ${code},信号: ${signal},正在重启...`); cluster.restart(); }); // 监控集群就绪 cluster.on("ready", () => { console.log(`集群 ${cluster.id} 准备完成`); }); }); manager.spawn({ timeout: -1 });
内容的提问来源于stack exchange,提问作者NullDev
相关产品推荐
相关产品推荐

