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 database.models import WordSource from services.user_service import UserService from services.vocabulary_service import VocabularyService from services.ai_service import ai_service router = Router() class WordsStates(StatesGroup): """Состояния для работы с тематическими подборками""" viewing_words = State() @router.message(Command("words")) async def cmd_words(message: Message, state: FSMContext): """Обработчик команды /words [тема]""" 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 # Извлекаем тему из команды command_parts = message.text.split(maxsplit=1) if len(command_parts) < 2: await message.answer( "📚 Тематические подборки слов\n\n" "Используй: /words [тема]\n\n" "Примеры:\n" "• /words travel - путешествия\n" "• /words food - еда\n" "• /words work - работа\n" "• /words nature - природа\n" "• /words technology - технологии\n\n" "Я сгенерирую 10 слов по теме, подходящих для твоего уровня!" ) return theme = command_parts[1].strip() # Показываем индикатор генерации generating_msg = await message.answer(f"🔄 Генерирую подборку слов по теме '{theme}'...") # Генерируем слова через AI words = await ai_service.generate_thematic_words( theme=theme, level=user.level.value, count=10 ) await generating_msg.delete() if not words: await message.answer( "❌ Не удалось сгенерировать подборку. Попробуй другую тему или повтори позже." ) return # Сохраняем данные в состоянии await state.update_data( theme=theme, words=words, user_id=user.id ) await state.set_state(WordsStates.viewing_words) # Показываем подборку await show_words_list(message, words, theme) async def show_words_list(message: Message, words: list, theme: str): """Показать список слов с кнопками для добавления""" text = f"📚 Подборка слов: {theme}\n\n" for idx, word_data in enumerate(words, 1): text += ( f"{idx}. {word_data['word']} " f"[{word_data.get('transcription', '')}]\n" f" {word_data['translation']}\n" f" {word_data.get('example', '')}\n\n" ) text += "Выбери слова, которые хочешь добавить в словарь:" # Создаем кнопки для каждого слова (по 2 в ряд) keyboard = [] for idx, word_data in enumerate(words): button = InlineKeyboardButton( text=f"➕ {word_data['word']}", callback_data=f"add_word_{idx}" ) # Добавляем по 2 кнопки в ряд if len(keyboard) == 0 or len(keyboard[-1]) == 2: keyboard.append([button]) else: keyboard[-1].append(button) # Кнопка "Добавить все" keyboard.append([ InlineKeyboardButton(text="✅ Добавить все", callback_data="add_all_words") ]) # Кнопка "Закрыть" keyboard.append([ InlineKeyboardButton(text="❌ Закрыть", callback_data="close_words") ]) reply_markup = InlineKeyboardMarkup(inline_keyboard=keyboard) await message.answer(text, reply_markup=reply_markup) @router.callback_query(F.data.startswith("add_word_"), WordsStates.viewing_words) async def add_single_word(callback: CallbackQuery, state: FSMContext): """Добавить одно слово из подборки""" word_index = int(callback.data.split("_")[2]) data = await state.get_data() words = data.get('words', []) user_id = data.get('user_id') if word_index >= len(words): await callback.answer("❌ Ошибка: слово не найдено") return word_data = words[word_index] async with async_session_maker() as session: # Проверяем, нет ли уже такого слова existing = await VocabularyService.get_word_by_original( session, user_id, word_data['word'] ) if existing: await callback.answer(f"Слово '{word_data['word']}' уже в словаре", show_alert=True) return # Добавляем слово await VocabularyService.add_word( session=session, user_id=user_id, word_original=word_data['word'], word_translation=word_data['translation'], transcription=word_data.get('transcription'), examples=[{"en": word_data.get('example', ''), "ru": ""}] if word_data.get('example') else [], source=WordSource.SUGGESTED, category=data.get('theme', 'general'), difficulty=None ) await callback.answer(f"✅ Слово '{word_data['word']}' добавлено в словарь") @router.callback_query(F.data == "add_all_words", WordsStates.viewing_words) async def add_all_words(callback: CallbackQuery, state: FSMContext): """Добавить все слова из подборки""" data = await state.get_data() words = data.get('words', []) user_id = data.get('user_id') theme = data.get('theme', 'general') added_count = 0 skipped_count = 0 async with async_session_maker() as session: for word_data in words: # Проверяем, нет ли уже такого слова existing = await VocabularyService.get_word_by_original( session, user_id, word_data['word'] ) if existing: skipped_count += 1 continue # Добавляем слово await VocabularyService.add_word( session=session, user_id=user_id, word_original=word_data['word'], word_translation=word_data['translation'], transcription=word_data.get('transcription'), examples=[{"en": word_data.get('example', ''), "ru": ""}] if word_data.get('example') else [], source=WordSource.SUGGESTED, category=theme, difficulty=None ) added_count += 1 result_text = f"✅ Добавлено слов: {added_count}" if skipped_count > 0: result_text += f"\n⚠️ Пропущено (уже в словаре): {skipped_count}" await callback.message.edit_reply_markup(reply_markup=None) await callback.message.answer(result_text) await state.clear() await callback.answer() @router.callback_query(F.data == "close_words", WordsStates.viewing_words) async def close_words(callback: CallbackQuery, state: FSMContext): """Закрыть подборку слов""" await callback.message.delete() await state.clear() await callback.answer()