【问题标题】:How can I save a command coroutine in a variable that can be accessed through another command?如何将命令协程保存在可以通过另一个命令访问的变量中?
【发布时间】:2021-03-15 17:22:14
【问题描述】:

我试图在我的 discord 机器人中实现的是一个系统,该系统会在同一用户调用另一个命令时立即停止正在运行的命令协程,以尝试为每个用户保持唯一的活动会话。

所以本质上我需要能够访问那个协程对象并从另一个协程中停止它。

到目前为止,我所尝试的方法在我看来是合乎逻辑的,但我确信对于 discord API 如何处理协程存在一些误解。这是一个基本示例:

active_sessions = {}

@bot.command()
async def first_command(ctx):

    # Saving the coroutine in the dictionary with the user ID as key:
    active_sessions[ctx.author.id] = ctx.command.callback

    # do stuff

@bot.command()
async def second_command(ctx):

    # Accessing the coroutine object and trying to stop it.
    try:
        coro = active_sessions[ctx.author.id]
        coro.close()
    
    except KeyError:
        pass

抛出一个AttributeError 声明函数对象没有关闭属性。 但是,根据 discord.py 文档,command.callback 指的是应该具有 close 方法的协程对象。还尝试了coro.cancel(),结果相同。

我在这里在概念上做错了,但我不确定到底是什么,而且我不知道如何正确实施我的想法,所以非常感谢任何帮助。

编辑

所以,由于我的目标可能还不清楚,这就是我想要完成的目标。

我正在制作一个类似于游戏的不和谐机器人。用户可以运行多个命令,其中许多命令会等待一定的时间以等待命令作者的反应或消息。

但是这个用户可能在没有“完成”第一个命令的情况下运行另一个命令,如果他们回到第一个命令,他们的反应仍然会被接受。

我试图避免这种行为,并让用户专注于“单一会话”;无论他们运行什么命令,前一个命令的执行都需要自动停止。

因此,由于对协程的错误误解,我是如何实现这一点的,即拥有一个字典,将每个用户与您当前正在运行的命令的循环相关联。如果另一个循环开始时另一个循环仍在运行,则“旧循环”将停止,字典中的相应条目将使用新循环更新:

active_sessions = {}

@bot.command()
async def first_command(ctx):

    # In this first command I want to assign the current loop to a variable and
    # add it to the active_sessions dictionary along with the ID of the command author.
    
    # Then there's a loop that waits for user's reaction. 
    # Ignore the details such as the check function.       
    while True:
        try:
           reaction, user = await bot.wait_for("reaction_add",timeout=60,check=check)
           
           # some if conditions here make the bot behave differently according 
           # to the specific reaction added
        
        except asyncio.TimeoutError:
           return

@bot.command()
async def second_command(ctx):

    # As soon as this second command is invoked, I need to access the 'loop object' 
    # previously stored in the dictionary (if any) and stop it from here.

【问题讨论】:

  • 您到底想达到什么目的。 coro.cancel? ctx.command.callback 返回的是协程,不是可以取消的运行循环
  • 感谢您的评论。是的,我确定我指的是错误的对象。如果可能的话,我如何管理来自另一个循环的特定循环?
  • 确实如此。但是你应该说清楚,你想要阻止或控制什么?你现在的方法肯定行不通。更深入地了解您正在尝试做的事情将有助于轻松获得答案。
  • 编辑了更多信息。

标签: python discord discord.py coroutine


【解决方案1】:

有两种方法可以真正阻止wait_for 工作。

  1. 切换到机器人事件。
import json
@bot.event()
async def on_reaction_add(reaction, user):
   # use a config variable
   with open('config.json', 'r') as f:
       data = json.load(f)
   if data['wait_for_reaction']:
       # do stuff here
   else:
       return

@commands.command()
async def activate(ctx):
    #activates the listenere
    with open('config.json', 'r') as f:
       data = json.load(f)
    data['wait_for_reaction'] = True
    with open('config.json', 'w') as f:
       json.write(data, f, indent=4)
    await ctx.send('activated listener')

@commands.command()
async def deactivate(ctx):
with open('config.json', 'r') as f:
       data = json.load(f)
    data['wait_for_reaction'] = False
    with open('config.json', 'w') as f:
       json.write(data, f, indent=4)
    await ctx.send('activated listener')

您也可以使用全局变量或.env 文件来加载配置变量

  1. 使用任务
from discord.ext import tasks

@tasks.loop(seconds=0)
async def task(ctx):
   try: 
       reaction, user = await bot.wait_for("reaction_add",timeout=60, check=check)
   except asyncio.TimeoutError:
       return

@commands.command():
async def activate(ctx):
   try: 
      task.start(ctx)
   except RuntimeError:
      await ctx.send("task is already running")
   finally:
      await ctx.send('started task')

@commands.command()
async def deactivate(ctx):
   task.stop()
   await ctx.send('stopped task')

问题是任务是您一次只能运行一个任务,因此您不能在多个上下文中使用该命令。有一种解决方法,但我不建议使用它。仅当您的机器人是只有您运行的个人机器人时才使用任务。

参考资料:

注意: 如果您打算使用.env 文件来存储您的变量。查看dotenv

【讨论】:

  • 非常感谢您的回答。我发现第一种方法非常有趣。问题是我可能需要为一个命令启用 on_reaction 侦听器,而为另一个命令禁用,因此我无法使用布尔标志来管理它。
猜你喜欢
  • 2019-11-17
  • 1970-01-01
  • 1970-01-01
  • 2019-12-19
  • 2018-03-20
  • 2012-03-02
  • 1970-01-01
  • 1970-01-01
  • 2022-07-29
相关资源
最近更新 更多