You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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");
});

原因分析

  1. 重连/分片重启未触发状态更新:Discord.js的ClientReady事件仅在客户端首次登录成功时触发,当分片因网络波动或意外断开重连时,会重新应用初始化时的presence配置(即"Starting..."),但不会再次触发ClientReady,导致状态未更新。
  2. 定时器在分片重启后丢失:如果分片崩溃重启,原有的定时器会被销毁,新启动的分片未重新创建定时器,状态检查逻辑失效,初始状态持续保留。
  3. 异步操作无错误处理:client.guilds.fetch()等异步操作未捕获错误,一旦请求失败会导致定时器回调中断,后续状态检查无法执行。
  4. 状态检查逻辑存在漏洞:直接访问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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 17:32:01