Discord.js 命令处理器配置的统一冷却无报错也不生效问题
Discord.js 统一命令冷却功能失效修复方案
问题现状
- 需求:在消息事件处理器中实现全局统一冷却逻辑,无需在单个命令文件中重复编写冷却判断代码
- 异常表现:冷却逻辑运行无任何控制台报错,但连续触发命令完全不会被拦截,也不会弹出冷却提示
现有问题代码
消息事件处理器中的冷却实现
const args = message.content.slice(prefix.length).trim().split(/ +/g); const cmd = args.shift().toLowerCase(); let command = client.commands.get(cmd); //cooldowns const cooldowns = new Discord.Collection(); if (!cooldowns.has(command.name)) { cooldowns.set(command.name, new Discord.Collection()); } const now = Date.now(); const timestamps = cooldowns.get(command.name); const cooldownAmount = (command.cooldown || 3) * 1000; if (timestamps.has(message.author.id)) { const expirationTime = timestamps.get(message.author.id) + cooldownAmount; if (now < expirationTime) { const timeLeft = (expirationTime - now) / 1000; return message.reply(`please wait ${timeLeft.toFixed(1)} more second(s) before reusing the \`${command.name}\` command.`); } } timestamps.set(message.author.id, now); setTimeout(() => timestamps.delete(message.author.id), cooldownAmount); //cooldowns
对应命令文件配置
module.exports = { name: 'tag', aliases: ['tags', 't'], cooldown: 5, category: 'fun', description: 'Create a custom command', usage: `tag create|delete|edit`, run: (client, message, args) => { return message.reply('test, command is not done yet this is to test the cooldown') } }
失效核心原因
全局冷却状态集合的作用域错误:cooldowns集合被定义在消息事件的回调函数内部,每次收到新消息触发事件时,回调都会重新执行,生成一个全新的空cooldowns集合,上一次触发命令时存储的用户时间戳会被直接清空,根本无法跨消息事件留存状态,自然永远走不到冷却拦截的分支。
另外这段代码缺少基础边界判断:如果用户输入的是不存在的命令,直接访问command.name会触发报错,只是大概率前面已经加了非空判断才没暴露这个问题。
修复后的可运行实现
- 把
cooldowns集合的初始化移到bot启动阶段,挂在客户端实例上,保证整个程序运行周期内只初始化一次,状态可跨事件留存 - 补全命令非空、机器人消息拦截的基础判断
- 冷却判断逻辑放在命令执行流程之前,只有通过校验的请求才能进入命令执行
// 在bot初始化阶段、绑定消息事件之前,全局初始化冷却集合 client.cooldowns = new Discord.Collection(); client.on('messageCreate', message => { // 前缀、命令解析逻辑保持原有逻辑 const args = message.content.slice(prefix.length).trim().split(/ +/g); const cmd = args.shift().toLowerCase(); // 兼容命令别名查找 let command = client.commands.get(cmd) || client.aliases.get(cmd); // 边界校验:命令不存在、消息来自机器人时直接返回 if (!command || message.author.bot) return; // 全局冷却判断逻辑 if (!client.cooldowns.has(command.name)) { client.cooldowns.set(command.name, new Discord.Collection()); } const now = Date.now(); const timestamps = client.cooldowns.get(command.name); // 命令未配置冷却时间时默认3秒 const cooldownAmount = (command.cooldown || 3) * 1000; if (timestamps.has(message.author.id)) { const expirationTime = timestamps.get(message.author.id) + cooldownAmount; if (now < expirationTime) { const timeLeft = (expirationTime - now) / 1000; return message.reply(`请等待 ${timeLeft.toFixed(1)} 秒后再使用 \`${command.name}\` 命令。`); } } timestamps.set(message.author.id, now); setTimeout(() => timestamps.delete(message.author.id), cooldownAmount); // 冷却校验通过后,执行命令逻辑 command.run(client, message, args); })
注意事项
- 冷却集合绝对不能定义在高频执行的回调作用域内,否则永远无法留存状态
- 命令查找逻辑要同步适配别名,避免用别名触发命令时冷却规则失效
- 冷却判断要放在命令执行之前,避免未通过校验的请求直接触发命令逻辑
内容的提问来源于stack exchange,提问作者French Noodles
相关产品推荐
相关产品推荐

