Disnake框架on_voice_state_update事件UnboundLocalError问题求助
问题解决:Disnake on_voice_state_update的UnboundLocalError错误
核心问题
- 局部变量跨事件失效:
join_time_dt是在用户加入语音频道的分支里定义的局部变量,每次on_voice_state_update触发时都是新的函数上下文,用户离开时这个变量早就不存在了。 - 代码顺序错误:你先执行了
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
相关产品推荐
相关产品推荐

