Discord.js从反应用户数组随机抽取返回undefined问题
Discord机器人抽奖返回undefined的问题修复
问题根源分析
你的代码存在几个核心问题,导致抽奖结果返回undefined:
- 同步循环与异步延迟冲突:
delay(1000)未使用await,导致for循环瞬间执行完毕,直接触发结束逻辑时,reactionUsers可能还未收集到任何用户。 - 用户去重逻辑失效:直接用
user === reactionUsers[i]比较对象引用,Discord的User对象每次获取都是新实例,无法正确判断重复。 - 随机索引越界风险:
Math.ceil(Math.random() * reactionUsers.length)在数组为空时会得到0,reactionUsers[0-1]即reactionUsers[-1]会返回undefined;数组非空时也可能取到等于数组长度的索引,同样导致越界。 - 倒计时变量冲突:直接修改
timeRemaining会和反应收集器的time参数冲突,同步循环无法实现正确的倒计时更新。
修正后的代码
const delay = ms => new Promise(res => setTimeout(res, ms)); const collector = giveawayMessage.createReactionCollector(filter, { time: timeRemaining }); const reactionUserIds = new Set(); // 用Set自动去重,存储用户ID避免对象引用问题 // 初始添加👍反应 await giveawayMessage.react('👍'); // 收集有效参与者(排除机器人) collector.on('collect', (reaction, user) => { if (reaction.emoji.name === '👍' && !user.bot) { reactionUserIds.add(user.id); } }); // 异步更新倒计时 async function updateCountdown() { let remaining = timeRemaining; while (remaining > 0) { await delay(1000); remaining--; // 计算格式化剩余时间 const numDays = Math.trunc(remaining / 86400); const numHours = Math.trunc((remaining - numDays * 86400) / 3600); const numMinutes = Math.trunc((remaining - numDays * 86400 - numHours * 3600) / 60); const numSeconds = remaining - numDays * 86400 - numHours * 3600 - numMinutes * 60; const timeFormatted = `${numDays}d ${numHours}h ${numMinutes}m ${numSeconds}s`; // 更新消息embed const updatedEmbed = EmbedBuilder.from(embed) .setFields( { name: 'Prize', value: `${interaction.options.getString('prize')}` }, { name: 'Time Remaining', value: timeFormatted } ); await giveawayMessage.edit({ embeds: [updatedEmbed] }); } } // 等待收集器结束和倒计时完成 await Promise.all([collector.stop(), updateCountdown()]); // 执行抽奖逻辑 const userIdList = Array.from(reactionUserIds); if (userIdList.length === 0) { // 无有效参与者的情况 const finalEmbed = EmbedBuilder.from(embed) .addFields({ name: 'Winner', value: 'No valid participants!' }); await giveawayMessage.edit({ embeds: [finalEmbed] }); return; } // 安全生成随机索引 const randomIndex = Math.floor(Math.random() * userIdList.length); const winnerId = userIdList[randomIndex]; const winner = await interaction.guild.members.fetch(winnerId); // 获取完整用户对象 // 更新结果embed const finalEmbed = EmbedBuilder.from(embed) .addFields({ name: 'Winner', value: `${winner}` }); await giveawayMessage.edit({ embeds: [finalEmbed] });
关键改进点
- 用Set自动去重:通过存储用户ID替代User对象,彻底解决对象引用比较失效的问题。
- 异步倒计时:使用
async/await确保每次延迟1秒后再更新倒计时,避免循环瞬间执行完毕。 - 安全随机索引:
Math.floor(Math.random() * 数组长度)确保索引始终在0到数组长度-1的范围内,不会越界。 - 分离逻辑:用
Promise.all等待反应收集和倒计时完成,确保所有参与者都被收集后再抽奖。 - 边界处理:增加无参与者的情况判断,避免返回
undefined。
内容的提问来源于stack exchange,提问作者RedstoniaYT
相关产品推荐
相关产品推荐

