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

Discord.js中Bot被移动/断开时voiceStateUpdate未更新语音频道问题

Discord.js 语音状态更新异常问题修复

问题现象

原本通过voiceStateUpdate事件实现的功能:Bot单独留在语音频道5分钟后自动断开并销毁播放器,近期出现以下异常:

  • 管理员断开Bot后,Bot仍持续发送播放新歌提示
  • 拖动Bot到新频道时,语音频道ID未更新
  • 断开Bot时,日志显示Bot"移动"频道,但语音频道ID保持不变

控制台日志

User join: 
null - Start Channel ID
1025940884321206302 - End Channel ID
Bot join: null - Start Channel ID
1025940884321206302 - End Channel ID
User moved: 
1025940884321206302 - Start Channel ID
1026191047937773690 - End Channel ID
Bot moved: 
1025940884321206302 - Start Channel ID
1025940884321206302 - End Channel ID
User moved: 
1026191047937773690 - Start Channel ID
1025940884321206302 - End Channel ID
Bot moved: 
1025940884321206302 - Start Channel ID
1025940884321206302 - End Channel ID
User left: 
1025940884321206302 - Start Channel ID
null - End Channel ID
Bot moved: 
1025940884321206302 - Start Channel ID
1025940884321206302 - End Channel ID

对应操作流程

User joins VC
Bot joins VC
User moves VC
Bot dragged into VC (first error, no voice channel ID change)
User moves VC
Bot dragged into VC (second error, no voice channel ID change)
User disconnects VC
Bot disconnected from VC (third error, End Channel ID should be null)

问题根源与修复方案

1. channelFind函数返回值错误

原函数使用filter返回数组,但代码中直接当作对象调用users属性,数组无此属性导致逻辑失效。修改为find返回单个对象:

//Find voice channel in voiceChannels array
function channelFind(channelID) {
    return voiceChannels.find(channel => channel.channelID === channelID)
}

2. Bot状态更新判断逻辑错误

管理员拖动/断开Bot时,voiceStateUpdate的startChannel和endChannel可能不符合预期,需直接通过Bot自身的状态判断。修改移动分支逻辑:

// 替换原Bot移动的分支代码
else if (startChannel && endChannel) {                        //If user moves
    if (voiceStateEnd.id === client.user.id) { // 直接判断是Bot的状态更新
        const player = client.music.players.get(guildID);       
        if (player && !player.playing)                          
            player.pause(false); // 原代码逻辑错误,pause(false)才是恢复播放
        // 清理旧频道记录
        if (channelFind(startChannel)) {                        
            channelsSplice(startChannel);                       
        }
        // 添加新频道记录(避免重复添加)
        if (!channelFind(endChannel)) {
            voiceChannels.push({ channelID: endChannel, users: getSize(client, endChannel) })
        }
        console.log(`Bot moved: ${startChannel} -> ${endChannel}`);
    } else {
        console.log(`User moved: ${startChannel} -> ${endChannel}`);
        const startChan = channelFind(startChannel);
        if (startChan) startChan.users = getSize(client, startChannel);
        const endChan = channelFind(endChannel);
        if (endChan) endChan.users = getSize(client, endChannel);
    }
}

3. disconnect函数guildID未定义

原函数未传入guildID,导致无法获取播放器和语音连接。修改函数参数及调用:

//Disconnect bot and player
function disconnect(client, channelID, guildID) {
    const channel = channelFind(channelID);
    if (channel && channel.users <= 1) {
        const player = client.music.players.get(guildID);
        if (player) {
            player.destroy();
        } else {
            const connection = getVoiceConnection(guildID);
            if (connection) connection.disconnect();
        }
        channelsSplice(channelID);
    }
}

调用时补充guildID参数:

// 在checkDisconnect中修改调用
disconnect(client, channelID, guildID);
// 在Bot离开的分支中修改调用
disconnect(client, startChannel, guildID);

4. 重复创建定时器问题

checkUsers每次事件触发都会遍历频道创建定时器,导致同一频道多个定时器冲突。可维护一个定时器映射表避免重复:

// 新增全局变量存储定时器
const disconnectTimers = new Map();

//Recheck after interval for 0 users, if user joins back, restart time, else disconnect bot and player
function checkDisconnect(client, channelID, guildID) {
    // 清除已有定时器
    if (disconnectTimers.has(channelID)) {
        clearTimeout(disconnectTimers.get(channelID).timeout);
        clearInterval(disconnectTimers.get(channelID).interval);
    }

    let disconnectChannel = setTimeout(() => {
        clearInterval(intervalCheck);
        disconnect(client, channelID, guildID);
        disconnectTimers.delete(channelID);
    }, 300000); //5 minutes == 300000
    let intervalCheck = setInterval(() => {
        var size = getSize(client, channelID)
        if (size > 1) {
            clearInterval(intervalCheck);
            clearTimeout(disconnectChannel)
            disconnectTimers.delete(channelID);
        }
    }, 1000);

    disconnectTimers.set(channelID, { timeout: disconnectChannel, interval: intervalCheck });
}

内容的提问来源于stack exchange,提问作者ComicallyBad

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 00:40:27