Discord.py机器人on_voice_state_update触发NoneType对象无id属性报错
错误根因
on_voice_state_update事件会在成员所有语音状态变更时触发,包括成员完全退出语音频道的场景。此时成员没有连接任何语音频道,事件传入的after.channel参数值为None,你当前代码没有做空值校验,直接对None对象读取.id属性,直接触发AttributeError报错。
同时代码还存在几个会导致后续运行异常的隐患:
- 遍历
Bot.guilds全局查找分类的写法冗余,机器人接入多服务器时,容易在错误的服务器创建频道,完全没必要遍历全服务器列表,直接从事件上下文拿当前成员所在的服务器对象即可 - 没有对目标入口频道、目标分类做存在性校验,配置错ID时会无提示崩溃
修复方案
- 先对
after.channel做存在性判断,成员退出语音频道时直接跳过后续逻辑 - 直接从当前事件上下文获取所属服务器对象,不需要遍历机器人接入的所有服务器
- 增加必要的空值判断,目标分类找不到时直接退出逻辑,添加对应日志提示
修正后的可运行代码
@Bot.event async def on_voice_state_update(member, before, after): # 替换成你自己的配置ID ENTRY_CHANNEL_ID = "把原来&&&&&&&&&&&&&&&&&&&&&位置的入口语音频道ID填在这里" TEMP_CATEGORY_ID = "把原来$$$$$$$$$$$$$$$$$$位置的临时频道所属分类ID填在这里" # 跳过用户未加入任何语音频道的场景,直接解决当前的NoneType报错 if not after.channel: return # 判断用户是否进入了创建临时频道的入口语音房 if after.channel.id != ENTRY_CHANNEL_ID: return print(f'{member} зашёл в канал') # 直接从当前事件获取所属服务器,省去遍历全服务器的冗余操作 guild = after.channel.guild maincategory = discord.utils.get(guild.categories, id=TEMP_CATEGORY_ID) # 校验分类是否存在,避免配置ID错误导致无意义报错 if not maincategory: print(f"在服务器{guild.name}中未找到ID为{TEMP_CATEGORY_ID}的分类,无法创建临时语音频道") return channel2 = await guild.create_voice_channel( name=f"{member.display_name}'s 临时语音频道", category=maincategory ) await channel2.set_permissions( member, connect=True, mute_members=True, move_members=True, manage_channels=True ) await member.move_to(channel2) def check(member_after, before_state, after_state): # 监听临时频道的成员变化,所有成员离开时触发删除逻辑 return channel2.id == after_state.channel.id and len(channel2.members) == 0 await Bot.wait_for('voice_state_update', check=check) await channel2.delete()
适配说明
- 上述代码完全兼容你当前使用的Python 3.9运行环境,不需要升级版本
- 新增的频道ID匹配逻辑,能避免多临时频道同时存在时的误触发问题
内容的提问来源于stack exchange,提问作者MR_DRACULA_
相关产品推荐
相关产品推荐

