Discord.py音乐机器人添加歌曲至队列或查看队列时触发NameError问题求助
解决Discord.py音乐机器人的
song_queue未定义错误 兄弟,我仔细看了你的代码和报错信息,这个NameError的问题根源很清晰:你没有正确在类中管理song_queue队列变量,导致其他方法无法访问它。下面一步步给你讲怎么修复:
错误根源分析
song_queue是局部变量,不是实例属性:在MusicPlayer类的__init__里,你定义的song_queue = []只是局部变量,类里的其他方法(比如play、queue)根本访问不到它。- 重复定义内部函数导致作用域混乱:你在
play命令里又重新定义了play_song和search_song,不仅重复代码,还让内部函数更难访问类的变量。 - 未区分不同服务器的队列:如果多个服务器同时使用机器人,共用一个队列会导致混乱,最好用字典按服务器ID存储各自的队列。
具体修改步骤
1. 初始化实例级别的队列字典
修改__init__方法,把song_queue变成类的实例属性,用字典存储每个服务器的独立队列:
class MusicPlayer(commands.Cog): def __init__(self, cm): self.cm = cm # 用字典存储每个服务器的队列,key是服务器ID,value是队列列表 self.song_queue = {}
2. 修复所有类方法的定义(添加self)
类里的所有方法(包括命令方法)都要把self作为第一个参数,否则无法访问实例属性。比如join命令要改成这样:
@cm.command() async def join(self, ctx): # 必须加上self if ctx.author.voice is None: return await ctx.send("Bạn không ở trong 1 voice channel") if ctx.voice_client is not None: await ctx.voice_client.disconnect() await ctx.author.voice.channel.connect()
同理,leave、skip、pause、resume、search这些命令都要加上self作为第一个参数。
3. 修复search_song和play_song为实例方法
把这两个方法移到play命令外面,改成类的实例方法,并且用self.cm访问bot实例:
async def search_song(self, amount, song, get_url=False): info = await self.cm.loop.run_in_executor( None, lambda: youtube_dl.YoutubeDL({"format": "bestaudio", "quiet": True}).extract_info( f'ytsearch{amount}:{song}', download=False, ie_key="YoutubeSearch" ) ) if len(info['entries']) == 0: return None return [entry['webpage_url'] for entry in info['entries']] if get_url else info async def play_song(self, ctx, song): url = pafy.new(song).getbestaudio().url # 修复之前的executable拼写错误 ctx.voice_client.play( discord.PCMVolumeTransformer(discord.FFmpegPCMAudio(url, executable="C:\\ffmpeg-5.0-essentials_build\\bin\\ffmpeg.exe")), after=lambda error: self.cm.loop.create_task(self.check_queue(ctx)) ) ctx.voice_client.source.volume = 0.5
4. 修复check_queue方法
改成实例方法,并且获取当前服务器的专属队列:
async def check_queue(self, ctx): # 获取当前服务器的队列,不存在则返回空列表 guild_queue = self.song_queue.get(ctx.guild.id, []) if len(guild_queue) > 0: ctx.voice_client.stop() await self.play_song(ctx, guild_queue[0]) # 更新队列,移除已播放的歌曲 self.song_queue[ctx.guild.id] = guild_queue[1:]
5. 修复play命令
去掉内部重复定义的函数,直接调用类的方法,正确访问当前服务器的队列:
@cm.command() async def play(self, ctx, *, song=None): if song is None: return await ctx.send("Bạn phải chọn 1 bài để phát") if ctx.voice_client is None: return await ctx.send("Bot chưa trong 1 voice channel") # 如果不是YouTube链接,先执行搜索 if not ("youtube.com/watch" in song or "youtu.be" in song): await ctx.send("Đang search, vui lòng đợi") result = await self.search_song(1, song, get_url=True) if result is None: return await ctx.send("Không thể phát bài hát này") song = result[0] # 初始化当前服务器的队列(如果还未创建) if ctx.guild.id not in self.song_queue: self.song_queue[ctx.guild.id] = [] # 如果当前正在播放,将歌曲添加到队列 if ctx.voice_client.source is not None: self.song_queue[ctx.guild.id].append(song) queue_len = len(self.song_queue[ctx.guild.id]) return await ctx.send(f"Bài hát đã được thêm vào queue ở vị trí: {queue_len}.") # 直接播放歌曲 await self.play_song(ctx, song) await ctx.send(f"Đang phát: {song}")
6. 修复queue命令
正确获取当前服务器的队列并展示:
@cm.command() async def queue(self, ctx): guild_queue = self.song_queue.get(ctx.guild.id, []) if len(guild_queue) == 0: return await ctx.send("Không có bài hát nào trong queue") embed = discord.Embed(title="Quê-ue", description="", colour=discord.Colour.blue()) for idx, url in enumerate(guild_queue, 1): embed.description += f"{idx}) {url}\n" embed.set_footer(text="Bot thuong dank ehe") await ctx.send(embed=embed)
7. 修复search命令
去掉内部的search_song定义,直接调用类的方法:
@cm.command() async def search(self, ctx, *, song=None): if song is None: return await ctx.send("Nhập từ khóa cần tìm kiếm pls") await ctx.send("Đang tìm kiếm...") info = await self.search_song(10, song) embed = discord.Embed(title=f"Kết quả tìm kiếm cho '{song}':", description="*Copy và dùng URL để .play chính xác hơn*\n", colour=discord.Colour.blue()) amount = 0 for entry in info["entries"]: embed.description += f"[{entry['title']}]({entry['webpage_url']})\n" amount += 1 embed.set_footer(text=f"Đang hiển thị {amount} kết quả đầu tiên.") await ctx.send(embed=embed)
额外注意事项
- 确保你已经安装了所有依赖:
discord.py、youtube-dl、pafy、ffmpeg(并且路径配置正确)。 youtube-dl已停止维护,建议换成yt-dlp,兼容性更好,只需要把代码里的youtube_dl替换成yt_dlp即可。
这样修改后,你的机器人应该就能正常处理队列,不会再出现song_queue未定义的错误了!
内容的提问来源于stack exchange,提问作者Bắc Xuân
相关产品推荐
相关产品推荐

