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

discord.py已连语音频道时discord.utils.get取Voice clients返回None问题

问题根因

你调用discord.utils.get(bot.voice_clients, guild=guild)时返回None的核心原因是:get方法匹配guild参数时需要传入discord.Guild对象,但你代码里全局变量guild_for_reconnect存储的是str(ctx.guild)即服务器名称字符串,类型不匹配自然找不到对应语音客户端。

除此之外你的代码还存在几个影响重连逻辑的问题:

  • 全局变量guild_for_reconnect_voice_channels存储的是命令触发时的频道列表静态快照,后续服务器内语音频道发生增删改名时,你存储的快照就失效了,会导致重连时找不到目标频道
  • 用asyncio.run在音频回调里启动递归协程是错误用法,会和discord.py本身的事件循环冲突,导致运行异常
  • 用服务器名称作为文件名存储重连信息,一旦服务器改名就会导致重连逻辑失效,应该用服务器ID这种唯一不变的标识

修复方案

调整存储逻辑

不要存服务器名称和静态频道列表,直接存服务器ID、目标频道ID,数字ID不会随服务器/频道改名变化,稳定性更高。

修正匹配逻辑

获取语音客户端时用实时获取的Guild对象匹配,不要用名称字符串。

修正音频循环逻辑

用discord.py自带的事件循环处理回调,不要新开事件循环。

核心修正代码

import discord
from discord.ext import commands
from discord.utils import get
import asyncio
import os
import nacl
from keep_alive import keep_alive

bot = commands.Bot(command_prefix='!')
# 全局变量改存唯一ID,不存字符串和静态对象
reconnect_guild_id = None
reconnect_channel_id = None

@bot.event
async def on_ready():
  print('Logged in as {0.user}'.format(bot))

@bot.command(aliases=['shreksophone'])
async def shrek(ctx, *, join_channel: str=None):
  voice = get(bot.voice_clients, guild=ctx.guild)
  global reconnect_guild_id, reconnect_channel_id
  # 存储服务器唯一ID
  reconnect_guild_id = ctx.guild.id
  guild_id = str(ctx.guild.id)
  author_id = str(ctx.author.id)

  # 音频回调改用事件循环调度协程
  def shrek_repeat(e):
    bot.loop.create_task(_shrek_repeat_task(ctx))

  async def _shrek_repeat_task(ctx):
    f = open('shrek_counter', 'r+')
    counter = f.read()
    new_counter = int(counter) + 1
    f.seek(0)
    f.truncate()
    f.write(str(new_counter))
    f.close()
    current_voice = get(bot.voice_clients, guild=ctx.guild)
    if current_voice and current_voice.is_connected():
      current_voice.play(discord.FFmpegPCMAudio('songs/1 hour of shreksophone.m4a'), after=shrek_repeat)

  if not os.path.exists(guild_id):
    f = open(guild_id, 'w')
    f.write(author_id)
    f.close()
  f = open(guild_id, 'r')
  session_author = f.read()
  f.close()
  if session_author == author_id:
    if voice is None:
      target_channel = None
      if join_channel is None:
        if not ctx.author.voice:
          await ctx.send('You have to be in a voice channel, if you want me to play shreksophone.')
          print('User is not in a voice channel.')
          return
        target_channel = ctx.author.voice.channel
      else:
        target_channel = get(ctx.guild.voice_channels, name=join_channel)
        if not target_channel:
          await ctx.send('指定的语音频道不存在')
          return
      # 连接频道
      voice = await target_channel.connect()
      await ctx.send(f'Playing shreksophone now in "{target_channel.name}" repeatedly.')
      print(f'Playing shreksophone now repeatedly in {ctx.guild.name}.')
      voice.play(discord.FFmpegPCMAudio('songs/1 hour of shreksophone.m4a'), after=shrek_repeat)
      # 存储频道唯一ID
      reconnect_channel_id = target_channel.id
      with open(f"{reconnect_guild_id}_channel", "w") as f:
        f.write(str(reconnect_channel_id))
      bot.loop.create_task(voice_reconnect())

async def voice_reconnect():
  while True:
    if reconnect_guild_id and reconnect_channel_id:
      # 实时获取最新的Guild对象
      guild = bot.get_guild(reconnect_guild_id)
      if not guild:
        await asyncio.sleep(0.25)
        continue
      # 用Guild对象匹配,就能正确拿到语音客户端
      voice = get(bot.voice_clients, guild=guild)
      # 实时获取最新的频道对象
      target_channel = guild.get_channel(reconnect_channel_id)
      if not target_channel:
        await asyncio.sleep(0.25)
        continue
      if not voice or not voice.is_connected():
        # 未连接就重连
        await target_channel.connect()
      elif voice.channel.id != reconnect_channel_id:
        # 频道不符就移动
        await voice.move_to(target_channel)
    await asyncio.sleep(0.25)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.16 13:24:04