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

Disnake框架on_voice_state_update事件UnboundLocalError问题求助

问题解决:Disnake on_voice_state_update的UnboundLocalError错误

核心问题

  1. 局部变量跨事件失效:join_time_dt是在用户加入语音频道的分支里定义的局部变量,每次on_voice_state_update触发时都是新的函数上下文,用户离开时这个变量早就不存在了。
  2. 代码顺序错误:你先执行了join_time_dt = datetime.strptime(join_time, ...),但join_time是后面才赋值的,这行代码本身会先触发NameError,只是你还没走到这一步就遇到了离开时的问题。

修复方案

用全局字典存储每个成员的加入时间,这样跨事件调用也能保留数据,同时直接操作datetime对象,不用来回转字符串:

# 全局字典,存储成员ID到加入时间的映射
member_join_times = {}

@bot.event
async def on_voice_state_update(member, before, after):
    moscow_tz = pytz.timezone('Europe/Moscow') # 确保已导入pytz或使用zoneinfo
    if before.channel is None and after.channel is not None:
        # 用户加入语音频道
        print("Участник присоединился к голосовому каналу")
        embed = disnake.Embed(title="Участник присоединился к голосовому каналу", color=0x00ff00)
        embed.add_field(name="Участник", value=member.mention)
        embed.add_field(name="ID участника", value=member.id)
        embed.add_field(name="Канал", value=after.channel.mention)
        embed.add_field(name="ID канала", value=after.channel.id)
        await disnake.utils.get(member.guild.text_channels, id=1240402311147290685).send(embed=embed)
        # 直接存储datetime对象,避免字符串转解析的冗余操作
        join_time_dt = member.voice.joined_at.astimezone(moscow_tz)
        member_join_times[member.id] = join_time_dt
    elif before.channel is not None and after.channel is None:
        # 用户离开语音频道
        if member.id not in member_join_times:
            # 处理无加入时间记录的情况(如机器人重启后用户已在频道)
            embed = disnake.Embed(title="Участник отключился от голосового канала", color=0xFF0000)
            embed.add_field(name="Участник", value=member.mention)
            embed.add_field(name="Канал", value=before.channel.mention)
            embed.add_field(name="ID канала", value=before.channel.id)
            embed.add_field(name="Время в канале", value="Нет данных о времени подключения")
            await disnake.utils.get(member.guild.text_channels, id=1240402311147290685).send(embed=embed)
            return
        # 获取存储的加入时间,用完后移除节省内存
        join_time_dt = member_join_times.pop(member.id)
        leave_time_dt = datetime.now(tz=moscow_tz)
        duration = leave_time_dt - join_time_dt
        hours_on = duration.seconds // 3600
        minutes_on = (duration.seconds // 60) % 60
        seconds_on = duration.seconds % 60
        embed = disnake.Embed(title="Участник отключился от голосового канала", color=0xFF0000)
        embed.add_field(name="Участник", value=member.mention)
        embed.add_field(name="Канал", value=before.channel.mention)
        embed.add_field(name="ID канала", value=before.channel.id)
        embed.add_field(name="Время в канале", value=f'{hours_on} часов, {minutes_on} минут, {seconds_on} секунд')
        await disnake.utils.get(member.guild.text_channels, id=1240402311147290685).send(embed=embed)

关键改进点

  • 全局字典member_join_times实现了跨事件的时间数据持久化,确保离开时能获取到对应的加入时间。
  • 直接操作datetime对象,消除了字符串转解析的冗余步骤,降低出错概率。
  • 增加了边界情况处理,避免因无加入时间记录导致的崩溃,提升了代码稳定性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 19:06:15