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

Discord机器人无法加入语音频道播放音频问题求助

Discord机器人无法加入语音频道的排查与修复方案

问题描述

开发了一款Discord机器人,目标是加入语音频道播放YouTube音频。发送?play命令后,机器人未加入发送者的语音频道,且无任何报错。已确认机器人拥有除管理员外的全部权限,错误提示未触发,寻求解决方法。

原代码如下:

# Importing libraries
import discord
import os
import asyncio
import youtube_dl
import time

# Discord bot Initialization
client = discord.Client(intents=discord.Intents.default())
key = ""

voice_clients = {}

yt_dl_opts = {'format': 'bestaudio/best'}
ytdl = youtube_dl.YoutubeDL(yt_dl_opts)

ffmpeg_options = {'options': "-vn"}


# This event happens when the bot gets run
@client.event
async def on_ready():
    print(f"Bot logged in as {client.user}")


# This event happens when a message gets sent
@client.event
async def on_message(msg):
    if msg.content.startswith("?play"):

        try:
            voice_client = await msg.author.voice.channel.connect()
            voice_clients[voice_client.guild.id] = voice_client
        except:
            print("error")

        try:
            url = msg.content.split()[1]

            loop = asyncio.get_event_loop()
            data = await loop.run_in_executor(None, lambda: ytdl.extract_info(url, download=False))

            song = data['url']
            player = discord.FFmpegPCMAudio(song, **ffmpeg_options)

            voice_clients[msg.guild.id].play(player)

        except Exception as err:
            print(err)


    if msg.content.startswith("?pause"):
        try:
            voice_clients[msg.guild.id].pause()
        except Exception as err:
            print(err)

    # This resumes the current song playing if it's been paused
    if msg.content.startswith("?resume"):
        try:
            voice_clients[msg.guild.id].resume()
        except Exception as err:
            print(err)

    # This stops the current playing song
    if msg.content.startswith("?stop"):
        try:
            voice_clients[msg.guild.id].stop()
            await voice_clients[msg.guild.id].disconnect()
        except Exception as err:
            print(err)


client.run(key)

核心问题分析

  1. 消息内容意图未启用:Discord现在要求显式开启Message Content Intent才能让机器人读取消息内容,默认的discord.Intents.default()不包含这个权限,导致on_message可能无法正确识别?play命令。
  2. 未校验用户语音状态:如果发送命令的用户不在任何语音频道,msg.author.voice会返回None,直接调用.channel.connect()会抛出异常,但原代码的try-except只打印模糊的"error",无法定位问题。
  3. 语音连接逻辑不完善:如果机器人已经在当前服务器的语音频道中,重复调用connect()会报错,但原代码没有处理这种情况,也没有复用已有的连接。
  4. 依赖库过时:youtube_dl已停止维护,对YouTube的链接解析可能失效,导致后续播放逻辑中断,进而掩盖前面的连接问题。

修复后的完整代码

首先安装替代依赖:

pip uninstall youtube_dl
pip install yt-dlp

修改后的代码:

import discord
import os
import asyncio
import yt_dlp
import time

# 初始化客户端,启用必要的Intents
intents = discord.Intents.default()
intents.message_content = True  # 必须开启,否则无法读取消息内容
client = discord.Client(intents=intents)
key = ""

voice_clients = {}

# yt-dlp配置
yt_dl_opts = {
    'format': 'bestaudio/best',
    'quiet': True,
    'no_warnings': True
}
ytdl = yt_dlp.YoutubeDL(yt_dl_opts)

ffmpeg_options = {'options': "-vn"}


@client.event
async def on_ready():
    print(f"Bot logged in as {client.user}")


@client.event
async def on_message(msg):
    # 忽略机器人自身消息
    if msg.author == client.user:
        return

    if msg.content.startswith("?play"):
        # 1. 检查用户是否在语音频道
        if not msg.author.voice:
            await msg.channel.send("你得先加入一个语音频道啊!")
            return

        target_channel = msg.author.voice.channel
        guild_id = msg.guild.id

        try:
            # 2. 检查是否已有活跃的语音连接
            if guild_id in voice_clients and voice_clients[guild_id].is_connected():
                voice_client = voice_clients[guild_id]
                # 如果当前连接的频道不是目标频道,切换过去
                if voice_client.channel != target_channel:
                    await voice_client.move_to(target_channel)
            else:
                # 新建连接
                voice_client = await target_channel.connect()
                voice_clients[guild_id] = voice_client
        except Exception as err:
            print(f"连接语音频道失败: {err}")
            await msg.channel.send(f"连接失败:{str(err)}")
            return

        # 处理播放逻辑
        try:
            # 检查是否提供了URL
            if len(msg.content.split()) < 2:
                await msg.channel.send("请附上YouTube链接!格式:?play [链接]")
                return

            url = msg.content.split()[1]
            loop = asyncio.get_event_loop()
            data = await loop.run_in_executor(None, lambda: ytdl.extract_info(url, download=False))

            # 处理播放列表情况(可选)
            if 'entries' in data:
                data = data['entries'][0]

            song_url = data['url']
            player = discord.FFmpegPCMAudio(song_url, **ffmpeg_options)

            # 停止当前播放的音频(如果有的话)
            if voice_client.is_playing():
                voice_client.stop()

            voice_client.play(player)
            await msg.channel.send(f"正在播放:{data['title']}")
        except Exception as err:
            print(f"播放失败: {err}")
            await msg.channel.send(f"播放出错:{str(err)}")
            # 出错后断开连接(可选)
            try:
                await voice_client.disconnect()
                del voice_clients[guild_id]
            except:
                pass

    if msg.content.startswith("?pause"):
        guild_id = msg.guild.id
        if guild_id in voice_clients and voice_clients[guild_id].is_playing():
            voice_clients[guild_id].pause()
            await msg.channel.send("已暂停播放")
        else:
            await msg.channel.send("当前没有正在播放的音频")

    if msg.content.startswith("?resume"):
        guild_id = msg.guild.id
        if guild_id in voice_clients and voice_clients[guild_id].is_paused():
            voice_clients[guild_id].resume()
            await msg.channel.send("已恢复播放")
        else:
            await msg.channel.send("当前没有暂停的音频")

    if msg.content.startswith("?stop"):
        guild_id = msg.guild.id
        if guild_id in voice_clients:
            voice_clients[guild_id].stop()
            await voice_clients[guild_id].disconnect()
            del voice_clients[guild_id]
            await msg.channel.send("已停止播放并断开连接")
        else:
            await msg.channel.send("我不在任何语音频道里")


client.run(key)

关键改动说明

  • 启用消息内容意图:初始化客户端时添加intents.message_content = True,同时需要登录Discord开发者门户,找到你的Bot,在"Privileged Gateway Intents"下开启Message Content Intent。
  • 添加用户状态校验:在执行连接前检查用户是否在语音频道,避免无效操作并给出明确提示。
  • 优化语音连接管理:复用已有连接,支持切换频道,避免重复连接报错。
  • 替换依赖库:用维护活跃的yt-dlp替代youtube_dl,提升YouTube链接解析的稳定性。
  • 完善错误反馈:把模糊的错误提示改成具体异常信息,同时给用户发送频道消息,方便调试和使用。
  • 忽略自身消息:避免机器人响应自己发送的消息,减少不必要的逻辑触发。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 11:42:37