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,
level=user.level.name
)
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_level=data.get('level')
)
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_level=data.get('level')
)
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()