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

Discord舞台频道音乐机器人无声音/单轨播放问题求助

问题描述

我复刻了一个音乐机器人,为了让它在舞台频道播放音乐,修改了join命令,将suppress设为False。

改动后的join命令代码

@commands.command(name="join", invoke_without_subcommand=True)
async def _join(self, ctx: commands.Context):
    destination = ctx.author.voice.channel
    if ctx.voice_state.voice:
        await ctx.voice_state.voice.move_to(destination)
        return
    if ctx.author.voice.channel.type == discord.enums.ChannelType.stage_voice:
        ctx.voice_state.voice = await destination.connect()
        # await ctx.me.request_to_speak()
        await ctx.me.edit(suppress=False)
    else:
        ctx.voice_state.voice = await destination.connect()

play命令代码

async def _play(self, ctx: commands.Context, *, search: str):
    if search.__contains__("?list="):
        if not ctx.voice_state.voice:
            await ctx.invoke(self._join)
        async with ctx.typing():
            playlist, playlistTitle = self._playlist(search)
            for _title, _link in playlist.items():
                try:
                    source = await YTDLSource.create_source(
                        ctx, _link, loop=self.bot.loop
                    )
                except YTDLError as e:
                    await ctx.send("リクエストの手続きにエラーが起きました:{}".format(str(e)))
                else:
                    song = Song(source)
                    await ctx.voice_state.songs.put(song)
        await ctx.send(
            f"**{playlistTitle}からの曲を`{playlist.__len__()}`件キューに追加されました**"
        )
    else:
        async with ctx.typing():
            try:
                source = await YTDLSource.create_source(
                    ctx, search, loop=self.bot.loop
                )
            except YTDLError as e:
                await ctx.send("リクエストの手続きにエラーが起きました:{}".format(str(e)))
            else:
                song = Song(source)
                if not ctx.voice_state.voice:
                    await ctx.invoke(self._join)
                await ctx.voice_state.songs.put(song)
                await ctx.send("キューに追加されました {}".format(str(source)))

目前遇到两个问题:

  • 使用suppress=False时,机器人已成为舞台频道发言者,但完全无法播放音乐;普通语音频道工作正常。
  • 改用request_to_speak()替代suppress=False时,机器人仅能播放单轨,无法播放后续曲目。

原因分析与解决方案

一、suppress=False无法播放的原因

Discord舞台频道的发言权限逻辑中,直接修改suppress=False只是让机器人的发言状态显示为“未静音”,但没有真正获取到语音传输的底层权限。舞台频道的音频输出要求机器人处于“活跃发言”状态,单纯修改suppress属性无法触发Discord服务器的语音流权限分配,导致音频无法输出。

二、request_to_speak()只能播放单轨的原因

request_to_speak()是异步请求发言的方法,但机器人播放完一首曲目后,可能因为频道自动静音规则、播放结束时的状态重置等原因,导致下一首曲目播放时发言权限失效。另外原代码的播放逻辑中,没有在每首曲目开始前重新检查并确保机器人的发言状态。

解决方案

1. 正确处理舞台频道发言权限

放弃直接设置suppress=False,改用request_to_speak(),并且在每一首曲目开始播放前检查发言状态:

# 在播放单首歌的逻辑中添加(比如原代码的播放循环部分)
if ctx.voice_state.voice.channel.type == discord.enums.ChannelType.stage_voice:
    if ctx.me.suppress:
        await ctx.me.request_to_speak()
    await asyncio.sleep(1)  # 留给Discord服务器处理请求的时间

2. 修复播放循环的状态检查

找到原代码中处理歌曲播放循环的函数(通常类似play_next),添加发言状态校验:

async def play_next(self, ctx):
    song = await ctx.voice_state.songs.get()
    if song:
        # 检查舞台频道发言权限
        if ctx.voice_state.voice.channel.type == discord.enums.ChannelType.stage_voice:
            if ctx.me.suppress:
                await ctx.me.request_to_speak()
                await asyncio.sleep(0.5)
        # 播放歌曲
        ctx.voice_state.current = song
        ctx.voice_state.voice.play(song.source, after=lambda _: self.bot.loop.create_task(self.play_next(ctx)))
        await ctx.send(f"再生中: {song.source.title}")
    else:
        ctx.voice_state.current = None
        await ctx.voice_state.voice.disconnect()

3. 确认机器人权限配置

在Discord开发者后台,给机器人开启**「请求发言」权限**,同时确保机器人所在服务器角色拥有舞台频道的发言权限。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 08:03:35