Discord机器人按钮二次点击报Unknown interaction错误如何解决
问题根因
你的错误完全是interactionCreate事件重复注册导致的:
- 你把
client.on('interactionCreate')的监听逻辑写在了发送播放控制消息的代码块内,每次触发发送控制消息的逻辑,都会给客户端新增一个完全相同的交互监听器,不会覆盖旧的。 - 点击按钮时,所有已注册的监听器会同时触发执行:第一个监听器调用
deferUpdate后,Discord端的交互就已经被标记为已处理,第二个监听器再尝试操作同一个交互,就会抛出Unknown interaction错误,这也和你打印两次日志的现象完全吻合。 - 消息销毁时你没有手动清理冗余的监听器,只会导致重复注册的问题越来越严重,点击按钮触发的监听器数量会越来越多。
解决方案
方案1:全局仅注册一次interactionCreate监听器
把交互处理逻辑移到机器人初始化的代码块中,全程只注册一次,不要嵌套在发送消息、创建队列的逻辑内。
如果需要区分不同队列/不同消息的按钮交互,可以给按钮的customId加上唯一标识前缀,比如pause_${queueId},处理时拆分ID匹配对应队列即可:
// 机器人启动时仅执行一次的初始化逻辑中注册监听器 this.client.on('interactionCreate', async interaction => { if (!interaction.isButton()) return; // 拆分自定义ID,匹配对应操作和队列 const [action, queueId] = interaction.customId.split('_'); const player = getPlayerByQueueId(queueId); // 自己实现根据ID拿播放器实例的逻辑 if (!player) return; try { if (action === 'PauseButton') { player.pause(true); await interaction.deferUpdate(); await interaction.editReply({ components: [ResumeButtons] }); } else if (action === 'ResumeButton') { player.pause(false); await interaction.deferUpdate(); await interaction.editReply({ components: [Buttons] }); } } catch (err) { // 捕获交互过期、消息已删除等异常,避免程序崩溃 console.error('交互处理失败', err); } });
如果你需要过滤已销毁的消息的交互,可以在处理前先判断interaction.message.deleted,为true直接跳过处理即可。
方案2:使用消息专属交互收集器
如果你的控制按钮只对单条发送的消息生效,可以使用MessageComponentCollector,无需注册全局事件,消息过期/销毁时收集器会自动清理:
const sendMessage = await QueueChannel.send({ embeds: [sendMessageEmbed], components: [Buttons] }); // 创建收集器,只监听这条消息的按钮交互,超时时间可自己设置 const collector = sendMessage.createMessageComponentCollector({ componentType: 'BUTTON', time: 3600000 // 1小时后自动销毁,可根据需求调整 }); collector.on('collect', async interaction => { try { if (interaction.customId === "PauseButton") { player.pause(true); await interaction.deferUpdate(); await interaction.editReply({ components: [ResumeButtons] }); } else if (interaction.customId === "ResumeButton") { player.pause(false); await interaction.deferUpdate(); await interaction.editReply({ components: [Buttons] }); } } catch (err) { console.error('交互处理失败', err); } }); // 收集器销毁时可做清理操作 collector.on('end', () => { // 可选:消息过期后把按钮设为禁用,避免用户点击报错 sendMessage.edit({ components: [Buttons.setComponents(Buttons.components.map(btn => btn.setDisabled(true)))] }).catch(() => {}); });
这个方案不需要你手动处理监听器销毁的逻辑,完全和单条消息的生命周期绑定,不会出现重复触发的问题。
内容的提问来源于stack exchange,提问作者Zuher Laith
相关产品推荐
相关产品推荐

