如何让discord.py中的wait_for方法在单次运行中多次触发?
问题根源
wait_for检查逻辑缺失必要过滤条件:未限制反应绑定的消息对象,未过滤机器人自身触发的反应事件,会导致非目标操作触发逻辑异常- 反应移除逻辑错误:固定移除命令发起者
ctx.author的反应,实际触发操作的用户与ctx.author不一致时,移除操作会抛出异常,直接终止监听循环 - 未处理切歌场景:切换下一首时没有终止旧播放消息的监听循环,也没有为新播放消息启动新的监听逻辑,导致新消息的反应无响应
- 无异常捕获机制:移除反应、调用语音客户端方法时出现的错误会直接打断循环,导致后续操作无响应
修复步骤
- 完善检查函数,过滤条件新增:反应对应的消息是当前播放控制消息、触发用户不是机器人
- 调整反应移除逻辑,删除实际触发操作的用户的对应反应
- 封装控制监听逻辑为独立协程,切歌时先终止上一首的监听任务、清除旧消息的反应,再为新消息启动新的监听
- 新增异常捕获,避免非核心错误打断监听循环
修正后代码示例
import asyncio # 全局/服务器级变量,存储当前正在运行的控制监听任务,多服务器场景可按服务器id分组存储避免冲突 current_control_task = None current_control_msg = None # 以下为播放命令内的逻辑 msg = await ctx.channel.send(f"`Now playing: {var3}`\n{url}") await msg.add_reaction("⏸") await msg.add_reaction("▶") # 先终止旧的监听任务,清理旧消息反应 if current_control_task and not current_control_task.done(): current_control_task.cancel() if current_control_msg: try: await current_control_msg.clear_reactions() except: pass # 定义独立的反应监听协程 async def control_listen(): while True: try: reaction, user = await client.wait_for( "reaction_add", check=lambda r, u: r.message.id == msg.id and not u.bot and str(r.emoji) in ["⏸", "▶"], timeout=90.0 ) except asyncio.TimeoutError: await msg.clear_reactions() break except asyncio.CancelledError: # 切歌时主动取消任务的异常,直接退出即可 break try: # 移除实际触发用户的对应反应 await msg.remove_reaction(reaction.emoji, user) # 校验语音客户端状态,避免断开连接后调用方法报错 if not ctx.voice_client or not ctx.voice_client.is_connected(): break if str(reaction.emoji) == "⏸": if ctx.voice_client.is_playing(): await ctx.voice_client.pause() elif str(reaction.emoji) == "▶": if ctx.voice_client.is_paused(): await ctx.voice_client.resume() except: # 忽略权限不足、反应已被删除等非致命错误,继续监听后续操作 continue # 启动新的监听任务,更新全局存储变量 current_control_msg = msg current_control_task = client.loop.create_task(control_listen())
内容的提问来源于stack exchange,提问作者ZT255
相关产品推荐
相关产品推荐

