Files
tg_bot_language/bot/handlers/import_text.py

250 lines
9.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 ImportStates(StatesGroup):
"""Состояния для импорта слов из текста"""
waiting_for_text = State()
viewing_words = State()
@router.message(Command("import"))
async def cmd_import(message: Message, state: FSMContext):
"""Обработчик команды /import"""
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
await state.set_state(ImportStates.waiting_for_text)
await message.answer(
"📖 <b>Импорт слов из текста</b>\n\n"
"Отправь мне текст на английском языке, и я извлеку из него "
"полезные слова для изучения.\n\n"
"Можно отправить:\n"
"• Отрывок из книги или статьи\n"
"• Текст песни\n"
"• Описание чего-либо\n"
"• Любой интересный текст\n\n"
"Отправь /cancel для отмены."
)
@router.message(Command("cancel"), ImportStates.waiting_for_text)
async def cancel_import(message: Message, state: FSMContext):
"""Отмена импорта"""
await state.clear()
await message.answer("❌ Импорт отменён.")
@router.message(ImportStates.waiting_for_text)
async def process_text(message: Message, state: FSMContext):
"""Обработка текста от пользователя"""
text = message.text.strip()
if len(text) < 50:
await message.answer(
"⚠️ Текст слишком короткий. Отправь текст минимум из 50 символов.\n"
"Или используй /cancel для отмены."
)
return
if len(text) > 3000:
await message.answer(
"⚠️ Текст слишком длинный (максимум 3000 символов).\n"
"Отправь текст покороче или используй /cancel для отмены."
)
return
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, message.from_user.id)
# Показываем индикатор обработки
processing_msg = await message.answer("🔄 Анализирую текст и извлекаю слова...")
# Извлекаем слова через AI
words = await ai_service.extract_words_from_text(
text=text,
level=user.level.value,
max_words=15
)
await processing_msg.delete()
if not words:
await message.answer(
"Не удалось извлечь слова из текста. Попробуй другой текст или повтори позже."
)
await state.clear()
return
# Сохраняем данные в состоянии
await state.update_data(
words=words,
user_id=user.id,
original_text=text,
level=user.level.name
)
await state.set_state(ImportStates.viewing_words)
# Показываем извлечённые слова
await show_extracted_words(message, words)
async def show_extracted_words(message: Message, words: list):
"""Показать извлечённые слова с кнопками для добавления"""
text = f"📚 <b>Найдено слов: {len(words)}</b>\n\n"
for idx, word_data in enumerate(words, 1):
text += (
f"{idx}. <b>{word_data['word']}</b> "
f"[{word_data.get('transcription', '')}]\n"
f" {word_data['translation']}\n"
)
if word_data.get('context'):
# Укорачиваем контекст, если он слишком длинный
context = word_data['context']
if len(context) > 80:
context = context[:77] + "..."
text += f" <i>«{context}»</i>\n"
text += "\n"
text += "Выбери слова, которые хочешь добавить в словарь:"
# Создаем кнопки для каждого слова (по 2 в ряд)
keyboard = []
for idx, word_data in enumerate(words):
button = InlineKeyboardButton(
text=f" {word_data['word']}",
callback_data=f"import_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="import_all_words")
])
# Кнопка "Закрыть"
keyboard.append([
InlineKeyboardButton(text="❌ Закрыть", callback_data="close_import")
])
reply_markup = InlineKeyboardMarkup(inline_keyboard=keyboard)
await message.answer(text, reply_markup=reply_markup)
@router.callback_query(F.data.startswith("import_word_"), ImportStates.viewing_words)
async def import_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('context', ''), "ru": ""}] if word_data.get('context') else [],
source=WordSource.CONTEXT,
category='imported',
difficulty_level=data.get('level')
)
await callback.answer(f"✅ Слово '{word_data['word']}' добавлено в словарь")
@router.callback_query(F.data == "import_all_words", ImportStates.viewing_words)
async def import_all_words(callback: CallbackQuery, state: FSMContext):
"""Добавить все слова из импорта"""
data = await state.get_data()
words = data.get('words', [])
user_id = data.get('user_id')
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('context', ''), "ru": ""}] if word_data.get('context') else [],
source=WordSource.CONTEXT,
category='imported',
difficulty_level=data.get('level')
)
added_count += 1
result_text = f"✅ Добавлено слов: <b>{added_count}</b>"
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_import", ImportStates.viewing_words)
async def close_import(callback: CallbackQuery, state: FSMContext):
"""Закрыть импорт"""
await callback.message.delete()
await state.clear()
await callback.answer()