Запуск функций в боте по таймеру
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 секунд) это выглядит так: