Discord机器人重启后排行榜重置仅显示缓存用户的解决方法
问题根因
你的排行榜数据本身持久化存储在UserModel对应的数据库中,机器人重启不会造成数据丢失。出现“仅展示重启后使用过机器人的用户”问题,是现有代码的两处逻辑缺陷导致的:
- 筛选服务器有效成员时,仅判断用户ID是否存在于
message.guild.members.cache本地缓存中。Discord.js默认不会在启动时预加载全服务器成员到缓存,只有触发过交互、发过消息的用户才会进入缓存,因此刚重启时缓存数据不全 - 生成排行榜描述的循环中,只要遇到一个不在缓存的上榜用户,就直接执行
return终止整个渲染逻辑,直接导致后续内容无法正常输出
修复方案
前置配置
首先需要开启成员拉取权限:
- 打开Discord开发者后台,进入你的机器人应用的「Bot」配置页
- 在Privileged Gateway Intents分组下,打开
Server Members Intent开关并保存 - 在机器人客户端初始化代码中,添加
GuildMembers到intents配置列表,确保有权限拉取全量服务器成员
代码修正
核心改动点:
- 进入排行榜逻辑后,先主动拉取当前服务器全量成员到缓存,不依赖启动时的默认缓存
- 用
Set结构做成员存在性判断,提升大服务器下的运行性能 - 移除循环中遇到不存在用户就直接终止逻辑的写法,对已退出服务器的用户做降级展示
- 初始化描述变量为空字符串,解决你提到的内容开头出现
undefined的问题
修正后的完整核心代码如下:
// 主动拉取当前服务器全量成员,强制刷新本地缓存 await message.guild.members.fetch(); const allUserData = await UserModel.find({}); const validMembers = []; // 用Set存储服务器成员ID,存在性判断性能远高于数组includes const guildMemberIdSet = new Set(message.guild.members.cache.map(member => member.id)); for (const userData of allUserData) { if (guildMemberIdSet.has(userData.ID)) validMembers.push(userData); } const rankEmbed = new MessageEmbed() .setTitle(`${message.guild.name}'s Leaderboard`) .setFooter({ text: `You aren't ranked yet!`, iconURL: message.author.displayAvatarURL({ dynamic: true }), }); // 按余额降序排序,过滤余额为0的用户 const sortedMembers = validMembers .sort((a, b) => b.Money - a.Money) .filter(user => user.Money > 0); // 计算当前用户排名 let currentUserRank = 0; for (let i = 0; i < sortedMembers.length; i++) { if (sortedMembers[i].ID === message.author.id) { currentUserRank = i + 1; rankEmbed.setFooter({ text: `You are ranked ${currentUserRank} in the leaderboard!`, iconURL: message.author.displayAvatarURL({ dynamic: true }), }); break; } } // 取前10名生成排行榜描述 const top10Members = sortedMembers.slice(0, 10); let description = ''; for (let i = 0; i < top10Members.length; i++) { const memberData = top10Members[i]; const guildMember = message.guild.members.cache.get(memberData.ID); const balance = Math.round(memberData.Money); // 对已退出服务器的用户做降级展示,不中断整体渲染 const displayName = guildMember ? guildMember.user.tag : `已退出用户(ID: ${memberData.ID})`; description += `${i + 1}. ${displayName} - $${balance}\n`; } rankEmbed.setDescription(description); rankEmbed.setColor("RED"); message.reply({ embeds: [rankEmbed] });
额外优化说明
如果你的服务器成员规模超过10万,不建议每次调用排行榜都全量拉取成员,可以在用户每次触发指令、发送消息时主动更新对应成员的缓存,或者单独维护一份成员ID-用户名的映射表存在数据库中,进一步降低接口请求耗时。
内容的提问来源于stack exchange,提问作者FSCYT
相关产品推荐
相关产品推荐

