如何用Discord JS统计用户语音频道时长并存储至MongoDB
实现Discord语音频道用户停留时长统计并存储到MongoDB
由于Discord官方没有提供直接获取用户语音频道停留时长的API,你可以通过voiceStateUpdate事件追踪用户的加入/离开时间,计算时长差后存储到MongoDB。以下是具体实现方案:
前置准备
先安装必要的依赖包:
npm install discord.js mongoose
1. 定义MongoDB数据模型
创建一个模型来存储用户的语音时长记录,包含核心字段:
// models/VoiceDuration.js const mongoose = require('mongoose'); const voiceDurationSchema = new mongoose.Schema({ userId: { type: String, required: true }, // 用户ID channelId: { type: String, required: true }, // 语音频道ID guildId: { type: String, required: true }, // 服务器ID(可选,用于区分不同服务器) joinTime: { type: Date, required: true }, // 加入时间 leaveTime: { type: Date, required: true }, // 离开时间 duration: { type: Number, required: true } // 停留时长(秒) }); module.exports = mongoose.model('VoiceDuration', voiceDurationSchema);
2. 完善voiceStateUpdate事件逻辑
使用内存Map临时存储用户的当前语音频道加入时间,在用户离开/切换频道时计算时长并写入数据库:
const { Client, GatewayIntentBits } = require('discord.js'); const mongoose = require('mongoose'); const VoiceDuration = require('./models/VoiceDuration'); // 初始化Discord客户端(必须启用GuildVoiceStates意图) const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates ] }); // 临时存储用户当前的语音频道加入信息(键:用户ID,值:{channelId, joinTime}) const userActiveVoiceSessions = new Map(); // 连接MongoDB mongoose.connect('mongodb://localhost:27017/discord-voice-tracker') .then(() => console.log('MongoDB连接成功')) .catch(err => console.error('MongoDB连接失败:', err)); client.on('voiceStateUpdate', async (oldState, newState) => { const userId = newState.member.id; const oldChannel = oldState.channel; const newChannel = newState.channel; const guildId = newState.guild.id; // 场景1:用户首次加入语音频道 if (!oldChannel && newChannel) { userActiveVoiceSessions.set(userId, { channelId: newChannel.id, guildId: guildId, joinTime: new Date() }); return; } // 场景2:用户离开语音频道(从有到无) if (oldChannel && !newChannel) { const sessionData = userActiveVoiceSessions.get(userId); if (!sessionData || sessionData.channelId !== oldChannel.id) return; const leaveTime = new Date(); const duration = Math.floor((leaveTime - sessionData.joinTime) / 1000); // 转换为秒 // 写入数据库 try { await VoiceDuration.create({ userId: userId, channelId: oldChannel.id, guildId: guildId, joinTime: sessionData.joinTime, leaveTime: leaveTime, duration: duration }); console.log(`用户${userId}在频道${oldChannel.id}停留${duration}秒,记录已保存`); } catch (err) { console.error('保存时长记录失败:', err); } userActiveVoiceSessions.delete(userId); return; } // 场景3:用户切换语音频道 if (oldChannel && newChannel && oldChannel.id !== newChannel.id) { // 先处理旧频道的时长记录 const sessionData = userActiveVoiceSessions.get(userId); if (sessionData && sessionData.channelId === oldChannel.id) { const leaveTime = new Date(); const duration = Math.floor((leaveTime - sessionData.joinTime) / 1000); try { await VoiceDuration.create({ userId: userId, channelId: oldChannel.id, guildId: guildId, joinTime: sessionData.joinTime, leaveTime: leaveTime, duration: duration }); } catch (err) { console.error('保存旧频道时长记录失败:', err); } } // 记录新频道的加入时间 userActiveVoiceSessions.set(userId, { channelId: newChannel.id, guildId: guildId, joinTime: new Date() }); } }); // 登录机器人 client.login('你的Discord机器人Token');
注意事项
- 必须在Discord开发者门户为机器人启用
GuildVoiceStates意图,否则无法接收voiceStateUpdate事件 - 内存
Map在机器人重启后会丢失未保存的临时会话,若需持久化临时状态,可改用Redis或MongoDB临时集合 - 可以根据需求扩展数据模型,比如添加用户名、频道名称等冗余字段,方便后续查询统计
内容的提问来源于stack exchange,提问作者jksnDev
相关产品推荐
相关产品推荐

