Discord音乐机器人队列播放时出现Player无ctx属性的AttributeError错误
WaveLink音乐机器人添加歌曲到队列触发AttributeError错误
问题场景
开发基于WaveLink的Discord音乐机器人时,已有音乐播放状态下使用>>play指令添加歌曲到队列,触发以下错误:
AttributeError: 'Player' object has no attribute 'ctx'
完整错误栈
Bot Online Node <8d0e8f15e4723f72> is ready Ignoring exception in on_wavelink_track_end Traceback (most recent call last): File "/home/catiganvien3/.local/lib/python3.9/site-packages/discord/client.py", line 343, in _run_event await coro(*args, **kwargs) File "/home/catiganvien3/scripts/WaveLinkBot/main.py", line 28, in on_wavelink_track_end ctx = player.ctx AttributeError: 'Player' object has no attribute 'ctx'
完整代码
import wavelink import discord from discord.ext import commands import asyncio import os bot = commands.Bot(command_prefix='>>') @bot.event async def on_ready(): print('Bot Online') bot.loop.create_task(node_connect()) @bot.event async def on_wavelink_node_ready(node: wavelink.Node): print(f'Node <{node.identifier}> is ready') async def node_connect(): await bot.wait_until_ready() await wavelink.NodePool.create_node(bot=bot, host='ash-01.thermalhosting.com', port=2008, password='ASH-01') @bot.event async def on_wavelink_track_end(player: wavelink.Player, track: wavelink.Track, reason): ctx = player.ctx vc: player = ctx.voice_client if vc.loop: return await vc.play(track) try: next_song = vc.queue.get() await vc.play(next_song) embed = discord.Embed( title="Now playing", description=f'Song name: **{next_song.title}**\nSong author: **{next_song.author}**') await ctx.send(embed=embed) except: # An exception when after the track end, the queue is now empty. If you dont do this, it will get error. await vc.stop() @bot.command() async def play(ctx: commands.Context, *, search: wavelink.YouTubeTrack): if not ctx.voice_client: vc: wavelink.Player = await ctx.author.voice.channel.connect(cls=wavelink.Player) elif not getattr(ctx.author.voice, 'channel', None): await ctx.send(f'Connect to a voice channel to use this command!') else: vc: wavelink.Player = ctx.voice_client song_name = search.title song_author = search.author if song_author == None: song_author = 'Unknown' # embed = discord.Embed(title="Added to queue", description=f'Song name: **{song_name}**\nSong author: **{song_author}**') # await vc.play(search) # await ctx.send(embed=embed) if vc.queue.is_empty and vc.is_playing: await vc.play(search) embed = discord.Embed(title="Playing", description=f'Song name: **{song_name}**\nSong author: **{song_author}**') return await ctx.send(embed=embed) else: await vc.queue.put_wait(search) embed = discord.Embed(title="Added to queue", description=f'Song name: **{song_name}**\nSong author: **{song_author}**') return await ctx.send(embed=embed) vc.ctx = ctx setattr(vc, "loop", False) @bot.command() async def pause(ctx: commands.Context): if not ctx.voice_client: return await ctx.send('Im not playing any music!') elif not getattr(ctx.author.voice, 'channel', None): await ctx.send(f'Connect to a voice channel to use this command!') else: vc: wavelink.Player = ctx.voice_client await vc.pause() await ctx.send(f'Paused!') @bot.command() async def resume(ctx: commands.Context): if not ctx.voice_client: return await ctx.send('Im not playing any music!') elif not getattr(ctx.author.voice, 'channel', None): await ctx.send(f'Connect to a voice channel to use this command!') else: vc: wavelink.Player = ctx.voice_client await vc.resume() await ctx.send(f'Resumed!') @bot.command() async def stop(ctx: commands.Context): if not ctx.voice_client: return await ctx.send('Im not playing any music!') elif not getattr(ctx.author.voice, 'channel', None): await ctx.send(f'Connect to a voice channel to use this command!') else: vc: wavelink.Player = ctx.voice_client await vc.stop() await ctx.send(f'Stopped!') @bot.command() async def disconnect(ctx: commands.Context): if not ctx.voice_client: return await ctx.send('Im not playing any music!') elif not getattr(ctx.author.voice, 'channel', None): await ctx.send(f'Connect to a voice channel to use this command!') else: vc: wavelink.Player = ctx.voice_client await vc.disconnect() await ctx.send('Bye!') @bot.command() async def loop(ctx: commands.Context): if not ctx.voice_client: return await ctx.send('Im not playing any music!') elif not getattr(ctx.author.voice, 'channel', None): await ctx.send(f'Connect to a voice channel to use this command!') else: vc: wavelink.Player = ctx.voice_client try: vc.loop = True except Exception: setattr(vc, "loop", False) if vc.loop: return await ctx.send('Loop is now enabled!') else: vc.loop = False return await ctx.send('Loop is now disabled') @bot.command() async def queue(ctx: commands.Context): if not ctx.voice_client: return await ctx.send('Im not playing any music!') elif not getattr(ctx.author.voice, 'channel', None): await ctx.send(f'Connect to a voice channel to use this command!') else: vc: wavelink.Player = ctx.voice_client if vc.queue.is_empty: return await ctx.send('Queue is empty!') embed = discord.Embed(title="Queue") queue = vc.queue.copy() song_count = 0 for song in queue: song_count += 1 embed.add_field(name=f"Song number {song_count}", value=f"{song.title}") return await ctx.send(embed=embed) bot.run('secret')
问题原因及修复方案
原因
代码中给Player对象绑定ctx和loop属性的两行代码vc.ctx = ctx、setattr(vc, "loop", False)被放在了return语句之后,永远不会被执行。当第一首歌播放结束触发on_wavelink_track_end事件时,Player对象没有ctx属性,就会抛出AttributeError。
修复步骤
- 将绑定属性的代码移到
return语句之前,确保无论是否直接播放歌曲,Player对象都会拥有这两个属性。 - 修正
play命令中的逻辑错误,比如用户不在语音频道时添加return终止函数,以及调整直接播放歌曲的判断条件。
修改后的play命令代码
@bot.command() async def play(ctx: commands.Context, *, search: wavelink.YouTubeTrack): if not ctx.voice_client: vc: wavelink.Player = await ctx.author.voice.channel.connect(cls=wavelink.Player) elif not getattr(ctx.author.voice, 'channel', None): await ctx.send(f'Connect to a voice channel to use this command!') return # 用户不在频道时直接返回,避免后续代码执行 else: vc: wavelink.Player = ctx.voice_client # 先绑定必要属性,确保Player对象始终包含ctx和loop vc.ctx = ctx if not hasattr(vc, "loop"): setattr(vc, "loop", False) song_name = search.title song_author = search.author or 'Unknown' # 简化空值判断 # 修正判断逻辑:队列空且当前未播放时,直接播放歌曲 if vc.queue.is_empty and not vc.is_playing: await vc.play(search) embed = discord.Embed(title="Playing", description=f'Song name: **{song_name}**\nSong author: **{song_author}**') await ctx.send(embed=embed) else: await vc.queue.put_wait(search) embed = discord.Embed(title="Added to queue", description=f'Song name: **{song_name}**\nSong author: **{song_author}**') await ctx.send(embed=embed)
额外优化说明
- 原代码中
vc.queue.is_empty and vc.is_playing逻辑错误:应该是队列空且当前未播放时才直接播放,否则加入队列。 - 用户不在语音频道的分支添加
return,避免后续代码继续执行导致潜在错误。 - 用
search.author or 'Unknown'简化空作者的判断逻辑,代码更简洁。
内容的提问来源于stack exchange,提问作者VienCatigan
相关产品推荐
相关产品推荐

