From 10a249b81848e436757aeba4e6be79657846314c Mon Sep 17 00:00:00 2001 From: AVADA-KEDAVOR Date: Sat, 31 May 2025 12:41:21 +0300 Subject: [PATCH 1/3] =?UTF-8?q?CTFBOT-34=20=D0=9D=D0=B0=D0=BF=D0=BE=D0=BC?= =?UTF-8?q?=D0=B8=D0=BD=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=BE=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=D0=B1=D0=BB=D0=B8=D0=B6=D0=B0=D1=8E=D1=89=D0=B8=D1=85?= =?UTF-8?q?=D1=81=D1=8F=20=D0=B4=D0=B5=D0=B4=D0=BB=D0=B0=D0=B9=D0=BD=D0=B0?= =?UTF-8?q?=D1=85=20=D0=B8=20=D0=BC=D0=B5=D1=80=D0=BE=D0=BF=D1=80=D0=B8?= =?UTF-8?q?=D1=8F=D1=82=D0=B8=D1=8F=D1=85.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot/database/competition_dao.py | 13 ++++++++++++- bot/main.py | 17 ++++++++++++++--- bot/tasks.py | 28 ++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/bot/database/competition_dao.py b/bot/database/competition_dao.py index 154fd3d..24d26e2 100644 --- a/bot/database/competition_dao.py +++ b/bot/database/competition_dao.py @@ -4,7 +4,7 @@ class CompetitionDao: - """Data access object for Task""" + """Data access object for Competition""" def __init__(self, session): self.session = session @@ -29,3 +29,14 @@ def add_competition( self.session.commit() self.session.refresh(new_competition) return new_competition + + def get_events_between(self, start_time: datetime, end_time: datetime): + """Получить события в заданном временном диапазоне.""" + return ( + self.session.query(Competition) + .filter( + Competition.date >= start_time, + Competition.date < end_time + ) + .all() + ) diff --git a/bot/main.py b/bot/main.py index 440f4be..d677d22 100644 --- a/bot/main.py +++ b/bot/main.py @@ -22,6 +22,7 @@ from settings import config from middlewares import AuthMiddleware from tasks import sync_education_tasks +from tasks import send_event_notifications # Включаем логирование, чтобы не пропустить важные сообщения logging.basicConfig(level=logging.INFO) @@ -66,7 +67,6 @@ async def main(): - scheduler = AsyncIOScheduler() # Добавляем периодическую задачу восстановления жизней # Выполняется каждое 10-е число месяца в 00:00 по МСК (UTC+3) @@ -82,14 +82,25 @@ async def main(): name="Восстановление жизней студентов", replace_existing=True, ) - + scheduler.add_job( + send_event_notifications, + args=[bot], + trigger=CronTrigger( + hour=6, + minute=0, + timezone=timezone("Europe/Moscow"), + ), + id="send_event_notifications", + name="Отправка уведомлений о событиях", + replace_existing=True, + ) # Запускаем планировщик scheduler.start() await bot.set_my_commands(commands) # Запускаем задачу синхронизации задач asyncio.create_task(sync_education_tasks(bot)) # Фоновая задача - await dp.start_polling(bot) + if __name__ == "__main__": asyncio.run(main()) diff --git a/bot/tasks.py b/bot/tasks.py index 580d0d6..f78b46d 100644 --- a/bot/tasks.py +++ b/bot/tasks.py @@ -1,11 +1,14 @@ import asyncio +from datetime import datetime, timedelta from aiogram import Bot from logging import getLogger +from pytz import timezone from utils.notifications import Notifications from database.db import get_db from database.user_dao import UserDAO +from database.competition_dao import CompetitionDao from utils.root_me import get_solved_tasks_of_student @@ -75,5 +78,30 @@ async def restore_student_lives(): logger.error(f"Ошибка при восстановлении жизней: {e}") +async def send_event_notifications(bot: Bot): + """Отправка уведомлений о событиях за 1 день и в день мероприятия.""" + try: + moscow_tz = timezone('Europe/Moscow') + now = datetime.now(moscow_tz) + today_start = now.replace(hour=0, minute=0, second=0, microsecond=0) + today_end = today_start + timedelta(days=1) + tomorrow_start = today_start + timedelta(days=1) + tomorrow_end = today_start + timedelta(days=2) + with get_db() as session: + dao = CompetitionDao(session) + today_events = dao.get_events_between(today_start, today_end) + tomorrow_events = dao.get_events_between(tomorrow_start, tomorrow_end) + for event in today_events: + logger.info(f"Уведомление о событии сегодня: {event.name}") + # Здесь можно добавить отправку через bot, например: + # await bot.send_message(chat_id, f"Сегодня: {event.name} в {event.date}") + for event in tomorrow_events: + logger.info(f"Уведомление о событии завтра: {event.name}") + # Здесь можно добавить отправку через bot, например: + # await bot.send_message(chat_id, f"Завтра: {event.name} в {event.date}") + except Exception as e: + logger.error(f"Ошибка при отправке уведомлений: {e}") + + if __name__ == "__main__": asyncio.run(sync_education_tasks()) From 19dea7f1f277e3bc8b39c4fa331726df18e10732 Mon Sep 17 00:00:00 2001 From: AVADA-KEDAVOR Date: Tue, 3 Jun 2025 01:21:30 +0300 Subject: [PATCH 2/3] =?UTF-8?q?CTFBOT-34=20=D0=9D=D0=B0=D0=BF=D0=BE=D0=BC?= =?UTF-8?q?=D0=B8=D0=BD=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=BE=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=D0=B1=D0=BB=D0=B8=D0=B6=D0=B0=D1=8E=D1=89=D0=B8=D1=85?= =?UTF-8?q?=D1=81=D1=8F=20=D0=B4=D0=B5=D0=B4=D0=BB=D0=B0=D0=B9=D0=BD=D0=B0?= =?UTF-8?q?=D1=85=20=D0=B8=20=D0=BC=D0=B5=D1=80=D0=BE=D0=BF=D1=80=D0=B8?= =?UTF-8?q?=D1=8F=D1=82=D0=B8=D1=8F=D1=85.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot/tasks.py | 54 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/bot/tasks.py b/bot/tasks.py index f78b46d..f300fd6 100644 --- a/bot/tasks.py +++ b/bot/tasks.py @@ -11,6 +11,8 @@ from database.competition_dao import CompetitionDao from utils.root_me import get_solved_tasks_of_student +import os + logger = getLogger() @@ -29,7 +31,10 @@ async def sync_education_tasks(bot: Bot): ) for task in user.tasks: task.completed = task.name in solved_tasks - if not task.completed and task.is_expired and not task.violation_recorded: + if ( + not task.completed and task.is_expired + and not task.violation_recorded + ): user.lives -= 1 user.violations += 1 task.violation_recorded = True # Отмечаем, что нарушение обработано @@ -38,9 +43,16 @@ async def sync_education_tasks(bot: Bot): ) logger.info(teacher_message) notify = Notifications(bot) - await notify.say_about_deadline_fail(teacher_message) - student_message = f"Ты потерял 1 HP за задачу {task.name}. 😢 Пожалуйста, старайся выполнять задания вовремя, чтобы избежать потерь.\ - Если у тебя есть вопросы или трудности, не стесняйся обращаться за помощью в общий чат." + await notify.say_about_deadline_fail( + teacher_message + ) + student_message = ( + f"Ты потерял 1 HP за задачу {task.name}. 😢 " + "Пожалуйста, старайся выполнять задания вовремя, " + "чтобы избежать потерь. Если у тебя есть вопросы " + "или трудности, не стесняйся обращаться за помощью " + "в общий чат." + ) logger.info(student_message) await notify._say_student(user, student_message) @@ -87,21 +99,41 @@ async def send_event_notifications(bot: Bot): today_end = today_start + timedelta(days=1) tomorrow_start = today_start + timedelta(days=1) tomorrow_end = today_start + timedelta(days=2) + with get_db() as session: dao = CompetitionDao(session) today_events = dao.get_events_between(today_start, today_end) - tomorrow_events = dao.get_events_between(tomorrow_start, tomorrow_end) + tomorrow_events = dao.get_events_between( + tomorrow_start, tomorrow_end + ) + for event in today_events: logger.info(f"Уведомление о событии сегодня: {event.name}") - # Здесь можно добавить отправку через bot, например: - # await bot.send_message(chat_id, f"Сегодня: {event.name} в {event.date}") + notify = Notifications(bot) + for participation in event.participations: + user = participation.user + msg = ( + f"Сегодня: {event.name} в " + f"{event.date.strftime('%H:%M')}." + ) + await notify._say_student(user, msg) + for event in tomorrow_events: logger.info(f"Уведомление о событии завтра: {event.name}") - # Здесь можно добавить отправку через bot, например: - # await bot.send_message(chat_id, f"Завтра: {event.name} в {event.date}") + notify = Notifications(bot) + for participation in event.participations: + user = participation.user + msg = ( + f"Завтра: {event.name} в " + f"{event.date.strftime('%H:%M')}." + ) + await notify._say_student(user, msg) + except Exception as e: logger.error(f"Ошибка при отправке уведомлений: {e}") -if __name__ == "__main__": - asyncio.run(sync_education_tasks()) +#if __name__ == "__main__": +# asyncio.run(sync_education_tasks()) +# Закомментировал __main__-блок, потому что этот файл подключается как модуль в основном боте, +# и запуск его напрямую приведёт к ошибке из-за отсутствия аргумента bot. \ No newline at end of file From 829540df902214e02ed2c8053c30c6ce267e1e8d Mon Sep 17 00:00:00 2001 From: AVADA-KEDAVOR Date: Tue, 3 Jun 2025 01:28:17 +0300 Subject: [PATCH 3/3] =?UTF-8?q?CTFBOT-34=20=D0=9D=D0=B0=D0=BF=D0=BE=D0=BC?= =?UTF-8?q?=D0=B8=D0=BD=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=BE=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=D0=B1=D0=BB=D0=B8=D0=B6=D0=B0=D1=8E=D1=89=D0=B8=D1=85?= =?UTF-8?q?=D1=81=D1=8F=20=D0=B4=D0=B5=D0=B4=D0=BB=D0=B0=D0=B9=D0=BD=D0=B0?= =?UTF-8?q?=D1=85=20=D0=B8=20=D0=BC=D0=B5=D1=80=D0=BE=D0=BF=D1=80=D0=B8?= =?UTF-8?q?=D1=8F=D1=82=D0=B8=D1=8F=D1=85.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot/tasks.py | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/bot/tasks.py b/bot/tasks.py index f300fd6..866613b 100644 --- a/bot/tasks.py +++ b/bot/tasks.py @@ -11,8 +11,6 @@ from database.competition_dao import CompetitionDao from utils.root_me import get_solved_tasks_of_student -import os - logger = getLogger() @@ -90,6 +88,14 @@ async def restore_student_lives(): logger.error(f"Ошибка при восстановлении жизней: {e}") +async def notify_event_participants(bot: Bot, event, prefix: str): + notify = Notifications(bot) + for participation in event.participations: + user = participation.user + msg = f"{prefix}: {event.name} в {event.date.strftime('%H:%M')}." + await notify._say_student(user, msg) + + async def send_event_notifications(bot: Bot): """Отправка уведомлений о событиях за 1 день и в день мероприятия.""" try: @@ -109,31 +115,15 @@ async def send_event_notifications(bot: Bot): for event in today_events: logger.info(f"Уведомление о событии сегодня: {event.name}") - notify = Notifications(bot) - for participation in event.participations: - user = participation.user - msg = ( - f"Сегодня: {event.name} в " - f"{event.date.strftime('%H:%M')}." - ) - await notify._say_student(user, msg) + await notify_event_participants(bot, event, "Сегодня") for event in tomorrow_events: logger.info(f"Уведомление о событии завтра: {event.name}") - notify = Notifications(bot) - for participation in event.participations: - user = participation.user - msg = ( - f"Завтра: {event.name} в " - f"{event.date.strftime('%H:%M')}." - ) - await notify._say_student(user, msg) + await notify_event_participants(bot, event, "Завтра") except Exception as e: logger.error(f"Ошибка при отправке уведомлений: {e}") -#if __name__ == "__main__": -# asyncio.run(sync_education_tasks()) # Закомментировал __main__-блок, потому что этот файл подключается как модуль в основном боте, -# и запуск его напрямую приведёт к ошибке из-за отсутствия аргумента bot. \ No newline at end of file +# и запуск его напрямую приведёт к ошибке из-за отсутствия аргумента bot.