Добавлены основные функции MVP: тематические подборки, импорт слов, диалоговая практика, напоминания и тест уровня
Новые команды: - /words [тема] - AI-генерация тематических подборок слов (10 слов по теме с учётом уровня) - /import - извлечение до 15 ключевых слов из текста (книги, статьи, песни) - /practice - диалоговая практика с AI в 6 сценариях (ресторан, магазин, путешествие, работа, врач, общение) - /reminder - настройка ежедневных напоминаний по расписанию - /level_test - тест из 7 вопросов для определения уровня английского (A1-C2) Основные изменения: - AI сервис: добавлены методы generate_thematic_words, extract_words_from_text, start_conversation, continue_conversation, generate_level_test - Диалоговая практика: исправление ошибок в реальном времени, подсказки, перевод реплик - Напоминания: APScheduler для ежедневной отправки напоминаний в выбранное время - Тест уровня: автоматическое определение уровня при регистрации, можно пропустить - База данных: добавлены поля reminders_enabled, last_reminder_sent - Vocabulary service: метод get_word_by_original для проверки дубликатов - Зависимости: apscheduler==3.10.4 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
141
services/reminder_service.py
Normal file
141
services/reminder_service.py
Normal file
@@ -0,0 +1,141 @@
|
||||
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 = (
|
||||
"⏰ <b>Время для практики!</b>\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
|
||||
Reference in New Issue
Block a user