Discord Bot如何设置指令全局冷却:一人触发后全用户需等30分钟使用
全局冷却实现核心逻辑
全局冷却与单用户冷却的本质区别仅在于冷却校验的键维度:
- 单用户冷却以「用户ID + 指令名」为键,为每个用户单独维护冷却状态
- 全局冷却不需要区分触发用户,直接用固定的指令标识作为唯一键,存储该指令最近一次有效执行的冷却截止时间戳,所有用户调用时统一校验该时间戳即可,无需为单个用户单独记录冷却信息
分步实现方案
- 初始化全局冷却存储:在全局作用域下维护一个冷却状态存储对象,单进程部署场景直接使用原生JS对象即可,存储指令对应的冷却截止时间戳
- 前置校验拦截:在指令触发逻辑的最开头,先读取对应指令的冷却截止时间戳:
- 如果截止时间戳大于当前系统时间,说明冷却仍在生效,直接计算剩余冷却时长给用户返回提示,中断后续所有逻辑
- 如果冷却已过期,再走原本的Embed确认弹窗、等待用户30秒内回复yes/no的流程
- 精准写入冷却:只有在用户确认执行(即回复yes、完成职员角色@通知发送)之后,才更新全局冷却的截止时间,不要在用户刚触发指令的阶段就写入冷却,避免用户取消操作、超时的场景无意义占用冷却时长
参考代码实现
首先在全局作用域初始化配置与存储:
const Discord = require('discord.js'); const client = new Discord.Client(); // 全局冷却存储结构:键为指令标识,值为冷却截止时间戳 const globalCooldowns = { 'call-all-staff': null // 初始状态无冷却 }; // 全局冷却时长:30分钟 = 30 * 60 * 1000 毫秒 const GLOBAL_COOLDOWN_MS = 30 * 60 * 1000;
指令触发与逻辑处理:
client.on('messageCreate', async (message) => { // 匹配call all staff的触发规则 if (message.content === '!callallstaff') { // 第一步:全局冷却前置校验 const cooldownEnd = globalCooldowns['call-all-staff']; const currentTime = Date.now(); if (cooldownEnd && cooldownEnd > currentTime) { // 计算剩余冷却时间 const remainingSec = Math.ceil((cooldownEnd - currentTime) / 1000); const remainingMin = Math.ceil(remainingSec / 60); return message.reply(`呼叫全体职员功能处于全局冷却中,请${remainingMin}分钟后再尝试使用。`); } // 冷却校验通过,弹出确认Embed const confirmEmbed = new Discord.MessageEmbed() .setTitle('紧急呼叫操作确认') .setDescription('该操作将@全体服务器职员,仅可在紧急场景使用,滥用将受到处罚。\n请在30秒内回复`yes`确认执行,回复`no`取消操作。'); await message.channel.send({ embeds: [confirmEmbed] }); // 收集发起者的回复 const filter = (msg) => msg.author.id === message.author.id; const userReply = await message.channel.awaitMessages({ filter, max: 1, time: 30 * 1000, errors: ['time'] }).catch(() => null); // 超时处理 if (!userReply) { return message.reply('操作超时,已取消呼叫请求。'); } const replyContent = userReply.first().content.trim().toLowerCase(); // 用户取消处理 if (replyContent === 'no') { return message.reply('已取消呼叫全体职员操作。'); } // 无效回复处理 if (replyContent !== 'yes') { return message.reply('回复内容无效,已取消操作。'); } // 用户确认yes,执行真正的通知逻辑 const staffRole = message.guild.roles.cache.get('你的职员角色ID'); await message.channel.send(`【紧急通知】${staffRole} 收到服务器紧急呼叫,请相关成员及时响应。`); // 通知发送成功后,才写入全局冷却 globalCooldowns['call-all-staff'] = Date.now() + GLOBAL_COOLDOWN_MS; } }); client.login('你的机器人Token');
常见踩坑提醒
- 冷却写入时机偏差:不要在用户刚触发指令、弹出确认框的时候就写入冷却,否则用户选择取消、超时无回复的场景,也会让所有用户等30分钟冷却,完全违背功能设计的初衷
- 多进程部署适配:如果机器人采用多进程/集群模式部署,不同进程的内存数据不互通,此时需要把全局冷却存储替换为共享缓存(如Redis),所有进程读取同一份冷却时间戳,否则会出现不同进程下的用户绕过冷却的问题
- 冷却计时锚点偏移:冷却的计时起点要选在通知真正发送成功的时刻,不要从用户触发指令的时刻开始算,否则会因为用户确认回复的几十秒延迟,导致实际冷却时长不足30分钟
内容的提问来源于stack exchange,提问作者Sasco X
相关产品推荐
相关产品推荐

