如何在discord.py中使用wait_for()实现等待语音播放停止?
解决Discord机器人等待语音播放结束的问题
我完全理解你的需求——你想要阻塞式地等待语音播放结束后再执行后续操作,而不是用after回调直接跳转到指定逻辑。直接用bot.wait_for(voice.stop())肯定行不通,因为bot.wait_for()需要监听的是Discord的事件名称(比如'message'、'reaction_add'),而voice.stop()是主动停止播放的方法,根本不是一个事件。下面给你两种靠谱的解决方案:
方案一:用asyncio.Event实现优雅等待(推荐)
这个方法其实会用到after回调,但只是用它来触发一个“播放结束”的信号,后续逻辑依然在主协程里按顺序执行,完美符合你的需求:
import asyncio import discord from discord.ext import commands bot = commands.Bot(command_prefix='!', intents=discord.Intents.all()) @bot.command() async def play(ctx, audio_url): # 连接到用户的语音频道 if not ctx.author.voice: return await ctx.send("你得先加入一个语音频道呀!") voice_channel = ctx.author.voice.channel voice = await voice_channel.connect() # 创建一个事件对象,用来标记播放状态 play_finished = asyncio.Event() def on_play_end(error): # 不管播放成功还是出错,都触发事件 play_finished.set() # 可选:播放结束后自动断开连接(也可以放到主协程里做) if voice.is_connected(): asyncio.create_task(voice.disconnect()) # 开始播放,用after回调触发结束事件 voice.play(discord.FFmpegPCMAudio(audio_url), after=on_play_end) # 在这里阻塞,直到播放结束 await play_finished.wait() # 播放结束后,执行你想要的任何操作 await ctx.send("🎵 播放结束!现在可以执行后续步骤啦~") bot.run("你的机器人Token")
这个方案的优点:
- 不会阻塞事件循环,
wait()是异步等待,机器人在等待期间依然能响应其他命令 - 能处理播放出错的情况(比如音频文件损坏),避免主协程一直挂起
- 逻辑清晰,主协程的流程是线性的,比嵌套在
after里好维护
方案二:轮询检查播放状态(不推荐但简单)
如果你完全不想碰after回调,可以用轮询的方式定期检查voice.is_playing()状态,但这种方法不够优雅,而且如果播放意外中断(比如机器人被踢出频道),可能会导致循环卡住:
@bot.command() async def play(ctx, audio_url): if not ctx.author.voice: return await ctx.send("先加入语音频道哦!") voice_channel = ctx.author.voice.channel voice = await voice_channel.connect() voice.play(discord.FFmpegPCMAudio(audio_url)) # 每秒检查一次是否还在播放 while voice.is_playing(): await asyncio.sleep(1) # 播放结束后的操作 await ctx.send("🎵 播放结束!") await voice.disconnect()
注意事项
- 不管用哪种方法,都要确保处理语音连接的异常情况(比如用户中途离开频道、机器人失去连接)
- 如果你的机器人需要处理多个播放任务,记得每个任务用独立的
asyncio.Event对象,避免信号混乱
内容的提问来源于stack exchange,提问作者Sathvik K S
相关产品推荐
相关产品推荐

