You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 01:57:03