Files
tg_bot_language/bot/handlers/vocabulary.py
mamonov.ep 1a02c979d0 Реализован MVP телеграм бота для изучения языков
Основные компоненты:
- База данных (PostgreSQL) с моделями User, Vocabulary, Task
- Интеграция с OpenAI API для перевода слов
- Команды: /start, /add, /vocabulary, /help
- Сервисы для работы с пользователями, словарем и AI

Реализовано:
 Регистрация и приветствие пользователя
 Добавление слов в словарь с автоматическим переводом
 Просмотр личного словаря
 Архитектура проекта с разделением на слои

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 11:09:54 +03:00

192 lines
7.7 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, CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton
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 AddWordStates(StatesGroup):
"""Состояния для добавления слова"""
waiting_for_confirmation = State()
waiting_for_word = State()
@router.message(Command("add"))
async def cmd_add(message: Message, state: FSMContext):
"""Обработчик команды /add [слово]"""
# Получаем слово из команды
parts = message.text.split(maxsplit=1)
if len(parts) < 2:
await message.answer(
"Отправь слово, которое хочешь добавить:\n"
"Например: <code>/add elephant</code>\n\n"
"Или просто отправь слово без команды!"
)
await state.set_state(AddWordStates.waiting_for_word)
return
word = parts[1].strip()
await process_word_addition(message, state, word)
@router.message(AddWordStates.waiting_for_word)
async def process_word_input(message: Message, state: FSMContext):
"""Обработка ввода слова"""
word = message.text.strip()
await process_word_addition(message, state, word)
async def process_word_addition(message: Message, state: FSMContext, word: str):
"""Обработка добавления слова"""
# Получаем пользователя
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
# Проверяем, есть ли уже такое слово
existing_word = await VocabularyService.find_word(session, user.id, word)
if existing_word:
await message.answer(
f"Слово '<b>{word}</b>' уже есть в твоём словаре!\n"
f"Перевод: {existing_word.word_translation}"
)
await state.clear()
return
# Показываем индикатор загрузки
processing_msg = await message.answer("⏳ Ищу перевод и примеры...")
# Получаем перевод через AI
word_data = await ai_service.translate_word(word)
# Удаляем сообщение о загрузке
await processing_msg.delete()
# Формируем примеры
examples_text = ""
if word_data.get("examples"):
examples_text = "\n\n<b>Примеры:</b>\n"
for idx, example in enumerate(word_data["examples"][:2], 1):
examples_text += f"{idx}. {example['en']}\n <i>{example['ru']}</i>\n"
# Отправляем карточку слова
card_text = (
f"📝 <b>{word_data['word']}</b>\n"
f"🔊 [{word_data.get('transcription', '')}]\n\n"
f"🇷🇺 {word_data['translation']}\n"
f"📂 Категория: {word_data.get('category', 'общая')}\n"
f"📊 Уровень: {word_data.get('difficulty', 'A1')}"
f"{examples_text}\n\n"
f"Добавить это слово в словарь?"
)
# Создаём inline-кнопки
keyboard = InlineKeyboardMarkup(inline_keyboard=[
[
InlineKeyboardButton(text="✅ Добавить", callback_data=f"add_word_confirm"),
InlineKeyboardButton(text="❌ Отмена", callback_data="add_word_cancel")
]
])
# Сохраняем данные слова в состоянии
await state.update_data(word_data=word_data, user_id=user.id)
await state.set_state(AddWordStates.waiting_for_confirmation)
await message.answer(card_text, reply_markup=keyboard)
@router.callback_query(F.data == "add_word_confirm", AddWordStates.waiting_for_confirmation)
async def confirm_add_word(callback: CallbackQuery, state: FSMContext):
"""Подтверждение добавления слова"""
data = await state.get_data()
word_data = data.get("word_data")
user_id = data.get("user_id")
async with async_session_maker() as session:
# Добавляем слово в базу
await VocabularyService.add_word(
session,
user_id=user_id,
word_original=word_data["word"],
word_translation=word_data["translation"],
transcription=word_data.get("transcription"),
examples={"examples": word_data.get("examples", [])},
category=word_data.get("category"),
difficulty_level=word_data.get("difficulty"),
source=WordSource.MANUAL
)
# Получаем общее количество слов
words_count = await VocabularyService.get_words_count(session, user_id)
await callback.message.edit_text(
f"✅ Слово '<b>{word_data['word']}</b>' добавлено в твой словарь!\n\n"
f"Всего слов в словаре: {words_count}\n\n"
f"Продолжай добавлять новые слова или используй /task для практики!"
)
await state.clear()
await callback.answer()
@router.callback_query(F.data == "add_word_cancel")
async def cancel_add_word(callback: CallbackQuery, state: FSMContext):
"""Отмена добавления слова"""
await callback.message.edit_text("Отменено. Можешь добавить другое слово командой /add")
await state.clear()
await callback.answer()
@router.message(Command("vocabulary"))
async def cmd_vocabulary(message: Message):
"""Обработчик команды /vocabulary"""
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
# Получаем слова пользователя
words = await VocabularyService.get_user_words(session, user.id, limit=10)
total_count = await VocabularyService.get_words_count(session, user.id)
if not words:
await message.answer(
"📚 Твой словарь пока пуст!\n\n"
"Добавь первое слово командой /add или просто отправь мне слово."
)
return
# Формируем список слов
words_list = "<b>📚 Твой словарь:</b>\n\n"
for idx, word in enumerate(words, 1):
progress = ""
if word.times_reviewed > 0:
accuracy = int((word.correct_answers / word.times_reviewed) * 100)
progress = f" ({accuracy}% точность)"
words_list += (
f"{idx}. <b>{word.word_original}</b> — {word.word_translation}\n"
f" 🔊 [{word.transcription or ''}]{progress}\n\n"
)
if total_count > 10:
words_list += f"\n<i>Показаны последние 10 из {total_count} слов</i>"
else:
words_list += f"\n<i>Всего слов: {total_count}</i>"
await message.answer(words_list)