discord.py已连语音频道时discord.utils.get取Voice clients返回None问题
问题根因
你调用discord.utils.get(bot.voice_clients, guild=guild)时返回None的核心原因是:get方法匹配guild参数时需要传入discord.Guild对象,但你代码里全局变量guild_for_reconnect存储的是str(ctx.guild)即服务器名称字符串,类型不匹配自然找不到对应语音客户端。
除此之外你的代码还存在几个影响重连逻辑的问题:
- 全局变量
guild_for_reconnect_voice_channels存储的是命令触发时的频道列表静态快照,后续服务器内语音频道发生增删改名时,你存储的快照就失效了,会导致重连时找不到目标频道 - 用
asyncio.run在音频回调里启动递归协程是错误用法,会和discord.py本身的事件循环冲突,导致运行异常 - 用服务器名称作为文件名存储重连信息,一旦服务器改名就会导致重连逻辑失效,应该用服务器ID这种唯一不变的标识
修复方案
调整存储逻辑
不要存服务器名称和静态频道列表,直接存服务器ID、目标频道ID,数字ID不会随服务器/频道改名变化,稳定性更高。
修正匹配逻辑
获取语音客户端时用实时获取的Guild对象匹配,不要用名称字符串。
修正音频循环逻辑
用discord.py自带的事件循环处理回调,不要新开事件循环。
核心修正代码
import discord from discord.ext import commands from discord.utils import get import asyncio import os import nacl from keep_alive import keep_alive bot = commands.Bot(command_prefix='!') # 全局变量改存唯一ID,不存字符串和静态对象 reconnect_guild_id = None reconnect_channel_id = None @bot.event async def on_ready(): print('Logged in as {0.user}'.format(bot)) @bot.command(aliases=['shreksophone']) async def shrek(ctx, *, join_channel: str=None): voice = get(bot.voice_clients, guild=ctx.guild) global reconnect_guild_id, reconnect_channel_id # 存储服务器唯一ID reconnect_guild_id = ctx.guild.id guild_id = str(ctx.guild.id) author_id = str(ctx.author.id) # 音频回调改用事件循环调度协程 def shrek_repeat(e): bot.loop.create_task(_shrek_repeat_task(ctx)) async def _shrek_repeat_task(ctx): f = open('shrek_counter', 'r+') counter = f.read() new_counter = int(counter) + 1 f.seek(0) f.truncate() f.write(str(new_counter)) f.close() current_voice = get(bot.voice_clients, guild=ctx.guild) if current_voice and current_voice.is_connected(): current_voice.play(discord.FFmpegPCMAudio('songs/1 hour of shreksophone.m4a'), after=shrek_repeat) if not os.path.exists(guild_id): f = open(guild_id, 'w') f.write(author_id) f.close() f = open(guild_id, 'r') session_author = f.read() f.close() if session_author == author_id: if voice is None: target_channel = None if join_channel is None: if not ctx.author.voice: await ctx.send('You have to be in a voice channel, if you want me to play shreksophone.') print('User is not in a voice channel.') return target_channel = ctx.author.voice.channel else: target_channel = get(ctx.guild.voice_channels, name=join_channel) if not target_channel: await ctx.send('指定的语音频道不存在') return # 连接频道 voice = await target_channel.connect() await ctx.send(f'Playing shreksophone now in "{target_channel.name}" repeatedly.') print(f'Playing shreksophone now repeatedly in {ctx.guild.name}.') voice.play(discord.FFmpegPCMAudio('songs/1 hour of shreksophone.m4a'), after=shrek_repeat) # 存储频道唯一ID reconnect_channel_id = target_channel.id with open(f"{reconnect_guild_id}_channel", "w") as f: f.write(str(reconnect_channel_id)) bot.loop.create_task(voice_reconnect()) async def voice_reconnect(): while True: if reconnect_guild_id and reconnect_channel_id: # 实时获取最新的Guild对象 guild = bot.get_guild(reconnect_guild_id) if not guild: await asyncio.sleep(0.25) continue # 用Guild对象匹配,就能正确拿到语音客户端 voice = get(bot.voice_clients, guild=guild) # 实时获取最新的频道对象 target_channel = guild.get_channel(reconnect_channel_id) if not target_channel: await asyncio.sleep(0.25) continue if not voice or not voice.is_connected(): # 未连接就重连 await target_channel.connect() elif voice.channel.id != reconnect_channel_id: # 频道不符就移动 await voice.move_to(target_channel) await asyncio.sleep(0.25)
内容的提问来源于stack exchange,提问作者sunless
相关产品推荐
相关产品推荐

