升级discord.js v13后awaitReactions过滤器不生效问题
过滤器失效根因
- 核心配置缺失:discord.js v13及以上版本要求显式开启
GuildMessageReactions网关意图,同时配置Reaction、User类部分结构(Partial)解析,否则客户端无法获取完整的反应事件、操作人信息,传入filter的参数为残缺占位对象,过滤逻辑完全不生效,会直接放行缓存内已存在的反应(包括机器人自己添加的反应)。 - 逻辑漏洞:原代码未在filter中排除机器人账号操作,靠3秒延迟规避自反应属于治标不治本;收集到反应后没有做二次校验,直接走分支判断,导致绕过filter的非法表情、非授权用户操作都会触发业务逻辑。
- 语法问题:原代码回调中引用的
user、reaction变量未定义,属于作用域错误,会导致逻辑判断异常;同时未给反应监听设置超时时间,会造成内存泄漏;旧版delete({timeout: xxx})写法在新版discord.js中已废弃,会直接报错。
前置配置修复
首先在客户端初始化时补全必需的意图和部分结构配置,否则过滤逻辑永远无法正常生效:
const { Client, GatewayIntentBits, Partials } = require('discord.js'); const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.GuildMessageReactions, // 必须开启,否则收不到用户的反应事件 GatewayIntentBits.MessageContent ], partials: [ Partials.Message, Partials.Channel, Partials.Reaction, // 必须配置,否则无法处理非缓存的反应 Partials.User // 必须配置,否则无法拿到非缓存的用户信息 ] });
修复后的业务代码
getRedCardChannel.send({ embeds: [cardEmbed] }) .then(async message => { console.log('Message sent to Channel'); // 等待两个反应添加完成后再启动监听,从流程上避免把自加反应纳入收集范围 await message.react('✅'); await message.react('❌'); console.log('Confirmation buttons added'); const confirmFilter = (reaction, user) => { // 按优先级过滤:先排除所有机器人操作,再校验表情合法性,最后校验用户权限 if (user.bot) return false; if (!['✅', '❌'].includes(reaction.emoji.name)) return false; return user.id === getAdminId || user.id === getApproverId; }; message.awaitReactions({ filter: confirmFilter, max: 1, time: 15 * 60 * 1000, // 设置15分钟审批超时,避免监听器永久驻留 errors: ['time'] }) .then(collected => { const approvalReaction = collected.first(); // 二次兜底校验,防止异常场景下非法反应绕过filter if (!['✅', '❌'].includes(approvalReaction.emoji.name)) return; const operator = approvalReaction.users.cache.last(); if (operator.bot || (operator.id !== getAdminId && operator.id !== getApproverId)) return; if (approvalReaction.emoji.name === '✅') { message.react('🟥'); console.log('Approved'); console.log(`${message.author.tag}'s message "${message.content}" gained a confirmed red card!`); } else { console.log('Rejected'); // 替换已废弃的timeout参数写法 setTimeout(() => { message.delete() .then(() => { getRedCardChannel.send('Provisional card removed') .then(deleteMessage => { setTimeout(() => deleteMessage.delete(), 3000); }); }); }, 1000); } }) .catch(() => { console.log('Approval timed out'); }); });
修复点说明
- 移除了不必要的3秒延迟:通过
await确保机器人自身的反应添加完成后再启动监听,同时在filter第一层直接拦截所有机器人账号的操作,彻底解决自反应误触发问题。 - 优化filter判断顺序:按照「排除机器人→校验表情→校验权限」的顺序过滤,不符合任意条件的反应直接拦截,不会进入收集队列。
- 增加二次兜底校验:在拿到收集到的反应后再次校验表情和操作人权限,哪怕出现缓存异常导致非法反应绕过filter,也不会触发后续审批逻辑。
- 修复语法兼容问题:替换了新版discord.js已废弃的
delete超时参数写法,修正了未定义变量的作用域错误,增加监听超时逻辑避免内存泄漏。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

