如何为同时处理的命令创建函数

Posted

技术标签:

【中文标题】如何为同时处理的命令创建函数【英文标题】:How to create a function for a command that processes concurrently 【发布时间】:2021-12-30 17:32:24 【问题描述】:

当我在 discord 中输入命令 !sleeper 时,紧跟在输入 !hello 之后。该机器人基本上暂停了 10 秒,因为它正在处理 !sleeper。 10 秒后,它发送消息I have been sleeping for 10 seconds,然后立即发送Hello partner!。如果有人发送!sleeper 命令,我怎样才能使整个机器人不会“暂停”。

现在发生了什么:

    我输入!sleeper 我输入!hello 机器人等待 9-10 秒 机器人发送I have been sleeping for 10 seconds 机器人发送Hello partner!

我想要什么:

    我输入!sleeper 我输入!hello 机器人发送Hello partner! 机器人等待 9-10 秒 机器人发送I have been sleeping for 10 seconds

PS:我写了“等待 9-10 秒”,因为我需要大约一秒钟才能输入 !hello

import time

from discord.ext import commands


class Hello(commands.Cog):
    def __init__(self, client):
        self.client = client

    @commands.Cog.listener()
    async def on_ready(self):
        print(f'self.__class__.__name__ Cog is ready')

    @commands.command()
    async def hello(self, ctx):
        await ctx.send('Hello partner!')

    @commands.command()
    async def sleeper(self, ctx):
        await self.sleep_now()
        await ctx.send('I have been sleeping for 10 seconds')

    async def sleep_now(self):
        time.sleep(10)

def setup(client):
    client.add_cog(Hello(client))

【问题讨论】:

【参考方案1】:

您可以尝试使用后台任务来实现此目的。我不确定这是否是最好或最干净的解决方案,但您可以找到更多文档here。

from discord.ext import tasks, commands


class Hello(commands.Cog):
    def __init__(self, client):
        self.client = client
        # flag variable to keep track of if we are waiting for a response from sleeper
        self.sleeping = 0

    @commands.Cog.listener()
    async def on_ready(self):
        print(f'self.__class__.__name__ Cog is ready')

    @commands.command()
    async def hello(self, ctx):
        await ctx.send('Hello partner!')

    @commands.command()
    async def sleeper(self, ctx):
        # if the task is already running, ie. someone already called sleeper
        # you cannot start a background task if it is already running, it will cause an error
        if self.sleep.is_running():
            await ctx.send('Already sleeping!')
        else:
            # start the background task
            self.sleep.start(ctx)

    # The loop runs 2 times
    # It runs immediately when you start the task (when you type !sleeper) and then again in increments of the time specified
    # I use a flag to check when to send the message and cancel the task (after the first ten seconds)
    # cancelling the task stops it from sending additional messages every ten seconds
    @tasks.loop(seconds=10.0)
    async def sleep(self, ctx):
        # helper method to stop the background task right after it sends the message
        async def stop_sleep():
            self.sleep.cancel()
        # checks to see if the task is sleeping or not
        if self.sleeping == 1:
            # reset the value
            self.sleeping = 0
            # send the message
            await ctx.send('I have been sleeping for 10 seconds')
            # stop the task
            await stop_sleep()
        else:
            # keep track of it sleeping
            self.sleeping = 1


def setup(client):
    client.add_cog(Hello(client))

编辑:

有一个很多更好的方法来解决您的问题,而无需使用后台任务。使用 asyncio 代替睡眠时间。

from discord.ext import commands
import asyncio

class Hello(commands.Cog):
    def __init__(self, client):
        self.client = client

    @commands.Cog.listener()
    async def on_ready(self):
        print(f'self.__class__.__name__ Cog is ready')

    @commands.command()
    async def hello(self, ctx):
        await ctx.send('Hello partner!')

    @commands.command()
    async def sleeper(self, ctx):
        await asyncio.sleep(10)
        await ctx.send('I have been sleeping for 10 seconds')

【讨论】:

任务不是我要找的。 sleep_now() 函数代表一个辅助非discord函数,需要一段时间来处理。所以本质上,我如何制作一个可以在命令函数中调用并保持并发的自定义函数。所以是的,我可以使用await asyncio.sleep(10),但是如果我有一个需要 10 秒的自定义流程呢?想象一下,sleep_now() 并不是一个真正的睡眠函数,而是一个需要一段时间才能完成的过程【参考方案2】:

根据您对 Kyle 回答的评论,您希望异步处理函数。

那么你要实现的是一个线程。您可以创建一个线程并在线程内休眠 10 秒(或执行您的耗时任务),然后打印“我已经休眠了 10 秒”。因此它不会阻止 !hello 和其他命令的执行。它看起来像这样

这将是昂贵任务的功能

async def thread_function(ctx):
    time.sleep(10)
    await ctx.send('I have been sleeping for 10 seconds')

你会这样称呼它

 x = threading.Thread(target= thread_function, args=(ctx))

或者,您可以使用事件循环,这是一个更高级的主题。如果你愿意,你可以研究它,但它本质上所做的是它评估你在运行时调用的函数并独立执行它而不创建新线程。我强烈建议您检查一下。 然而,这就是它可能的样子

from concurrent.futures import ThreadPoolExecutor
loop = asyncio.get_event_loop()
_executor = ThreadPoolExecutor(1)
await loop.run_in_executor(_executor, thread_function, ctx)

【讨论】:

以上是关于如何为同时处理的命令创建函数的主要内容,如果未能解决你的问题,请参考以下文章

MFC SDI中 如何为动态创建的按钮添加消息处理函数

如何为 Visual Studio 命令提示符创建批处理文件

如何为JSON创建错误处理函数[重复]

如何为我的 Discord.js 机器人编写事件/命令处理程序?

如何为 Presto 编写自定义窗口函数?

如何为DOS批处理ping指令加时间戳?