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

DiscordJS 多次执行命令后机器人重复发消息问题排查修复

异常产生原因
  • 每次执行!dm命令触发对应类逻辑时,都会重复给客户端的messageCreate事件绑定新的处理监听器,历史绑定的监听器从未被销毁移除。
  • Discord.js 遵循标准的Node.js EventEmitter事件机制:事件触发时会依次执行所有已注册的同事件监听器。首次执行!dm时仅存在1个绑定的监听器,逻辑正常执行1次;第二次执行时累计绑定了2个监听器,收到消息就会重复触发2次处理逻辑;后续每执行一次!dm就新增1个监听器,消息发送、处理的次数自然随命令执行次数逐次累加。
  • 给出的简写代码仅实现了事件绑定逻辑,没有做监听器去重、用完销毁的处理,属于典型的事件监听器内存泄漏问题。
修复方案

有两种可落地的修复方式,根据业务场景选其一即可:

  • 全局单次绑定监听器:将messageCreate的事件绑定逻辑移到类初始化、机器人启动的全局执行逻辑中,保证整个机器人运行生命周期内,DM消息处理的监听器只绑定1次。命令触发时仅修改状态标记,通知已存在的监听器开始等待用户回复即可,不需要重复绑定事件。
    参考实现:
    // 初始化阶段执行,仅绑定一次
    let isWaitingDmReply = false
    this.client.on("messageCreate", function (msg) {
      if (msg.author.bot || msg.channel.type !== "DM") return
      if (!isWaitingDmReply) return
      // 在这里写收到用户DM回复后的处理逻辑
      console.log("收到用户回复,执行对应逻辑")
      isWaitingDmReply = false
    })
    
    // !dm命令触发时仅修改状态,不重复绑定事件
    async function onDmCommandTrigger() {
      // 发送提示消息的原有逻辑
      isWaitingDmReply = true
    }
    
  • 临时绑定用完即销:如果业务逻辑要求必须在命令触发时才临时挂载监听器,那么在收到目标回复、处理逻辑执行完成后,必须调用client.off()方法移除本次绑定的监听器,避免监听器残留累计。
    参考实现:
    async function onDmCommandTrigger() {
      // 发送提示消息的原有逻辑
      const dmReplyHandler = function (msg) {
        if (msg.author.bot || msg.channel.type !== "DM") return
        // 在这里写收到用户DM回复后的处理逻辑
        console.log("收到用户回复,执行对应逻辑")
        // 处理完成后立刻移除当前监听器
        this.client.off("messageCreate", dmReplyHandler)
      }.bind(this)
      this.client.on("messageCreate", dmReplyHandler)
    }
    
  • 排查辅助手段:调试阶段可以在绑定监听器前打印this.client.listenerCount("messageCreate")的值,正常运行时该数值不会随!dm命令的执行次数持续上涨,可以快速验证是否还存在重复绑定问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 00:45:47