import logging from datetime import datetime, timedelta from typing import List from apscheduler.schedulers.asyncio import AsyncIOScheduler from apscheduler.triggers.cron import CronTrigger from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from database.models import User from database.db import async_session_maker logger = logging.getLogger(__name__) class ReminderService: """Сервис для управления напоминаниями""" def __init__(self, bot): self.bot = bot self.scheduler = AsyncIOScheduler() def start(self): """Запустить планировщик""" # Проверяем напоминания каждые 5 минут self.scheduler.add_job( self.check_and_send_reminders, trigger='interval', minutes=5, id='check_reminders', replace_existing=True ) self.scheduler.start() logger.info("Планировщик напоминаний запущен") def shutdown(self): """Остановить планировщик""" self.scheduler.shutdown() logger.info("Планировщик напоминаний остановлен") async def check_and_send_reminders(self): """Проверить и отправить напоминания пользователям""" try: async with async_session_maker() as session: # Получаем всех пользователей с включенными напоминаниями result = await session.execute( select(User).where( User.reminders_enabled == True, User.daily_task_time.isnot(None) ) ) users = list(result.scalars().all()) current_time = datetime.utcnow() for user in users: if await self._should_send_reminder(user, current_time): await self._send_reminder(user, session) except Exception as e: logger.error(f"Ошибка при проверке напоминаний: {e}") async def _should_send_reminder(self, user: User, current_time: datetime) -> bool: """ Проверить, нужно ли отправлять напоминание пользователю Args: user: Пользователь current_time: Текущее время (UTC) Returns: True если нужно отправить """ if not user.daily_task_time: return False # Парсим время напоминания (формат HH:MM) try: hour, minute = map(int, user.daily_task_time.split(':')) except: return False # Создаем datetime для времени напоминания сегодня (UTC) reminder_time = current_time.replace(hour=hour, minute=minute, second=0, microsecond=0) # Проверяем, не отправляли ли мы уже напоминание сегодня if user.last_reminder_sent: last_sent_date = user.last_reminder_sent.date() current_date = current_time.date() # Если уже отправляли сегодня, не отправляем снова if last_sent_date == current_date: return False # Проверяем, наступило ли время напоминания (с погрешностью 5 минут) time_diff = abs((current_time - reminder_time).total_seconds()) return time_diff < 300 # 5 минут в секундах async def _send_reminder(self, user: User, session: AsyncSession): """ Отправить напоминание пользователю Args: user: Пользователь session: Сессия базы данных """ try: message_text = ( "⏰ Время для практики!\n\n" "Не забудь потренироваться сегодня:\n" "• /task - выполни задания\n" "• /practice - попрактикуй диалог\n" "• /words - добавь новые слова\n\n" "💪 Регулярная практика - ключ к успеху!" ) await self.bot.send_message( chat_id=user.telegram_id, text=message_text ) # Обновляем время последнего напоминания user.last_reminder_sent = datetime.utcnow() await session.commit() logger.info(f"Напоминание отправлено пользователю {user.telegram_id}") except Exception as e: logger.error(f"Ошибка при отправке напоминания пользователю {user.telegram_id}: {e}") # Глобальный экземпляр сервиса (будет инициализирован в main.py) reminder_service: ReminderService = None def init_reminder_service(bot): """Инициализировать сервис напоминаний""" global reminder_service reminder_service = ReminderService(bot) return reminder_service