Добавлены основные функции 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:
2025-12-04 15:46:02 +03:00
parent 2c51fa19b6
commit 72a63eeda5
13 changed files with 1781 additions and 23 deletions

172
bot/handlers/reminder.py Normal file
View File

@@ -0,0 +1,172 @@
from aiogram import Router, F
from aiogram.filters import Command
from aiogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import State, StatesGroup
from database.db import async_session_maker
from services.user_service import UserService
router = Router()
class ReminderStates(StatesGroup):
"""Состояния для настройки напоминаний"""
waiting_for_time = State()
@router.message(Command("reminder"))
async def cmd_reminder(message: Message):
"""Обработчик команды /reminder"""
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, message.from_user.id)
if not user:
await message.answer("Сначала запусти бота командой /start")
return
# Формируем текст
status = "✅ Включены" if user.reminders_enabled else "❌ Выключены"
time_text = user.daily_task_time if user.daily_task_time else "Не установлено"
text = (
f"⏰ <b>Напоминания</b>\n\n"
f"Статус: {status}\n"
f"Время: {time_text} UTC\n\n"
f"Напоминания помогут не забывать о ежедневной практике.\n"
f"Бот будет присылать сообщение в выбранное время каждый день."
)
# Создаем кнопки
keyboard = []
if user.reminders_enabled:
keyboard.append([
InlineKeyboardButton(text="❌ Выключить", callback_data="reminder_disable")
])
else:
keyboard.append([
InlineKeyboardButton(text="✅ Включить", callback_data="reminder_enable")
])
keyboard.append([
InlineKeyboardButton(text="⏰ Изменить время", callback_data="reminder_set_time")
])
reply_markup = InlineKeyboardMarkup(inline_keyboard=keyboard)
await message.answer(text, reply_markup=reply_markup)
@router.callback_query(F.data == "reminder_enable")
async def enable_reminders(callback: CallbackQuery):
"""Включить напоминания"""
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
if not user.daily_task_time:
await callback.answer(
"Сначала установи время напоминаний!",
show_alert=True
)
return
user.reminders_enabled = True
await session.commit()
await callback.answer("✅ Напоминания включены!")
await callback.message.edit_text(
f"✅ <b>Напоминания включены!</b>\n\n"
f"Время: {user.daily_task_time} UTC\n\n"
f"Ты будешь получать ежедневные напоминания о практике."
)
@router.callback_query(F.data == "reminder_disable")
async def disable_reminders(callback: CallbackQuery):
"""Выключить напоминания"""
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
user.reminders_enabled = False
await session.commit()
await callback.answer("❌ Напоминания выключены")
await callback.message.edit_text(
"❌ <b>Напоминания выключены</b>\n\n"
"Используй /reminder чтобы включить их снова."
)
@router.callback_query(F.data == "reminder_set_time")
async def set_reminder_time_prompt(callback: CallbackQuery, state: FSMContext):
"""Запросить время для напоминаний"""
await state.set_state(ReminderStates.waiting_for_time)
await callback.message.edit_text(
"⏰ <b>Установка времени напоминаний</b>\n\n"
"Отправь время в формате <b>HH:MM</b> (UTC)\n\n"
"Примеры:\n"
"• <code>09:00</code> - 9 утра по UTC\n"
"• <code>18:30</code> - 18:30 по UTC\n"
"• <code>20:00</code> - 8 вечера по UTC\n\n"
"💡 UTC = МСК - 3 часа\n"
"(если хочешь 12:00 по МСК, введи 09:00)\n\n"
"Отправь /cancel для отмены"
)
await callback.answer()
@router.message(Command("cancel"), ReminderStates.waiting_for_time)
async def cancel_set_time(message: Message, state: FSMContext):
"""Отменить установку времени"""
await state.clear()
await message.answer("❌ Установка времени отменена")
@router.message(ReminderStates.waiting_for_time)
async def process_reminder_time(message: Message, state: FSMContext):
"""Обработать введённое время"""
time_str = message.text.strip()
# Валидация формата HH:MM
try:
parts = time_str.split(':')
if len(parts) != 2:
raise ValueError()
hour, minute = int(parts[0]), int(parts[1])
if not (0 <= hour <= 23 and 0 <= minute <= 59):
raise ValueError()
# Формат OK
formatted_time = f"{hour:02d}:{minute:02d}"
except:
await message.answer(
"❌ Неверный формат времени!\n\n"
"Используй формат <b>HH:MM</b> (например, 09:00 или 18:30)\n"
"Или отправь /cancel для отмены"
)
return
# Сохраняем время
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, message.from_user.id)
user.daily_task_time = formatted_time
# Автоматически включаем напоминания
user.reminders_enabled = True
await session.commit()
await state.clear()
await message.answer(
f"✅ <b>Время установлено!</b>\n\n"
f"Напоминания: <b>{formatted_time} UTC</b>\n"
f"Статус: <b>Включены</b>\n\n"
f"Ты будешь получать ежедневные напоминания о практике.\n"
f"Используй /reminder для изменения настроек."
)