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
相关产品推荐
相关产品推荐

