Discord.js giveaway命令报reactions is not defined错误如何修复
错误原因
- 核心报错
reactions is not defined是作用域问题导致的:你在setTimeout回调中直接访问reactions变量,但该变量从未被声明赋值。你发送抽奖embed后拿到的sentEmbed消息实例仅在.then()回调的作用域内生效,回调外部无法访问,自然拿不到消息上的反应数据。 - 代码还存在多个会导致运行异常的隐性问题:
- 分割后的参数没有做去空格处理,用户输入逗号后加空格就会导致时间单位匹配失败,
duration变成undefined,setTimeout会直接按0延迟执行开奖逻辑。 - 中奖逻辑完全失效:定义的
winner是未被调用的函数,且属性名书写错误(消息对象上是reactions不是reaction),就算解决作用域问题也无法正常抽取获奖者。 - 参数索引硬编码且无校验:如果用户未@指定频道、奖品名称带逗号、输入的时长/中奖人数不是合法数字,都会导致逻辑错乱或直接报错。
- 未处理多获奖者场景:代码写死抽取1位获奖者,和传入的
winners参数不匹配。 - 反应数据未强制拉取:Discord.js的缓存可能不会存储所有参与用户,直接读缓存会出现漏统计参与人的问题。
- 分割后的参数没有做去空格处理,用户输入逗号后加空格就会导致时间单位匹配失败,
可落地修复方案
- 将开奖相关的
setTimeout逻辑移动到发送embed的.then()回调内部,直接通过sentEmbed实例访问消息反应数据,解决作用域问题。 - 对逗号分割后的所有参数做去空格、转小写处理,避免空格、大小写导致的匹配失败。
- 增加前置参数校验:如果时长、时间单位、中奖人数、奖品不符合要求,直接返回命令格式提示,终止执行。
- 修正开奖逻辑:开奖前先拉取全量反应用户列表,过滤机器人账号,判断有效参与人数是否满足开奖要求,再按设置的中奖人数随机抽取获奖者。
- 统一频道逻辑:如果用户@了指定频道就把抽奖消息发到对应频道,未@则默认使用当前执行命令的频道,避免开奖消息发错位置。
修复后完整代码
if (command === "giveaway") { // 分割参数并统一去除前后空格 const argsList = args.join(` `).split(`,`).map(item => item.trim()); const time = Number(argsList[0]); const type = argsList[1]?.toLowerCase(); const winnersCount = Number(argsList[2]); const targetChannel = message.mentions.channels.first() || message.channel; // 处理奖品:如果@了频道,奖品从第4位开始取,否则从第3位开始取,支持奖品带逗号 const prize = message.mentions.channels.first() ? argsList.slice(4).join(',').trim() : argsList.slice(3).join(',').trim(); // 前置参数校验 if (!time || time <=0) return message.reply("请输入合法的抽奖时长数字"); if (!["m","minute","minutes","h","hour","hours","d","day","days"].includes(type)) { return message.reply("时间单位仅支持 m(分钟)/h(小时)/d(天)"); } if (!winnersCount || winnersCount <=0 || !Number.isInteger(winnersCount)) { return message.reply("请输入合法的中奖人数(正整数)"); } if (!prize) return message.reply("请填写抽奖奖品内容"); // 计算抽奖时长 let duration; if (type === "m" || type === "minute" || type === "minutes") { duration = time * 60000; } if (type === "h" || type === "hour" || type === "hours") { duration = time * 3600000; } if (type === "d" || type === "day" || type === "days") { duration = time * 86400000; } const embed = new Discord.MessageEmbed() .setTitle("🎉 新抽奖活动") .setColor(`RANDOM`) .addFields( { name: "活动时长", value: `${time} ${type}`, inline: true }, { name: "中奖人数", value: `${winnersCount}`, inline: true }, { name: "奖品", value: `${prize}`, inline: true }, ) .setDescription(`点击 🎉 表情参与抽奖\n活动发起人:${message.author.username}`) // 发送抽奖消息到目标频道 targetChannel.send(embed).then(sentEmbed => { sentEmbed.react("🎉"); // 定时器移到回调内部,可直接访问sentEmbed实例 setTimeout(async () => { // 强制拉取最新的反应用户数据,避免缓存不全 const reaction = sentEmbed.reactions.cache.get("🎉"); await reaction.users.fetch(); // 过滤机器人账号,拿到有效参与人列表 const validUsers = reaction.users.cache.filter(u => !u.bot); if (validUsers.size < winnersCount) { return targetChannel.send(`参与人数不足,无法抽取${winnersCount}位获奖者,本次抽奖作废`); } // 随机抽取指定数量的获奖者 const winnerList = validUsers.random(winnersCount); const winnerText = winnerList.map(u => u.toString()).join(", "); targetChannel.send(`恭喜 ${winnerText} 获得奖品:**${prize}**!`); }, duration); }); }
命令使用格式
- 发到当前频道:
!giveaway 30,m,1,50元京东卡(30分钟后开奖,抽1人,奖品是50元京东卡) - 发到指定频道:
!giveaway 1,h,2,#抽奖频道,Switch游戏卡带(1小时后开奖,在#抽奖频道抽2人,奖品是Switch游戏卡带)
内容的提问来源于stack exchange,提问作者ZeidHassan
相关产品推荐
相关产品推荐

