You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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。

修复步骤

  1. 将绑定属性的代码移到return语句之前,确保无论是否直接播放歌曲,Player对象都会拥有这两个属性。
  2. 修正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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 11:24:35