Запуск функций в боте по таймеру

Запуск функций в боте по таймеру

Latand

Вы наверное хоть раз задавались вопросом (а может когда-то зададитесь) как реализовать выполнение каких-то функций с определенной регулярностью. Либо раз в час, либо каждый день в 2 часа дня. Либо каждый вторник. Например, вот что спрашивали либо меня лично, либо в чатах:

Каждый час умножать значения одного столбца из бд. 
Архивация и создания бекапа в фоне раз в час
Функция "Как сделать ежедневный бонус?" Допустим у меня есть внутри бота валюта и я понимаю как ее выдавать пользователю, но не понимаю как сделать запустить таймер на 24 часа (т.к. бонус ЕЖЕДНЕВНЫЙ) для каждого пользователя 
Нужен бот удаления сообщений в группе по таймеру
Есть варианты настроить выполнение одного и того же задания каждый день в определённое время?
Добрый день , такой вопрос, есть такая вещь , что надо каждый день удалять базу, хотел использовать библиотеку shedule и он же запускается в цикле, он не бужет тормозить основной ход процесса? 
Как мне указать время отправки сообщения ботом? Весь код который я закладываю в бота исполняется мгновенно, а как реализовать отправку с делеем?


И именно эту тему мы и рассмотрим сейчас, и работать мы будем в связке с AIOgram!


Библиотека APScheduler

Advanced Python Scheduler - это библиотека, которая позволит ставить задачи на повторение с определенными интервалами, или запуском функций в определенное время и определенную дату.

Для начала, её было бы неплохо установить и для этого пропишем в терминале

pip install apscheduler


И хоть работать в ней можно различными способами, как запуск функций в блокирующем потоке (BlockingScheduler тогда кроме них ничего работать не будет), так и в фоновом потоке (BackgroundScheduler), так и в специальных потоках для некоторых фреймворков. У нас фреймворк - это Asyncio, поэтому для асинхронных задач - корутин - там существует специальный класс AsyncIOScheduler

Пишем код

Читая документацию, узнать можно очень много... Но мы пройдемся по конкретному примеру.

Чтобы создать объект шедулера, нам необходимо импортировать его и инициализировать:

from apscheduler.schedulers.asyncio import AsyncIOScheduler

scheduler = AsyncIOScheduler()

Но, если вы работаете с моим темплейтом, то делаю я это в модуле loader.py и выглядит это как-то так:

Теперь, когда я создал этот объект, мне необходимо погрузить в него мои задачи, или если у вас на старте их нет - можно этот пункт пропустить.

Добавлять задачи можно следующим образом:

scheduler.add_job(func, trigger, args, kwargs, ...)

Что это за параметры?

  • func - асинхронная (или синхронная) функция, которую необходимо запустить
  • trigger - способ, как мы хотим задать отсрочку задачи
  • args - аргументы, которые пойдут в вашу функцию
  • kwargs - именованные аргументы, которые пойдут в вашу функцию
  • Другие параметры

И если с func, args, kwargs все понятно, то с trigger - не совсем. Есть три варианта задания отсрочки:

Их вам нужно передавать строкой в параметр trigger, т.е.:

scheduler.add_job(func, "interval", ...)

ИЛИ

scheduler.add_job(func, "cron", ...)

А вот у них уже есть свои параметры, которые нужно туда передавать.

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

Сделаю это я так:

scheduler.add_job(send_message_to_admin, "interval", seconds=5, args=(dp,))

Где dp - диспатчер, из которого я возьму экземпляр бота для отправки сообщений.

Внимание, функция send_message_to_admin - это асинхронная функция и ее не надо делать корутиной. Я не вызываю функцию - не делаю так send_message_to_admin().

ВНИМАНИЕ

Добавлять job можно в любое время, в любом месте кода. Хоть из хендлера!


Короче говоря, выглядеть это будет как-то так:


Или например, вы захотели отправить какое-то сообщение в определенное время. Это можно сделать так:

from loader import scheduler
from datetime import datetime

...

scheduler.add_job(send_message_to_admin, "date", run_date=datetime(2020, 11, 30, 18, 50), args=(dp,))

Тут используется библиотека datetime и я указал конкретную дату - 30 ноября 2020 года, и конкретное время - 18:50. Обратите внимание, что желательно указывать таймзону, если это имеет значение для пользователя.


Еще можно использовать cron. Кто работал с crontab должен знать формат, он позволяет планировать регулярные задачи в определенное время/дни или интервалом. Согласно документации есть следующие параметры:

  • year (int|str) – 4-значное число (год)
  • month (int|str) – номер месяца (1-12)
  • day (int|str) – число месяца (1-31)
  • week (int|str) – число недели в году по ISO (1-53)
  • day_of_week (int|str) – число или строка дня недели (0-6 или mon,tue,wed,thu,fri,sat,sun)
  • hour (int|str) – час (0-23)
  • minute (int|str) – минута (0-59)
  • second (int|str) – секунда (0-59)

Но, как и в кроне, можно указывать не только цифру, но и выражение. Возможные выражения можно смотреть в документации.

Таким образом, можно сделать:

# Запуск с Понедельника по Пятницу в 5:30 (утра) до 2021-05-30 00:00:00

scheduler.add_job(func, 'cron', day_of_week='mon-fri', hour=5, minute=30, end_date='2021-05-30')

Либо

# Запуск в каждое последнее Воскресенье месяца

scheduler.add_job(job_function, "cron", day="last sun")


Теперь, необходимо запустить функцию шедулера. Это делается очень просто:

scheduler.start()


И опять же, если вы используете мой темплейт, то запускать шедулер я буду прямо перед запуском бота в модуле app.py, т.е. вот так:

И теперь надо фукнцию shedule_jobs запустить в функции on_startup. Тогда шедулер начнет работать после запуска бота.

Все вместе (для примера постоянного таймера в 5 секунд) это выглядит так:

Готово!

Report Page