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

Nextcord中如何终止已启动的wait_for消息等待?

问题

开发Discord斜杠命令时遇到以下问题:

  • 功能为监听用户发送的图片并保存,基础功能正常
  • 点击停止按钮后,若当前循环已进入bot.wait_for("message")的等待阶段,仍会执行完本次循环迭代,无法立即终止等待

原核心等待代码:

check = lambda msg: interaction.user.id == msg.author.id
message = await bot.wait_for("message", check=check)

原完整命令逻辑(不含装饰器):

async def auto(interaction:Interaction):
    await interaction.response.defer()
    event = asyncio.Event()
    embed = Embed(title="", color=0x2f3136)
    embed.add_field(name = "", value="Make a screenshot with `Win+Shift+S` and press `Ctrl+V` on the chat to send it.", inline= False)
    stop_button = Button(label="Stop", style = ButtonStyle.red)
    async def stopb_callback(interaction):
        event.set()
    stop_button.callback = stopb_callback
    view = View()
    view.add_item(stop_button)
    message_to_delete = await interaction.followup.send(embed=embed, view = view)

    while event.is_set() != True:
        print("Event:",event.is_set())
        if event.is_set() != True:
            check = lambda msg: interaction.user.id == msg.author.id
            message = await bot.wait_for("message", check=check)
            
            if len(message.attachments) > 0:
                print(message.attachments[0].url)
                url = message.attachments[0].url
                #save image
                r = requests.get(url, stream=True)
                imageName = str(uuid.uuid4()) + '.png'
                with open(imageName, 'wb') as out_file:
                    shutil.copyfileobj(r.raw, out_file)
            
            else:
                embed1 = Embed(title="", color=0x2f3136)
                embed1.add_field(name = "", value="You have to send a screenshot to start.", inline= False)
                message1 = await interaction.followup.send(embed=embed1)
                all_messages.append(message1)

尝试过变量控制循环和asyncio.Event,均无法解决等待阶段无法终止的问题。

解决方案

问题根源是bot.wait_for是阻塞式等待,一旦进入等待就无法被外部事件打断。可以通过asyncio.wait同时监听停止事件和消息事件,实现触发停止时立即中断等待。

具体修改点:

  • 用asyncio.wait同时启动两个任务:等待消息、等待停止事件被设置
  • 检查哪个任务先完成:若停止事件触发则退出循环,否则处理消息
  • 移除冗余的event.is_set()判断,简化循环逻辑

修改后的完整代码:

import asyncio
from discord import Interaction, Embed, Button, ButtonStyle, View
import requests
import shutil
import uuid

async def auto(interaction:Interaction):
    await interaction.response.defer()
    event = asyncio.Event()
    embed = Embed(title="", color=0x2f3136)
    embed.add_field(name = "", value="Make a screenshot with `Win+Shift+S` and press `Ctrl+V` on the chat to send it.", inline= False)
    stop_button = Button(label="Stop", style = ButtonStyle.red)
    
    async def stopb_callback(interaction):
        event.set()
        await interaction.response.defer()  # 避免按钮无响应提示
    
    stop_button.callback = stopb_callback
    view = View()
    view.add_item(stop_button)
    message_to_delete = await interaction.followup.send(embed=embed, view = view)

    check = lambda msg: interaction.user.id == msg.author.id
    
    while not event.is_set():
        # 同时等待消息事件和停止事件,任一完成即返回
        done, pending = await asyncio.wait(
            [
                bot.wait_for("message", check=check),
                event.wait()
            ],
            return_when=asyncio.FIRST_COMPLETED
        )
        
        # 检查是否是停止事件触发
        if event.is_set():
            # 取消未完成的任务(如果有的话)
            for task in pending:
                task.cancel()
            break
        
        # 处理收到的消息
        message_task = next(iter(done))
        message = message_task.result()
        
        if len(message.attachments) > 0:
            print(message.attachments[0].url)
            url = message.attachments[0].url
            # 保存图片
            r = requests.get(url, stream=True)
            imageName = str(uuid.uuid4()) + '.png'
            with open(imageName, 'wb') as out_file:
                shutil.copyfileobj(r.raw, out_file)
        else:
            embed1 = Embed(title="", color=0x2f3136)
            embed1.add_field(name = "", value="You have to send a screenshot to start.", inline= False)
            message1 = await interaction.followup.send(embed=embed1)
            all_messages.append(message1)
    
    # 可选:停止后删除提示消息或更新状态
    await message_to_delete.delete()
关键说明
  1. asyncio.wait的return_when=FIRST_COMPLETED参数确保只要消息到达或停止事件触发,就立即返回结果
  2. 停止事件触发时,需要取消未完成的bot.wait_for任务,避免残留异步任务
  3. 给停止按钮的回调添加await interaction.response.defer(),防止Discord提示按钮无响应

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 21:01:35