You need to enable JavaScript to run this app. 在discord.py中如何记录用户进出语音频道时间及使用on_voice_state_update()
优惠活动
大模型
产品
解决方案
定价
更多

在discord.py中如何记录用户进出语音频道时间及使用on_voice_state_update()

Discord机器人语音频道进出时间记录实现思路

核心方法on_voice_state_update基础说明

该事件会在任意用户的语音状态发生变化时触发,自带三个固定参数:

  • member:触发状态变化的用户对象
  • before:状态变化前的用户语音状态对象,可通过before.channel获取变化前的语音频道
  • after:状态变化后的用户语音状态对象,可通过after.channel获取变化后的语音频道

实现核心思路

1. 状态变化场景判断

你只需要通过before.channel和after.channel的对比,就能覆盖所有需要统计的场景:

  • 加入语音频道:before.channel is None且after.channel is not None,此时记录用户的加入时间和对应频道ID到临时存储
  • 离开语音频道:before.channel is not None且after.channel is None,此时取出临时存储的加入时间,计算停留时长后写入持久化存储,同时删除临时存储的对应记录
  • 切换语音频道:before.channel is not None且after.channel is not None且before.channel.id != after.channel.id,先结算用户在前一个频道的停留时长并存入持久化存储,再记录用户在新频道的加入时间

2. 存储方案选择

  • 临时存储:优先使用Python字典实现,结构示例:voice_join_records = {用户ID: (加入时间戳, 频道ID)},不需要额外依赖,适合中小体量机器人;如果需要避免机器人重启丢失临时记录,可以使用Redis做持久化缓存
  • 持久化存储:数据量小可以直接用SQLite,数据量大可以用MySQL、PostgreSQL等关系型数据库,表结构建议包含字段:user_id、channel_id、join_time、leave_time、duration_seconds

3. 边界情况处理

  • 方法开头先添加if member.bot: return逻辑,过滤掉机器人自身的语音状态变化,避免生成无效记录
  • 机器人重启后,先主动拉取当前所有语音频道的在线用户,为这些用户预写入当前时间戳作为加入时间,避免重启后用户离开时无法匹配加入记录
  • 必须在Discord开发者后台的机器人设置页开启Voice State Intent,同时代码中初始化Intents时显式开启voice_states权限,否则on_voice_state_update事件不会被触发

最小可运行代码示例

import datetime
import discord
from discord.ext import commands

# 临时存储用户加入语音的记录,key为用户ID,value为(加入时间戳, 频道ID)
voice_join_records = {}

# 注意必须开启voice_states intent
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, before, after):
    # 过滤机器人自身的事件
    if member.bot:
        return
    
    # 场景1:用户加入语音频道
    if before.channel is None and after.channel is not None:
        join_time = datetime.datetime.now().timestamp()
        voice_join_records[member.id] = (join_time, after.channel.id)
        return
    
    # 场景2:用户离开语音频道
    if before.channel is not None and after.channel is None:
        # 无加入记录则跳过(多为机器人重启前就已在频道的用户)
        if member.id not in voice_join_records:
            return
        join_time, channel_id = voice_join_records.pop(member.id)
        leave_time = datetime.datetime.now().timestamp()
        duration = round(leave_time - join_time, 2)
        # 此处可替换为写入数据库/本地文件的逻辑
        print(f"用户{member.name}({member.id}) 在频道 {before.channel.name}({channel_id}) 停留了 {duration} 秒")
        return
    
    # 场景3:用户切换语音频道
    if before.channel is not None and after.channel is not None and before.channel.id != after.channel.id:
        # 先结算旧频道的停留时长
        if member.id in voice_join_records:
            join_time, old_channel_id = voice_join_records.pop(member.id)
            leave_time = datetime.datetime.now().timestamp()
            duration = round(leave_time - join_time, 2)
            print(f"用户{member.name}({member.id}) 在频道 {before.channel.name}({old_channel_id}) 停留了 {duration} 秒")
        # 记录新频道的加入时间
        voice_join_records[member.id] = (datetime.datetime.now().timestamp(), after.channel.id)
        return

bot.run("你的机器人Token")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.21 22:06:03