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
相关产品推荐
相关产品推荐

