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

如何让Python Discord音乐机器人的音乐队列对各服务器独立?

解决Discord音乐机器人多服务器状态共享问题

核心思路

你的问题根源在于所有服务器共享了music_cog实例里的全局变量。解决方法是按服务器ID单独存储每个服务器的音乐状态,用字典映射每个服务器到它的专属状态数据。

具体实现步骤

1. 封装单服务器状态类

先创建一个内部类,把单个服务器需要的所有音乐状态变量封装进去:

class music_cog(commands.Cog):
    # 定义单服务器的音乐状态容器
    class GuildMusicState:
        def __init__(self):
            self.found_title = ""
            self.found_duration = ""
            self.current_title = ""
            self.current_duration = ""
            self.current_mention = ""
            self.is_playing = False
            self.is_paused = False
            self.music_queue = []
            self.vc = None  # 存储当前服务器的语音连接对象

    def __init__(self, bot):
        self.bot = bot
        # 用字典存储每个服务器的状态,键为服务器ID
        self.guild_states = {}

2. 添加状态获取辅助方法

写一个快捷方法,根据服务器ID获取对应的状态,不存在则自动创建:

def get_guild_state(self, guild_id):
        if guild_id not in self.guild_states:
            self.guild_states[guild_id] = self.GuildMusicState()
        return self.guild_states[guild_id]

3. 修改点歌逻辑

点歌时,根据当前服务器ID获取专属队列,再添加歌曲:

@commands.command(name="play")
async def play(self, ctx, *, query):
    # 获取当前服务器的状态实例
    state = self.get_guild_state(ctx.guild.id)
    # ... 你的歌曲搜索逻辑,最终得到song和voice_channel ...
    state.music_queue.append([song, voice_channel])
    # 后续其他状态操作也全部替换为state.xxx

4. 重构播放逻辑

播放歌曲时,同样针对当前服务器的状态进行操作,注意回调里要传入服务器ID:

def play_next(self, guild_id):
    state = self.get_guild_state(guild_id)
    if len(state.music_queue) > 0:
        state.is_playing = True
        # 获取当前服务器队列的首条歌曲
        m_url = state.music_queue[0][0]['source']
        # 弹出队列
        state.music_queue.pop(0)
        # 播放歌曲,回调传递服务器ID以处理后续队列
        state.vc.play(discord.FFmpegPCMAudio(m_url, **self.FFMPEG_OPTIONS), 
                      after=lambda e: self.play_next(guild_id))
        # 更新当前播放信息
        state.current_title = state.found_title
        state.current_duration = state.found_duration
    else:
        state.is_playing = False

# 在play命令中触发播放的部分
async def play(self, ctx, *, query):
    state = self.get_guild_state(ctx.guild.id)
    # ... 前面的搜索、入队逻辑 ...
    if not state.is_playing:
        # 连接语音频道并保存到当前服务器状态
        state.vc = await voice_channel.connect()
        # 启动播放,传入当前服务器ID
        self.play_next(ctx.guild.id)

5. 同步修改其他操作

所有涉及状态的命令(暂停、查看队列等)都要改为操作对应服务器的状态:

@commands.command(name="pause")
async def pause(self, ctx):
    state = self.get_guild_state(ctx.guild.id)
    if state.is_playing and not state.is_paused:
        state.vc.pause()
        state.is_paused = True
        await ctx.send("播放已暂停")

@commands.command(name="queue")
async def queue(self, ctx):
    state = self.get_guild_state(ctx.guild.id)
    if len(state.music_queue) == 0:
        await ctx.send("当前队列为空")
    else:
        queue_str = "当前播放队列:\n"
        for i, item in enumerate(state.music_queue):
            queue_str += f"{i+1}. {item[0]['title']} - {item[0]['duration']}\n"
        await ctx.send(queue_str)

关键注意事项

  • 必须在after回调中传递服务器ID,否则回调无法识别要处理哪个服务器的队列。
  • 每个服务器的语音连接对象vc要存在对应状态里,避免不同服务器的连接互相干扰。
  • 可选优化:当机器人离开服务器时,从self.guild_states中删除对应状态,节省内存。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 00:27:05