Discord.js:channels.fetch()返回过期数据的技术求助
问题描述
每分钟获取服务器中当前在任意语音频道的所有成员,但channels.fetch()始终返回client.login时刻的旧数据,而非实时数据。尝试过使用channels.fetch()、channels.cache,调整客户端的不同缓存选项,在函数顶部重新获取频道以更新缓存,但均无效。
原代码
import { getConfig } from '../config/config'; import { client } from '../client'; import { CronJob } from 'cron'; import { EmbedBuilder, GuildMember, TextChannel } from 'discord.js'; const checkerVoice = async () => { const guild = await client.guilds.fetch(getConfig().guild); const log = (await guild.channels.fetch(getConfig().channels.text.log.debug)) as TextChannel; const channels = await guild.channels.fetch(); const voices = channels.filter( (channel) => channel.type === 2 && channel.id !== getConfig().channels.voice.afk, ); voices.forEach(async (channel) => { const voice = await guild.channels.fetch(channel.id); const members = voice.members as unknown as GuildMember[]; for (const member of members) { log.send({ embeds: [new EmbedBuilder().setDescription(`${channel.id}@${member}}`)], }); } }); }; export const voiceCron = new CronJob('* * * * *', checkerVoice);
解决方案
核心问题
guild.channels.fetch()默认只获取频道基础信息,不会同步语音频道内的成员状态。Discord.js中语音频道的成员列表依赖网关事件同步,如果客户端未正确接收VOICE_STATE_UPDATE事件,缓存内的成员数据就不会实时更新。
修复步骤
开启必要的特权意图
在Discord开发者门户的应用页面,开启SERVER MEMBERS INTENT和GUILD VOICE STATES特权意图,同时在客户端初始化时配置:const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates, // 必须开启此意图 GatewayIntentBits.GuildMembers // 需获取成员详细信息时开启 ] });直接使用缓存获取实时成员
频道缓存会通过网关事件自动更新,无需重复fetch频道。修改checkerVoice函数:const checkerVoice = async () => { const guild = client.guilds.cache.get(getConfig().guild); if (!guild) return; const logChannel = guild.channels.cache.get(getConfig().channels.text.log.debug) as TextChannel; if (!logChannel) return; // 从缓存过滤有效语音频道 const voiceChannels = guild.channels.cache.filter( channel => channel.type === ChannelType.GuildVoice && channel.id !== getConfig().channels.voice.afk ); // 遍历频道与成员,发送日志 for (const channel of voiceChannels.values()) { for (const member of channel.members.values()) { await logChannel.send({ embeds: [new EmbedBuilder().setDescription(`${channel.id}@${member.user.tag}`)] }); } } };修正异步遍历问题
原代码中forEach结合async/await会导致异步操作无序执行,改用for...of可保证执行顺序,同时避免未处理的Promise问题。
关键说明
- 只要客户端正确接收
GUILD_VOICE_STATES意图,语音频道的members缓存会实时更新,无需手动fetch频道同步成员数据。 - 若需强制刷新频道基础信息,可在fetch时添加
force: true参数,但这仍不会同步成员列表,成员数据始终依赖网关事件。
内容的提问来源于stack exchange,提问作者waseeen
相关产品推荐
相关产品推荐

