Реализован 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>
This commit is contained in:
191
bot/handlers/vocabulary.py
Normal file
191
bot/handlers/vocabulary.py
Normal file
@@ -0,0 +1,191 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user