from aiogram import Router, F from aiogram.filters import CommandStart, Command from aiogram.types import ( Message, InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery, ReplyKeyboardMarkup, KeyboardButton, ) from aiogram.fsm.context import FSMContext from database.db import async_session_maker from services.user_service import UserService router = Router() # Тексты кнопок главного меню BTN_ADD = "➕ Добавить слово" BTN_VOCAB = "📚 Словарь" BTN_TASK = "🧠 Задание" BTN_PRACTICE = "💬 Практика" BTN_WORDS = "🎯 Тематические слова" BTN_IMPORT = "📖 Импорт из текста" BTN_STATS = "📊 Статистика" BTN_SETTINGS = "⚙️ Настройки" def main_menu_keyboard() -> ReplyKeyboardMarkup: """Клавиатура с основными командами (кнопки отправляют команды).""" return ReplyKeyboardMarkup( resize_keyboard=True, keyboard=[ [ KeyboardButton(text=BTN_ADD), KeyboardButton(text=BTN_VOCAB), ], [ KeyboardButton(text=BTN_TASK), KeyboardButton(text=BTN_PRACTICE), ], [ KeyboardButton(text=BTN_WORDS), KeyboardButton(text=BTN_IMPORT), ], [ KeyboardButton(text=BTN_STATS), KeyboardButton(text=BTN_SETTINGS), ], ], ) @router.message(CommandStart()) async def cmd_start(message: Message, state: FSMContext): """Обработчик команды /start""" async with async_session_maker() as session: # Проверяем, существует ли пользователь existing_user = await UserService.get_user_by_telegram_id(session, message.from_user.id) is_new_user = existing_user is None # Создаём или получаем пользователя user = await UserService.get_or_create_user( session, telegram_id=message.from_user.id, username=message.from_user.username ) if is_new_user: # Новый пользователь await message.answer( f"👋 Привет, {message.from_user.first_name}!\n\n" f"Я бот для изучения английского языка. Помогу тебе:\n" f"📚 Пополнять словарный запас (ручное/тематическое/из текста)\n" f"✍️ Выполнять интерактивные задания\n" f"💬 Практиковать язык в диалоге с AI\n" f"📊 Отслеживать свой прогресс\n\n" f"Команды:\n" f"• /add [слово] - добавить слово\n" f"• /words [тема] - тематическая подборка\n" f"• /import - импорт из текста\n" f"• /vocabulary - мой словарь\n" f"• /task - задания\n" f"• /practice - диалог с AI\n" f"• /stats - статистика\n" f"• /settings - настройки\n" f"• /reminder - напоминания\n" f"• /help - полная справка", reply_markup=main_menu_keyboard(), ) # Предлагаем пройти тест уровня keyboard = InlineKeyboardMarkup(inline_keyboard=[ [InlineKeyboardButton(text="📊 Пройти тест уровня", callback_data="offer_level_test")], [InlineKeyboardButton(text="➡️ Пропустить", callback_data="skip_level_test")] ]) await message.answer( "🎯 Определим твой уровень?\n\n" "Короткий тест (7 вопросов) поможет подобрать задания под твой уровень.\n" "Это займёт 2-3 минуты.\n\n" "Или можешь пропустить и установить уровень вручную позже в /settings", reply_markup=keyboard ) else: # Существующий пользователь await message.answer( f"С возвращением, {message.from_user.first_name}! 👋\n\n" f"Готов продолжить обучение?\n\n" f"Быстрый доступ:\n" f"• /vocabulary - посмотреть словарь\n" f"• /task - получить задание\n" f"• /practice - практика диалога\n" f"• /words [тема] - тематическая подборка\n" f"• /stats - статистика\n" f"• /help - все команды", reply_markup=main_menu_keyboard(), ) @router.message(Command("menu")) async def cmd_menu(message: Message): """Показать клавиатуру с основными командами.""" await message.answer("Главное меню доступно ниже ⤵️", reply_markup=main_menu_keyboard()) # Обработчики кнопок главного меню (по тексту) @router.message(F.text == BTN_ADD) async def btn_add_pressed(message: Message, state: FSMContext): from bot.handlers.vocabulary import AddWordStates await message.answer( "Отправь слово, которое хочешь добавить:\n" "Например: /add elephant\n\n" "Или просто отправь слово без команды!" ) await state.set_state(AddWordStates.waiting_for_word) @router.message(F.text == BTN_VOCAB) async def btn_vocab_pressed(message: Message): from bot.handlers.vocabulary import cmd_vocabulary await cmd_vocabulary(message) @router.message(F.text == BTN_TASK) async def btn_task_pressed(message: Message, state: FSMContext): from bot.handlers.tasks import cmd_task await cmd_task(message, state) @router.message(F.text == BTN_PRACTICE) async def btn_practice_pressed(message: Message, state: FSMContext): from bot.handlers.practice import cmd_practice await cmd_practice(message, state) @router.message(F.text == BTN_IMPORT) async def btn_import_pressed(message: Message, state: FSMContext): from bot.handlers.import_text import cmd_import await cmd_import(message, state) @router.message(F.text == BTN_STATS) async def btn_stats_pressed(message: Message): from bot.handlers.tasks import cmd_stats await cmd_stats(message) @router.message(F.text == BTN_SETTINGS) async def btn_settings_pressed(message: Message): from bot.handlers.settings import cmd_settings await cmd_settings(message) @router.message(F.text == BTN_WORDS) async def btn_words_pressed(message: Message, state: FSMContext): """Подсказать про тематические слова и показать быстрые темы.""" from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton text = ( "📚 Тематические подборки слов\n\n" "Используй: /words [тема]\n\n" "Популярные темы:" ) keyboard = InlineKeyboardMarkup(inline_keyboard=[ [InlineKeyboardButton(text="✈️ Путешествия", callback_data="menu_theme_travel")], [InlineKeyboardButton(text="🍔 Еда", callback_data="menu_theme_food")], [InlineKeyboardButton(text="💼 Работа", callback_data="menu_theme_work")], [InlineKeyboardButton(text="🌿 Природа", callback_data="menu_theme_nature")], [InlineKeyboardButton(text="💻 Технологии", callback_data="menu_theme_technology")], ]) await message.answer(text, reply_markup=keyboard) @router.callback_query(F.data.startswith("menu_theme_")) async def pick_theme_from_menu(callback: CallbackQuery, state: FSMContext): """Сгенерировать слова по выбранной теме из меню и показать список.""" from database.db import async_session_maker from services.user_service import UserService from services.ai_service import ai_service from bot.handlers.words import show_words_list, WordsStates theme = callback.data.split("menu_theme_")[-1] async with async_session_maker() as session: user = await UserService.get_user_by_telegram_id(session, callback.from_user.id) if not user: await callback.answer("Сначала /start", show_alert=True) return generating = await callback.message.answer(f"🔄 Генерирую подборку слов по теме '{theme}'...") words = await ai_service.generate_thematic_words(theme=theme, level=user.level.value, count=10) await generating.delete() if not words: await callback.message.answer("❌ Не удалось сгенерировать подборку. Попробуй позже.") await callback.answer() return # Сохраняем в состояние как в /words await state.update_data(theme=theme, words=words, user_id=user.id, level=user.level.name) await state.set_state(WordsStates.viewing_words) await show_words_list(callback.message, words, theme) await callback.answer() @router.message(Command("help")) async def cmd_help(message: Message): """Обработчик команды /help""" await message.answer( "📖 Справка по командам:\n\n" "Управление словарём:\n" "• /add [слово] - добавить слово в словарь\n" "• /vocabulary - просмотр словаря\n" "• /words [тема] - тематическая подборка слов\n" "• /import - импортировать слова из текста\n\n" "Обучение:\n" "• /task - задание (перевод, заполнение пропусков)\n" "• /practice - диалог с ИИ (6 сценариев)\n" "• /level_test - тест определения уровня\n\n" "Статистика:\n" "• /stats - твой прогресс\n\n" "Настройки:\n" "• /settings - уровень и язык\n" "• /reminder - ежедневные напоминания\n\n" "💡 Ты также можешь просто отправить мне слово, и я предложу добавить его в словарь!" ) @router.callback_query(F.data == "offer_level_test") async def offer_level_test_callback(callback: CallbackQuery, state: FSMContext): """Начать тест уровня из приветствия""" from bot.handlers.level_test import start_level_test await callback.message.delete() await start_level_test(callback.message, state) await callback.answer() @router.callback_query(F.data == "skip_level_test") async def skip_level_test_callback(callback: CallbackQuery): """Пропустить тест уровня""" await callback.message.edit_text( "✅ Хорошо!\n\n" "Ты можешь пройти тест позже командой /level_test\n" "или установить уровень вручную в /settings\n\n" "Давай начнём! Попробуй:\n" "• /words travel - тематическая подборка\n" "• /practice - диалог с AI\n" "• /add hello - добавить слово" ) await callback.answer()