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

Discord.py 等级系统开发:如何统计语音频道驻留时长获取对应积分

解决方案:Discord Bot语音频道驻留时长统计实现思路

核心实现逻辑

基于Discord官方API提供的voiceStateUpdate事件实现,该事件会在用户加入/离开语音频道、静音/闭麦/进入AFK频道时触发,你只需要筛选出进出频道的状态变更即可完成时长统计:

  • 第一步:维护一个临时存储结构(内存字典/Redis均可),Key为用户ID,Value存储用户进入当前语音频道的时间戳,如果你的规则不计入静音、AFK状态的时长,可以额外存储用户当前的语音状态标记
  • 第二步:监听voiceStateUpdate事件,对比事件返回的oldState(变更前状态)和newState(变更后状态)两个参数,处理三类核心场景:
    • 如果oldState.channel为null且newState.channel不为null:判定为用户进入语音频道,将当前时间戳写入临时存储
    • 如果oldState.channel不为null且newState.channel为null:判定为用户离开语音频道,用当前时间戳减去存储的进入时间戳得到本次驻留时长,累加到该用户的总语音时长数据后,清除临时存储中该用户的进入时间戳
    • 如果oldState.channel.id不等于newState.channel.id:判定为用户切换语音频道,按先离开旧频道结算时长、再进入新频道记录时间的逻辑执行两次操作即可
  • 第三步:积分发放可以根据需求选两种实现:
    • 实时结算:用户离开频道时统一结算本次时长,按每满1分钟发放对应积分的规则计算,不满1分钟的部分可以累计到下次或者直接舍弃
    • 定时结算:每分钟遍历一次临时存储中的所有在线用户,直接给符合有效状态的用户增加1分钟时长、发放对应积分,这种方式不需要等用户退出就可以实时到账,用户感知更好
  • 额外注意:你需要在Discord开发者后台的Bot设置页开启Server Members Intent和Voice State Intent,否则无法接收语音状态变更事件。

踩坑提示:记得过滤掉机器人账号的语音状态变更,避免给机器人发放无效积分;如果规则要排除静音、AFK的无效时长,判断newState.self_mute、newState.self_deaf、newState.afk三个字段即可,符合无效状态的时段不统计时长。

示例代码片段(基于discord.py v2.x)

import discord
from datetime import datetime
from discord.ext import commands

# 临时存储:key=用户ID,value=进入语音频道的时间戳
voice_join_time = {}
# 总时长存储:key=用户ID,value=累计语音时长(秒),生产环境替换为数据库
user_voice_total = {}

# 必须开启语音状态权限
intents = discord.Intents.default()
intents.voice_states = True
intents.members = True

bot = commands.Bot(command_prefix="!", intents=intents)

@bot.event
async def on_voice_state_update(member, old_state, new_state):
    # 过滤机器人账号
    if member.bot:
        return
    # 场景1:进入语音频道
    if old_state.channel is None and new_state.channel is not None:
        voice_join_time[member.id] = datetime.now().timestamp()
    # 场景2:离开语音频道
    elif old_state.channel is not None and new_state.channel is None:
        if member.id in voice_join_time:
            stay_sec = datetime.now().timestamp() - voice_join_time[member.id]
            # 累计总语音时长
            user_voice_total[member.id] = user_voice_total.get(member.id, 0) + stay_sec
            # 按1分钟1积分的规则结算,不满1分钟的部分舍去
            add_point = int(stay_sec // 60)
            # 这里调用你现有逻辑中的积分增加接口即可
            # add_user_point(member.id, add_point)
            del voice_join_time[member.id]
    # 场景3:切换语音频道
    elif old_state.channel.id != new_state.channel.id:
        # 先结算旧频道的驻留时长
        if member.id in voice_join_time:
            stay_sec = datetime.now().timestamp() - voice_join_time[member.id]
            user_voice_total[member.id] = user_voice_total.get(member.id, 0) + stay_sec
            add_point = int(stay_sec // 60)
            # add_user_point(member.id, add_point)
        # 记录新频道的进入时间
        voice_join_time[member.id] = datetime.now().timestamp()

内容的提问来源于stack exchange,提问作者Georgi Koynov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 04:54:03