You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Discord.js机器人消息反应重复触发问题:重复发送内容的修复方案

修复Discord机器人重复发送消息的问题

看起来你的机器人出现重复发送消息的问题,根源在于全局的messageReactionAdd事件监听器被重复注册,或者监听器没有针对特定消息做过滤,导致旧的命令实例的监听器仍然在响应新的反应操作。

问题分析

如果你的client.on('messageReactionAdd', ...)代码是写在命令处理函数内部的,那么每次运行命令时,都会新增一个事件监听器。当你多次执行命令后,同一个⛔表情反应会触发所有已注册的监听器,自然就会重复发送消息。另外,全局的messagerun等变量也会导致不同命令实例的状态互相干扰。

修复方案:使用消息专属的反应收集器

Discord.js提供了createReactionCollectorawaitReactions方法,能针对单个消息创建独立的反应监听,完成后自动销毁,不会和其他命令实例冲突。下面是修改后的完整逻辑:

// 首先移除原来的全局messageReactionAdd监听器,改用收集器
// client.on('messageReactionAdd', ...) 这段代码可以删掉了

// 假设这是你的命令触发逻辑(以!start命令为例)
client.on('messageCreate', async (message) => {
  // 替换成你的实际命令触发词
  if (message.content !== '!start') return;

  // 用对象存储用户状态,避免全局变量冲突(支持多用户同时使用)
  if (!global.speedrunStates) global.speedrunStates = {};
  const userId = message.author.id;
  const userState = global.speedrunStates[userId] || { run: false, index: 0, interval: null, timeout: null };

  // 检查用户是否已有运行中的速通
  if (userState.run) {
    return message.channel.send('你已经有一个正在进行的速通了!');
  }

  // 初始化用户状态
  userState.run = true;
  userState.index = 0;
  // 假设这里是你的计时逻辑(比如启动interval)
  userState.interval = setInterval(() => {
    userState.index++;
  }, 1000);
  global.speedrunStates[userId] = userState;

  // 发送启动消息,并创建反应收集器
  const startMessage = await message.channel.send('速通已开始!点击⛔表情停止计时');
  
  // 创建收集器:只监听当前消息的⛔反应,且仅对命令发起者有效
  const collector = startMessage.createReactionCollector({
    filter: (reaction, reactingUser) => {
      return reaction.emoji.name === '⛔' && reactingUser.id === userId && !reactingUser.bot;
    },
    max: 1, // 只收集一次有效反应
    time: 300000 // 可选:5分钟超时自动结束
  });

  // 监听收集到反应的事件
  collector.on('collect', async () => {
    // 清理计时相关资源
    clearInterval(userState.interval);
    clearTimeout(userState.timeout);

    // 发送结果嵌入
    const resultEmbed = new Discord.MessageEmbed()
      .setColor('#fefefe')
      .setTitle(`你的记录是 ${userState.index} 秒!`)
      .setAuthor(message.author.tag, message.author.avatarURL())
      .setDescription('Bot made by tempacc');
    
    await message.channel.send(resultEmbed);
    
    // 离开语音频道(添加?避免用户不在频道时报错)
    message.member.voice.channel?.leave();

    // 重置用户状态
    userState.run = false;
    userState.index = 0;
    global.speedrunStates[userId] = userState;
    collector.stop();
  });

  // 监听收集器结束事件(比如超时)
  collector.on('end', (collected) => {
    if (collected.size === 0 && userState.run) {
      message.channel.send('速通已超时结束!');
      clearInterval(userState.interval);
      userState.run = false;
      userState.index = 0;
      global.speedrunStates[userId] = userState;
    }
  });
});

关键改进点

  1. 移除全局事件监听器:不再使用全局的messageReactionAdd,而是针对每个命令发送的消息创建专属的反应收集器,避免重复触发。
  2. 用户状态隔离:用对象存储每个用户的速通状态,避免全局变量导致多用户使用时的冲突。
  3. 精准过滤反应:只处理当前命令发起者对目标消息的⛔反应,排除机器人和其他用户的干扰。
  4. 自动清理资源:收集器完成或超时后自动停止,同时清理计时资源和重置状态。

这样修改后,无论你运行多少次命令,每个反应只会触发对应命令实例的逻辑,就不会出现重复发送消息的问题了。

内容的提问来源于stack exchange,提问作者TempAcc

火山引擎 最新活动