Discord.py中voice.play的after方法调用时无法发送消息问题
问题描述
使用discord.py开发音乐机器人时,遇到以下问题:
- 从
play命令直接调用play_song方法时,消息发送等功能正常 - 通过
VoiceClient.play的after参数调用play_song时,程序执行到await ctx.send会无限挂起
试过直接调用play函数、传入频道ID而非上下文、去掉await、使用异步生成器等方法,均未解决。
代码示例
from discord.ext import commands from discord import FFmpegPCMAudio from asyncio import run FFMPEG_OPTIONS = {'before_options': '-avioflags direct', 'options': '-vn -sn -dn -movflags +faststart -b:a 49k'} class Play(commands.Cog): def __init__(self, bot): self.bot = bot self.run = False self.voice = "" async def play_song(self, ctx ,data=()): if data != (): data = "+".join(data) print(data) else: print("Passes Automatically") await ctx.send("auto pass") await ctx.send("playing") source = FFmpegPCMAudio("https://www2.iis.fraunhofer.de/AAC/ChID-BLITS-EBU-Narration.mp4", **FFMPEG_OPTIONS) await self.voice.play(source, after=lambda x=None: run(self.play_song(ctx))) @commands.Cog.listener() async def on_ready(self): print("Play is working") @commands.command(pass_context=True) async def play(self, ctx, *initial_user_input): if not self.run: if ctx.author.voice: channel = ctx.message.author.voice.channel self.voice = await channel.connect() self.run = True await self.play_song(ctx, initial_user_input) else: await ctx.send("Channel Not Found") else: await self.play_song(ctx, initial_user_input) async def setup(bot): await bot.add_cog(Play(bot))
问题原因
VoiceClient.play是同步方法,它的after回调会在独立于discord.py主事件循环的线程中同步执行- 使用
asyncio.run()会创建一个全新的事件循环,和bot的主循环完全隔离,导致ctx.send这类依赖主循环的异步操作无法被调度,进而无限挂起 - 原代码中给
self.voice.play加了await,但该方法本身不是异步方法,属于错误用法
修复方案
核心修改点
- 移除
self.voice.play前的await - 用bot的主事件循环来调度
play_song异步任务,替换asyncio.run()
修改后的play_song方法
async def play_song(self, ctx ,data=()): if data != (): data = "+".join(data) print(data) else: print("Passes Automatically") await ctx.send("auto pass") await ctx.send("playing") source = FFmpegPCMAudio("https://www2.iis.fraunhofer.de/AAC/ChID-BLITS-EBU-Narration.mp4", **FFMPEG_OPTIONS) # 用bot的主循环创建异步任务,代替asyncio.run() self.voice.play(source, after=lambda x=None: self.bot.loop.create_task(self.play_song(ctx)))
额外说明
self.bot.loop.create_task()会把异步任务提交到bot的主事件循环中执行,确保ctx.send等操作能正常被调度- 如果需要处理
after回调中的异常,可以在lambda中添加捕获逻辑,或者在play_song内部加入try-except块
内容的提问来源于stack exchange,提问作者MrMez
相关产品推荐
相关产品推荐

