Несколько экземпляров @tasks.петля в зубчатом колесе

#python #command #discord.py

#python #команда #discord.py

Вопрос:

Я разрабатываю бота Discord, который очищает сообщения на reddit. Теперь я создал команду, чтобы я мог получать x количество сообщений каждые y промежутков времени с помощью @tasks.loop(time=y). Встроил команду в шестеренку, загрузил ее в бота, и она работает так, как ожидалось.

Теперь моя проблема заключается в том, что, насколько я понимаю, одновременно может выполняться только один экземпляр этой команды. то есть, если я выполню команду на сервере, она будет работать нормально, но я не смогу запустить команду на другом сервере, пока я не отменю () запущенный экземпляр.

Мне было интересно, есть ли какой-нибудь способ запустить несколько экземпляров. Нравится способ назвать цикл или что-то в этом роде, или если я должен использовать другую функциональность Discord.py чтобы иметь возможность достичь того, что я пытаюсь сделать.

Редактировать: я опустил некоторые параметры в коде для простоты, но вот код, о котором идет речь

 def launch_client():
bot = Bot()
reddit = client.Client()

bot.add_cog(ScheduleCog(bot, reddit))
 
 class ScheduleCog(commands.Cog):
    time_unit = {'seconds': 1, 'minutes': 60, 'hours': 3600, 'days': 86400, 'weeks': 604800}
    def __init__(self, bot, reddit):
        self.bot = bot
        self.reddit = reddit

    def cog_unload(self):
        self.schedule_loop.cancel()

    @tasks.loop(seconds=1)
    async def schedule_loop(self, ctx, sub, sort, limit, time):
        posts = await self.reddit.get_posts(sub, sort, limit, time)

        if posts is not None:
            for post in posts:
                await ctx.channel.send(post.url)
        else:
            await ctx.channel.send('Error in your request. Please validate fields.')

    @schedule_loop.before_loop
    async def before_schedule_loop(self):
        await self.bot.wait_until_ready()

    @commands.command()
    async def subschedule(self, ctx, interval: int, interval_unit: str, sub: str, sort: str, limit: int,
                          time: str = 'hot'):
        unit: int = self.time_unit[interval_unit]
        self.schedule_loop.change_interval(seconds=interval * unit)
        self.schedule_loop.start(ctx, sub, sort, limit, time)

    @commands.command()
    async def subschedulestop(self, ctx):
        self.schedule_loop.cancel()
 

Итак, я написал это в соответствии с официальной документацией discord.ext.tasks .

Теперь все работает так, как задумано для цикла. Я запускаю команду subschedule(), и она отправляет сообщения через каждый интервал «x» в единицу времени «y».

Теперь, как упоминалось в исходном вопросе, это то, что он работает в одном экземпляре, и я не знаю, как иметь несколько экземпляров. Я хочу иметь возможность запускать несколько экземпляров subschedule() в разных гильдиях или даже несколько в одной и той же, но поскольку это работает прямо сейчас, когда я снова вызываю команду, я получаю сообщение об ошибке времени выполнения «Задача уже запущена и не завершена».

Я просто хочу знать правильный метод, чтобы иметь возможность запускать несколько задач параллельно.

Ответ №1:

Как указано в ошибке, невозможно снова запустить тот же цикл, если он уже запущен, но вы можете динамически создавать новые экземпляры tasks.Loop

 class TaskHandler(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        self._tasks = [] # Empty list, holds all the loops. You can also use a dict if you want to differentiate the tasks somehow

    async def static_loop(self, *args):
        # This is the function that will be 'looping'
        print(args)

    def task_launcher(self, *args, **interval): # The `args` are the arguments passed into the loop
        """Creates new instances of `tasks.Loop`"""
        # Creating the task
        new_task = tasks.loop(**interval)(self.static_loop) # You can also pass a static interval and/or count
        # Starting the task
        new_task.start(*args)
        self._tasks.append(new_task)

    @commands.command()
    async def start_task(self, ctx, *args):
        """Command that launches a new task with the arguments given"""
        self.task_launcher(*args, seconds=1)
        await ctx.send('Task started!')
 

Если вам также нужна поддержка before/after_loop

 new_task = tasks.loop(**interval)(self.static_loop) # You can also pass a static interval and/or count
# Adding before/after_loop functions
new_task.before_loop(BEFORE_LOOP_FUNCTION)
new_task.after_loop(AFTER_LOOP_FUNCTION)