使用discord.js v12开发统计用户持有指定角色时长的机器人如何实现?
问题核心原因
你当前代码的错误主要有两点:
message.member.roles.cache.has(role.id)方法返回的是布尔值(true/false),直接用它做除法运算时,布尔值会被隐式转换为数字1/0,自然不可能得到正确的时长结果- 你没有存储用户获得「🚀On Duty」角色的时间戳,没有计算时长的基准值,且原有时间单位转换公式完全错误
修复方案
你需要新增一个存储结构记录用户的上岗时间戳,离岗时用当前时间减去开始时间得到总时长,再转换为天/时/分/秒格式即可。
首先在你命令处理逻辑的外层(比如机器人启动代码的顶部)声明存储结构:
// 存储用户上岗时间戳,key为用户ID,value为毫秒级时间戳 const dutyStartTimes = new Map(); const DUTY_ROLE_NAME = '🚀On Duty';
修改后的命令逻辑如下:
if(command === 'on') { const role = message.guild.roles.cache.find(role => role.name === DUTY_ROLE_NAME); // 避免重复上岗 if (message.member.roles.cache.has(role.id)) { return message.reply('你已经处于上岗状态,无需重复执行命令').then(msg => msg.delete({timeout: 3000})); } message.member.roles.add(role).then(() => { // 记录上岗时间 dutyStartTimes.set(message.author.id, Date.now()); message.author.send('你已进入上岗状态'); message.react('<:tick:902920026489749564>'); }).catch(err => console.error('添加角色失败:', err)); } else if(command == 'off'){ const role = message.guild.roles.cache.find(role => role.name === DUTY_ROLE_NAME); // 避免未上岗直接离岗 if (!message.member.roles.cache.has(role.id) || !dutyStartTimes.has(message.author.id)) { return message.reply('你当前没有处于上岗状态,无法执行离岗命令').then(msg => msg.delete({timeout: 3000})); } // 计算在岗时长 const startTime = dutyStartTimes.get(message.author.id); const totalMs = Date.now() - startTime; const days = Math.floor(totalMs / (1000 * 60 * 60 * 24)); const hours = Math.floor((totalMs % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); const minutes = Math.floor((totalMs % (1000 * 60 * 60)) / (1000 * 60)); const seconds = Math.floor((totalMs % (1000 * 60)) / 1000); // 移除角色、清理存储记录 message.member.roles.remove(role).then(() => { dutyStartTimes.delete(message.author.id); // 发送webhook日志 const wc = new WebhookClient('MyWebhookId', 'myWebhookUrl'); const log = new Discord.MessageEmbed() .setTitle('上岗日志') .setDescription(`用户: <@${message.author.id}> 天数: **${days}** 小时: **${hours}** 分钟: **${minutes}** 秒数: **${seconds}**`) .setTimestamp() .setColor('#fa0505'); wc.send({ username : message.author.tag, avatarURL : message.author.displayAvatarURL({ dynamic : true }), embeds : [log] }); message.react('<:tick:902920026489749564>'); }).catch(err => console.error('移除角色失败:', err)); }
额外说明
- 上述代码使用内存Map存储时间戳,重启机器人后数据会丢失,如果需要持久化存储可以接入
quick.db、mongoose等数据库存储用户的上岗时间 - 代码新增了异常校验逻辑,避免用户重复执行上岗、未上岗直接执行离岗的异常场景
内容的提问来源于stack exchange,提问作者antonmetas
相关产品推荐
相关产品推荐

