discord.js使用embed反应翻页时触发DiscordAPIError: Unknown Message报错
问题原因
你遇到的报错核心是全局事件监听器重复注册,具体逻辑如下:
- 每次调用
movieChoiceEmbed函数,都会通过client.on("messageReactionAdd")给客户端新增一个独立的反应事件监听器,而非覆盖之前的监听器。 - 第一次调用函数时注册1个监听器,第一次按下反应后监听器正常执行删除消息,随后再次调用
movieChoiceEmbed,此时会新增第2个监听器,总共有2个监听器同时生效。 - 第二次按下反应时,2个监听器会同时触发:第一个监听器执行时正常删除消息,第二个监听器执行时该消息已经被删除,请求Discord接口删除不存在的消息就会抛出
Unknown Message错误。 - 更关键的是,就算
await reaction.message.delete()执行报错,后面修改count、重新调用movieChoiceEmbed的逻辑依然会执行,所以每次按下反应会新增和当前监听器数量相同的新监听器,总监听器数量每次翻倍,报错数量也会同步翻倍。 - 你尝试删除频道最新消息依然报错,也是因为多个监听器几乎同时执行,第一个监听器已经把最新的embed消息删掉了,后续监听器拿到的
lastMessage要么不存在要么不是目标消息,依然会触发接口报错。
解决方案
有两种常用的修复方案:
方案1:全局仅注册一次事件监听器(也就是你排查到的解法)
把messageReactionAdd监听逻辑移到movieChoiceEmbed函数外面,全局只注册一次,避免重复叠加。需要额外做消息校验,避免误处理其他消息的反应,示例逻辑:
// 外部存储分页状态 let currentPageCount = 0; let targetMovies = null; let triggerMessage = null; // 全局只注册一次监听 client.on("messageReactionAdd", async (reaction, user) => { if(user.bot) return; // 只处理分页embed的反应,可根据embed标题、消息ID等判断 if(reaction.message.embeds[0]?.title !== 'Choose movie') return; if(!["⏪", "⏩"].includes(reaction.emoji.name)) return; await reaction.message.delete(); if(reaction.emoji.name === "⏪") currentPageCount -=4; else currentPageCount +=4; movieChoiceEmbed(targetMovies, triggerMessage, currentPageCount); }); // 分页函数里不再注册监听器 function movieChoiceEmbed(movies, message, count) { targetMovies = movies; triggerMessage = message; currentPageCount = count; // 原有发送embed、加反应的逻辑不变,去掉内部的client.on代码 }
方案2:使用单条消息的反应收集器(更推荐)
不需要全局监听,只给当前发送的embed消息绑定专属的反应收集器,处理完自动销毁,不会产生监听器叠加的问题,示例修改:
function movieChoiceEmbed(movies, message, count) { const pagecount = count; message.channel.send({ embed: { // 原有embed配置不变,这里省略 } }).then(async sentEmbed => { await sentEmbed.react("⏪"); await sentEmbed.react("⏩"); // 创建仅作用于当前这条embed的反应收集器 const collector = sentEmbed.createReactionCollector( (reaction, user) => !user.bot && ["⏪", "⏩"].includes(reaction.emoji.name), { max: 1 } // 只收集一次反应就自动销毁 ); collector.on("collect", async reaction => { await sentEmbed.delete(); const newCount = reaction.emoji.name === "⏪" ? count -4 : count +4; movieChoiceEmbed(movies, message, newCount); }); }).catch(console.error); // 删掉原来的client.on监听代码 }
为什么把监听器移到函数外可以解决问题
把messageReactionAdd移到函数外部后,整个程序生命周期内只会注册一次事件监听器,不管你调用多少次分页函数都不会新增监听器。每次按下反应只会有一个监听器执行,不会出现多个监听器同时操作同一条已删除消息的情况,自然就不会触发报错。
但这个方案要注意做好消息过滤,否则其他消息的点击反应也可能触发分页逻辑,造成不必要的异常。
内容的提问来源于stack exchange,提问作者ProfVoltorb
相关产品推荐
相关产品推荐

