You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 04:53:27