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

如何修复Discord Bot的/playmusic斜杠命令,实现语音频道播放音频

解决方案

核心错误修复

报错的根本原因是你错误地从ctx.response(交互回复对象)中获取用户信息,正确的做法是从ctx.user(交互发起用户)中获取。除此之外还需要补充意图权限、错误处理和播放逻辑优化,以下是完整修复后的代码:

import os, discord
from discord.ext import commands
import ffmpeg
import asyncio

DISCORD_TOKEN = os.getenv("DISCORD_TOKEN")

# 开启必要的意图,包括语音状态获取
intents = discord.Intents.default()
intents.message_content = True
intents.voice_states = True  # 新增:获取用户语音状态所需
client = commands.Bot(command_prefix='/', intents=intents)

@client.event
async def on_ready():
    await client.tree.sync()
    print(f'登录为 {client.user}')

@client.event
async def on_message(message):
    print(f"收到消息: {message.content}")
    if message.author == client.user:
        return

    lowered_msg = message.content.lower()
    if lowered_msg in ('i love baguettes!', 'i love baguettes'):
        role = message.guild.get_role(1085983212855115887)
        await message.author.add_roles(role, reason="触发魔法口令", atomic=True)
        await message.channel.send('我也是!已为你添加法棍小队角色!')

@client.tree.command(name="kill", description="尝试关闭机器人")
async def kill_bot(ctx: discord.Interaction):
    await ctx.response.send_message(content="你敢碰我试试!")

@client.tree.command(name="give-lollipop", description="给机器人一根棒棒糖")
async def give_lollipop(ctx: discord.Interaction):
    await ctx.response.send_message(content="谢谢你的棒棒糖!😋")

@client.tree.command(name="playmusic", description="在你所在的语音频道播放音乐")
async def play_music(ctx: discord.Interaction):
    # 1. 检查用户是否在语音频道
    voice_state = ctx.user.voice
    if not voice_state or not voice_state.channel:
        await ctx.response.send_message("请先加入一个语音频道再使用此命令!")
        return

    voice_channel = voice_state.channel

    # 2. 处理机器人的语音连接状态
    voice_client = ctx.guild.voice_client
    if voice_client:
        if voice_client.channel != voice_channel:
            await voice_client.move_to(voice_channel)
            await ctx.response.send_message(f"已切换到频道 {voice_channel.name},开始播放音乐!")
        else:
            await ctx.response.send_message(f"已在 {voice_channel.name},开始播放音乐!")
    else:
        voice_client = await voice_channel.connect()
        await ctx.response.send_message(f"已加入频道 {voice_channel.name},开始播放音乐!")

    # 3. 处理音频播放(若ffmpeg不在系统PATH,需指定executable路径,比如executable='C:/ffmpeg/bin/ffmpeg.exe')
    try:
        source = discord.PCMVolumeTransformer(discord.FFmpegPCMAudio('sample-15s.mp3'))
        
        # 播放结束后自动断开语音的回调函数
        def after_play(error):
            if error:
                print(f"播放错误: {error}")
            # 异步执行断开操作
            asyncio.run_coroutine_threadsafe(voice_client.disconnect(), client.loop)

        voice_client.play(source, after=after_play)
    except Exception as e:
        await ctx.followup.send(f"播放失败: {str(e)}")
        if voice_client.is_connected():
            await voice_client.disconnect()

def start():
    client.run(DISCORD_TOKEN)

关键修改点说明

  • 意图权限补充:添加intents.voice_states = True,确保机器人能获取用户的语音频道信息,否则无法检测用户是否在语音频道。
  • 用户信息获取修正:将ctx.response.author改为ctx.user,ctx是交互上下文对象,直接通过它获取发起命令的用户。
  • 语音连接逻辑优化:检查机器人是否已在语音频道,若在不同频道则移动过去,避免重复连接。
  • 命令超时处理:斜杠命令要求3秒内回复,所以先发送提示消息,避免触发超时错误。
  • 播放后自动断开:使用after回调函数处理播放结束后的断开操作,避免阻塞命令执行线程。
  • 错误处理:添加用户不在频道、播放失败等场景的错误提示,提升用户体验。

额外注意事项

  • 确保ffmpeg已安装且添加到系统环境变量PATH中,若未添加,需在FFmpegPCMAudio中指定executable参数(如Windows系统:executable='C:/ffmpeg/bin/ffmpeg.exe')。
  • 音频文件sample-15s.mp3需放在机器人代码的同一目录下,或指定完整文件路径。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 17:37:22