Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion bot/database/competition_dao.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


class CompetitionDao:
"""Data access object for Task"""
"""Data access object for Competition"""

def __init__(self, session):
self.session = session
Expand All @@ -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()
)
17 changes: 14 additions & 3 deletions bot/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -66,7 +67,6 @@


async def main():

scheduler = AsyncIOScheduler()
# Добавляем периодическую задачу восстановления жизней
# Выполняется каждое 10-е число месяца в 00:00 по МСК (UTC+3)
Expand All @@ -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())
62 changes: 56 additions & 6 deletions bot/tasks.py
Original file line number Diff line number Diff line change
@@ -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


Expand All @@ -26,7 +29,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 # Отмечаем, что нарушение обработано
Expand All @@ -35,9 +41,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)

Expand Down Expand Up @@ -75,5 +88,42 @@ async def restore_student_lives():
logger.error(f"Ошибка при восстановлении жизней: {e}")


if __name__ == "__main__":
asyncio.run(sync_education_tasks())
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:
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}")
await notify_event_participants(bot, event, "Сегодня")

for event in tomorrow_events:
logger.info(f"Уведомление о событии завтра: {event.name}")
await notify_event_participants(bot, event, "Завтра")

except Exception as e:
logger.error(f"Ошибка при отправке уведомлений: {e}")


# Закомментировал __main__-блок, потому что этот файл подключается как модуль в основном боте,
# и запуск его напрямую приведёт к ошибке из-за отсутствия аргумента bot.