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

Discord音乐Bot执行$play命令触发AttributeError: 'NoneType'无play属性报错

问题排查与修复方案

异常根因定位

报错AttributeError: 'NoneType' object has no attribute 'play'的直接原因是**play命令作用域内的voice变量始终为None**,问题出在代码的作用域逻辑缺陷:

  • 首次执行play命令时,Bot尚未加入任何语音频道,get(client.voice_clients, guild=ctx.guild)返回None赋值给初始的voice变量
  • 调用join()函数时,函数内部在自己的局部作用域新建了语音连接对象,但没有将这个连接对象返回给play命令的外层作用域
  • 外层作用域的voice变量仍然是初始的None,直接调用voice.play()自然触发方法不存在的报错。

同步需要修复的潜在隐患

当前代码还有几处会导致运行报错的问题,需要同步修正:

  • search()函数定义的入参为query,但函数内部全部使用了未定义的arg变量,运行时会直接抛出NameError
  • 未判断命令发起者是否处于语音频道,用户不在频道时执行join会触发空指针异常
  • YoutubeDL配置中noplaylist参数传入了字符串'True',实际该配置需要布尔值True才能生效
  • 原逻辑直接取info['formats'][0]['url'],该格式通常不是最佳音频流,会导致播放异常

具体修复操作

  • 修改join()函数,在完成语音连接/频道切换后,将最终的语音客户端对象返回
  • 在play命令中调用join()时,用返回值重新赋值voice变量,覆盖初始的None值
  • 统一search()函数的参数名,将所有未定义的arg替换为query
  • 增加前置校验:如果用户未加入语音频道,直接返回提示终止命令
  • 修正YoutubeDL参数类型,直接使用ytdl提取的最佳音频流地址

修复后可运行的代码示例

import requests
import discord
from discord.ext import commands
from discord import FFmpegPCMAudio
from yt_dlp import YoutubeDL
# 替换为你自己的Bot Token
token = "你的Bot Token"
client = commands.Bot(command_prefix='$')

@client.event
async def on_ready():
    print('ready')

def search(query):
    with YoutubeDL({'format': 'bestaudio', 'noplaylist': True}) as ydl:
        try:
            requests.get(query)
            info = ydl.extract_info(query, download=False)
        except:
            info = ydl.extract_info(f"ytsearch:{query}", download=False)['entries'][0]
    return info, info['url']

async def join(ctx, voice):
    channel = ctx.author.voice.channel
    if voice and voice.is_connected():
        await voice.move_to(channel)
    else:
        voice = await channel.connect()
    # 返回最终的语音连接对象
    return voice

@client.command(pass_context=True)
async def play(ctx, *, query):
    FFMPEG_OPTS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn'}
    # 前置校验用户是否在语音频道
    if not ctx.author.voice or not ctx.author.voice.channel:
        await ctx.send('你需要先加入一个语音频道才能播放音乐')
        return
    # 获取当前服务器已有的bot语音连接
    voice = discord.utils.get(client.voice_clients, guild=ctx.guild)
    # 用join返回的对象更新voice变量
    voice = await join(ctx, voice)
    if not voice:
        await ctx.send('无法连接到你的语音频道')
        return
    video, source = search(query)
    await ctx.send(f'正在播放: {video.get("title", "未知曲目")}')
    voice.play(FFmpegPCMAudio(source, **FFMPEG_OPTS), after=lambda e: print('done', e))

client.run(token)

内容的提问来源于stack exchange,提问作者pepega

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.26 14:54:03