diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..ea85242
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,11 @@
+# Telegram Bot Token (получить у @BotFather)
+BOT_TOKEN=your_telegram_bot_token_here
+
+# OpenAI API Key
+OPENAI_API_KEY=your_openai_api_key_here
+
+# Database
+DATABASE_URL=postgresql+asyncpg://user:password@localhost:5432/language_bot
+
+# Settings
+DEBUG=True
diff --git a/README.md b/README.md
index 70f2923..e8d6366 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,141 @@
-# tg_bot_language
+# Telegram Бот для изучения языков
-Бот для изучения иностранных языков
\ No newline at end of file
+Интеллектуальный Telegram бот для изучения английского языка с использованием AI.
+
+## Возможности
+
+- 📚 Управление словарным запасом с автоматическим переводом через AI
+- ✍️ Ежедневные задания для практики (в разработке)
+- 💬 Диалоговая практика с ИИ (в разработке)
+- 📊 Статистика прогресса (в разработке)
+
+## Текущая версия (MVP)
+
+**Реализовано:**
+- ✅ Команда `/start` - приветствие и регистрация пользователя
+- ✅ Команда `/add [слово]` - добавление слов в словарь с AI-переводом
+- ✅ Команда `/vocabulary` - просмотр словаря
+- ✅ Команда `/help` - справка
+- ✅ База данных (PostgreSQL) для хранения пользователей и словарей
+- ✅ Интеграция с OpenAI API для перевода слов
+
+## Установка и запуск
+
+### 1. Клонирование репозитория
+
+```bash
+git clone http://103.137.249.134:3000/NANDI/tg_bot_language.git
+cd tg_bot_language
+```
+
+### 2. Установка зависимостей
+
+```bash
+pip install -r requirements.txt
+```
+
+### 3. Настройка окружения
+
+Скопируйте `.env.example` в `.env`:
+
+```bash
+cp .env.example .env
+```
+
+Отредактируйте `.env` и заполните необходимые параметры:
+
+```env
+BOT_TOKEN=your_telegram_bot_token_here
+OPENAI_API_KEY=your_openai_api_key_here
+DATABASE_URL=postgresql+asyncpg://user:password@localhost:5432/language_bot
+DEBUG=True
+```
+
+**Получение токенов:**
+- Telegram Bot Token: создайте бота через [@BotFather](https://t.me/BotFather)
+- OpenAI API Key: получите на [platform.openai.com](https://platform.openai.com/api-keys)
+
+### 4. Настройка базы данных
+
+Создайте PostgreSQL базу данных:
+
+```bash
+createdb language_bot
+```
+
+Или используйте Docker:
+
+```bash
+docker run --name language-bot-db -e POSTGRES_PASSWORD=password -e POSTGRES_DB=language_bot -p 5432:5432 -d postgres:15
+```
+
+### 5. Запуск бота
+
+```bash
+python main.py
+```
+
+## Структура проекта
+
+```
+bot_tg_language/
+├── bot/
+│ ├── handlers/ # Обработчики команд
+│ │ ├── start.py # /start, /help
+│ │ └── vocabulary.py # /add, /vocabulary
+│ └── keyboards/ # Клавиатуры (пока не используется)
+├── database/
+│ ├── models.py # Модели БД (User, Vocabulary, Task)
+│ └── db.py # Подключение к БД
+├── services/
+│ ├── ai_service.py # Сервис для работы с OpenAI
+│ ├── user_service.py # Сервис пользователей
+│ └── vocabulary_service.py # Сервис словаря
+├── config/
+│ └── settings.py # Настройки приложения
+├── main.py # Точка входа
+├── requirements.txt # Зависимости
+├── .env.example # Пример конфигурации
+└── TZ.md # Техническое задание
+```
+
+## Использование
+
+### Команды бота
+
+- `/start` - Начать работу с ботом
+- `/add [слово]` - Добавить слово в словарь
+- `/vocabulary` - Посмотреть свой словарь
+- `/help` - Показать справку
+
+### Пример использования
+
+1. Запустите бота: `/start`
+2. Добавьте слово: `/add elephant`
+3. Бот переведёт слово через AI и предложит добавить в словарь
+4. Подтвердите добавление
+5. Просмотрите словарь: `/vocabulary`
+
+## Roadmap
+
+См. [TZ.md](TZ.md) для полного технического задания.
+
+**Следующие этапы:**
+- [ ] Ежедневные задания с разными типами упражнений
+- [ ] Тематические подборки слов
+- [ ] Импорт слов из текста
+- [ ] Диалоговая практика с AI
+- [ ] Статистика и прогресс
+- [ ] Spaced repetition алгоритм
+
+## Технологии
+
+- **Python 3.11+**
+- **aiogram 3.x** - Telegram Bot framework
+- **SQLAlchemy 2.x** - ORM для работы с БД
+- **PostgreSQL** - База данных
+- **OpenAI API** - AI для перевода и проверки
+
+## Лицензия
+
+MIT
\ No newline at end of file
diff --git a/bot/__init__.py b/bot/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/bot/handlers/__init__.py b/bot/handlers/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/bot/handlers/start.py b/bot/handlers/start.py
new file mode 100644
index 0000000..ed22caa
--- /dev/null
+++ b/bot/handlers/start.py
@@ -0,0 +1,66 @@
+from aiogram import Router, F
+from aiogram.filters import CommandStart, Command
+from aiogram.types import Message
+from aiogram.fsm.context import FSMContext
+
+from database.db import async_session_maker
+from services.user_service import UserService
+
+router = Router()
+
+
+@router.message(CommandStart())
+async def cmd_start(message: Message, state: FSMContext):
+ """Обработчик команды /start"""
+ async with async_session_maker() as session:
+ user = await UserService.get_or_create_user(
+ session,
+ telegram_id=message.from_user.id,
+ username=message.from_user.username
+ )
+
+ if user.created_at.timestamp() > (message.date.timestamp() - 60):
+ # Новый пользователь (создан менее минуты назад)
+ await message.answer(
+ f"👋 Привет, {message.from_user.first_name}!\n\n"
+ f"Я бот для изучения английского языка. Помогу тебе:\n"
+ f"📚 Пополнять словарный запас\n"
+ f"✍️ Выполнять ежедневные задания\n"
+ f"💬 Практиковать язык в диалоге\n\n"
+ f"Основные команды:\n"
+ f"/add [слово] - добавить слово в словарь\n"
+ f"/vocabulary - мой словарь\n"
+ f"/task - получить задание\n"
+ f"/stats - статистика\n"
+ f"/help - справка\n\n"
+ f"Давай начнём! Отправь мне слово, которое хочешь выучить, или используй команду /add"
+ )
+ else:
+ # Существующий пользователь
+ await message.answer(
+ f"С возвращением, {message.from_user.first_name}! 👋\n\n"
+ f"Готов продолжить обучение?\n"
+ f"/vocabulary - посмотреть словарь\n"
+ f"/task - получить задание\n"
+ f"/stats - статистика"
+ )
+
+
+@router.message(Command("help"))
+async def cmd_help(message: Message):
+ """Обработчик команды /help"""
+ await message.answer(
+ "📖 Справка по командам:\n\n"
+ "Управление словарём:\n"
+ "/add [слово] - добавить слово в словарь\n"
+ "/vocabulary - просмотр словаря\n"
+ "/import - импортировать слова из текста\n\n"
+ "Обучение:\n"
+ "/task - получить задание\n"
+ "/practice - практика с ИИ\n\n"
+ "Статистика:\n"
+ "/stats - твой прогресс\n\n"
+ "Настройки:\n"
+ "/settings - настройки бота\n\n"
+ "Ты также можешь просто отправить мне слово, и я предложу добавить его в словарь!"
+ )
diff --git a/bot/handlers/vocabulary.py b/bot/handlers/vocabulary.py
new file mode 100644
index 0000000..b833530
--- /dev/null
+++ b/bot/handlers/vocabulary.py
@@ -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"
+ "Например: /add elephant\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"Слово '{word}' уже есть в твоём словаре!\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Примеры:\n"
+ for idx, example in enumerate(word_data["examples"][:2], 1):
+ examples_text += f"{idx}. {example['en']}\n {example['ru']}\n"
+
+ # Отправляем карточку слова
+ card_text = (
+ f"📝 {word_data['word']}\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"✅ Слово '{word_data['word']}' добавлено в твой словарь!\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 = "📚 Твой словарь:\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}. {word.word_original} — {word.word_translation}\n"
+ f" 🔊 [{word.transcription or ''}]{progress}\n\n"
+ )
+
+ if total_count > 10:
+ words_list += f"\nПоказаны последние 10 из {total_count} слов"
+ else:
+ words_list += f"\nВсего слов: {total_count}"
+
+ await message.answer(words_list)
diff --git a/bot/keyboards/__init__.py b/bot/keyboards/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/config/__init__.py b/config/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/config/settings.py b/config/settings.py
new file mode 100644
index 0000000..315db6c
--- /dev/null
+++ b/config/settings.py
@@ -0,0 +1,26 @@
+from pydantic_settings import BaseSettings, SettingsConfigDict
+
+
+class Settings(BaseSettings):
+ """Настройки приложения"""
+
+ # Telegram
+ bot_token: str
+
+ # OpenAI
+ openai_api_key: str
+
+ # Database
+ database_url: str
+
+ # App settings
+ debug: bool = False
+
+ model_config = SettingsConfigDict(
+ env_file='.env',
+ env_file_encoding='utf-8',
+ case_sensitive=False
+ )
+
+
+settings = Settings()
diff --git a/database/__init__.py b/database/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/database/db.py b/database/db.py
new file mode 100644
index 0000000..658a7b4
--- /dev/null
+++ b/database/db.py
@@ -0,0 +1,29 @@
+from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
+from config.settings import settings
+from database.models import Base
+
+# Создание движка базы данных
+engine = create_async_engine(
+ settings.database_url,
+ echo=settings.debug,
+ future=True
+)
+
+# Фабрика сессий
+async_session_maker = async_sessionmaker(
+ engine,
+ class_=AsyncSession,
+ expire_on_commit=False
+)
+
+
+async def init_db():
+ """Инициализация базы данных (создание таблиц)"""
+ async with engine.begin() as conn:
+ await conn.run_sync(Base.metadata.create_all)
+
+
+async def get_session() -> AsyncSession:
+ """Получение сессии базы данных"""
+ async with async_session_maker() as session:
+ yield session
diff --git a/database/models.py b/database/models.py
new file mode 100644
index 0000000..1b3f9a5
--- /dev/null
+++ b/database/models.py
@@ -0,0 +1,83 @@
+from datetime import datetime
+from typing import Optional
+
+from sqlalchemy import String, BigInteger, DateTime, Integer, Boolean, JSON, Enum as SQLEnum
+from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
+import enum
+
+
+class Base(DeclarativeBase):
+ """Базовая модель"""
+ pass
+
+
+class LanguageLevel(str, enum.Enum):
+ """Уровни владения языком"""
+ A1 = "A1"
+ A2 = "A2"
+ B1 = "B1"
+ B2 = "B2"
+ C1 = "C1"
+ C2 = "C2"
+
+
+class WordSource(str, enum.Enum):
+ """Источник добавления слова"""
+ MANUAL = "manual" # Ручное добавление
+ SUGGESTED = "suggested" # Предложено ботом
+ CONTEXT = "context" # Из контекста диалога
+ IMPORT = "import" # Импорт из текста
+ ERROR = "error" # Из ошибок в заданиях
+
+
+class User(Base):
+ """Модель пользователя"""
+ __tablename__ = "users"
+
+ id: Mapped[int] = mapped_column(primary_key=True)
+ telegram_id: Mapped[int] = mapped_column(BigInteger, unique=True, nullable=False)
+ username: Mapped[Optional[str]] = mapped_column(String(255))
+ language_interface: Mapped[str] = mapped_column(String(2), default="ru") # ru/en
+ learning_language: Mapped[str] = mapped_column(String(2), default="en") # en
+ level: Mapped[LanguageLevel] = mapped_column(SQLEnum(LanguageLevel), default=LanguageLevel.A1)
+ timezone: Mapped[str] = mapped_column(String(50), default="UTC")
+ daily_task_time: Mapped[Optional[str]] = mapped_column(String(5)) # HH:MM
+ streak_days: Mapped[int] = mapped_column(Integer, default=0)
+ created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
+ last_active: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
+
+
+class Vocabulary(Base):
+ """Модель словарного запаса"""
+ __tablename__ = "vocabulary"
+
+ id: Mapped[int] = mapped_column(primary_key=True)
+ user_id: Mapped[int] = mapped_column(Integer, nullable=False, index=True)
+ word_original: Mapped[str] = mapped_column(String(255), nullable=False)
+ word_translation: Mapped[str] = mapped_column(String(255), nullable=False)
+ transcription: Mapped[Optional[str]] = mapped_column(String(255))
+ examples: Mapped[Optional[dict]] = mapped_column(JSON) # JSON массив примеров
+ category: Mapped[Optional[str]] = mapped_column(String(100))
+ difficulty_level: Mapped[Optional[LanguageLevel]] = mapped_column(SQLEnum(LanguageLevel))
+ source: Mapped[WordSource] = mapped_column(SQLEnum(WordSource), default=WordSource.MANUAL)
+ times_reviewed: Mapped[int] = mapped_column(Integer, default=0)
+ correct_answers: Mapped[int] = mapped_column(Integer, default=0)
+ last_reviewed: Mapped[Optional[datetime]] = mapped_column(DateTime)
+ created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
+ notes: Mapped[Optional[str]] = mapped_column(String(500)) # Заметки пользователя
+
+
+class Task(Base):
+ """Модель задания"""
+ __tablename__ = "tasks"
+
+ id: Mapped[int] = mapped_column(primary_key=True)
+ user_id: Mapped[int] = mapped_column(Integer, nullable=False, index=True)
+ task_type: Mapped[str] = mapped_column(String(50), nullable=False) # translate, sentence, fill, etc.
+ content: Mapped[dict] = mapped_column(JSON, nullable=False) # Содержание задания
+ correct_answer: Mapped[Optional[str]] = mapped_column(String(500))
+ user_answer: Mapped[Optional[str]] = mapped_column(String(500))
+ is_correct: Mapped[Optional[bool]] = mapped_column(Boolean)
+ ai_feedback: Mapped[Optional[str]] = mapped_column(String(1000))
+ completed_at: Mapped[Optional[datetime]] = mapped_column(DateTime)
+ created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..66580a8
--- /dev/null
+++ b/main.py
@@ -0,0 +1,41 @@
+import asyncio
+import logging
+
+from aiogram import Bot, Dispatcher
+from aiogram.client.default import DefaultBotProperties
+from aiogram.enums import ParseMode
+
+from config.settings import settings
+from bot.handlers import start, vocabulary
+from database.db import init_db
+
+
+async def main():
+ """Главная функция запуска бота"""
+ # Настройка логирования
+ logging.basicConfig(
+ level=logging.INFO if settings.debug else logging.WARNING,
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
+ )
+
+ # Инициализация бота и диспетчера
+ bot = Bot(
+ token=settings.bot_token,
+ default=DefaultBotProperties(parse_mode=ParseMode.HTML)
+ )
+ dp = Dispatcher()
+
+ # Регистрация роутеров
+ dp.include_router(start.router)
+ dp.include_router(vocabulary.router)
+
+ # Инициализация базы данных
+ await init_db()
+
+ # Запуск бота
+ logging.info("Бот запущен")
+ await dp.start_polling(bot)
+
+
+if __name__ == '__main__':
+ asyncio.run(main())
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..bf6481e
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,8 @@
+aiogram==3.13.1
+sqlalchemy==2.0.36
+asyncpg==0.30.0
+alembic==1.14.0
+python-dotenv==1.0.1
+openai==1.57.3
+pydantic==2.10.3
+pydantic-settings==2.6.1
diff --git a/services/__init__.py b/services/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/services/ai_service.py b/services/ai_service.py
new file mode 100644
index 0000000..19ffdc3
--- /dev/null
+++ b/services/ai_service.py
@@ -0,0 +1,117 @@
+from openai import AsyncOpenAI
+from config.settings import settings
+from typing import Dict, List
+
+
+class AIService:
+ """Сервис для работы с OpenAI API"""
+
+ def __init__(self):
+ self.client = AsyncOpenAI(api_key=settings.openai_api_key)
+
+ async def translate_word(self, word: str, target_lang: str = "ru") -> Dict:
+ """
+ Перевести слово и получить дополнительную информацию
+
+ Args:
+ word: Слово для перевода
+ target_lang: Язык перевода (по умолчанию русский)
+
+ Returns:
+ Dict с переводом, транскрипцией и примерами
+ """
+ prompt = f"""Переведи английское слово/фразу "{word}" на русский язык.
+
+Верни ответ строго в формате JSON:
+{{
+ "word": "{word}",
+ "translation": "перевод",
+ "transcription": "транскрипция в IPA",
+ "examples": [
+ {{"en": "пример на английском", "ru": "перевод примера"}},
+ {{"en": "ещё один пример", "ru": "перевод примера"}}
+ ],
+ "category": "категория слова (работа, еда, путешествия и т.д.)",
+ "difficulty": "уровень сложности (A1/A2/B1/B2/C1/C2)"
+}}
+
+Важно: верни только JSON, без дополнительного текста."""
+
+ try:
+ response = await self.client.chat.completions.create(
+ model="gpt-4o-mini",
+ messages=[
+ {"role": "system", "content": "Ты - помощник для изучения английского языка. Отвечай только в формате JSON."},
+ {"role": "user", "content": prompt}
+ ],
+ temperature=0.3,
+ response_format={"type": "json_object"}
+ )
+
+ import json
+ result = json.loads(response.choices[0].message.content)
+ return result
+
+ except Exception as e:
+ # Fallback в случае ошибки
+ return {
+ "word": word,
+ "translation": "Ошибка перевода",
+ "transcription": "",
+ "examples": [],
+ "category": "unknown",
+ "difficulty": "A1"
+ }
+
+ async def check_answer(self, question: str, correct_answer: str, user_answer: str) -> Dict:
+ """
+ Проверить ответ пользователя с помощью ИИ
+
+ Args:
+ question: Вопрос задания
+ correct_answer: Правильный ответ
+ user_answer: Ответ пользователя
+
+ Returns:
+ Dict с результатом проверки и обратной связью
+ """
+ prompt = f"""Проверь ответ пользователя на задание по английскому языку.
+
+Задание: {question}
+Правильный ответ: {correct_answer}
+Ответ пользователя: {user_answer}
+
+Верни ответ в формате JSON:
+{{
+ "is_correct": true/false,
+ "feedback": "краткое объяснение (если ответ неверный, объясни ошибку и дай правильный вариант)",
+ "score": 0-100
+}}
+
+Учитывай возможные вариации ответа. Если смысл передан правильно, даже с небольшими грамматическими неточностями, засчитывай ответ."""
+
+ try:
+ response = await self.client.chat.completions.create(
+ model="gpt-4o-mini",
+ messages=[
+ {"role": "system", "content": "Ты - преподаватель английского языка. Проверяй ответы справедливо, учитывая контекст."},
+ {"role": "user", "content": prompt}
+ ],
+ temperature=0.3,
+ response_format={"type": "json_object"}
+ )
+
+ import json
+ result = json.loads(response.choices[0].message.content)
+ return result
+
+ except Exception as e:
+ return {
+ "is_correct": False,
+ "feedback": "Ошибка проверки ответа",
+ "score": 0
+ }
+
+
+# Глобальный экземпляр сервиса
+ai_service = AIService()
diff --git a/services/user_service.py b/services/user_service.py
new file mode 100644
index 0000000..7f08fe9
--- /dev/null
+++ b/services/user_service.py
@@ -0,0 +1,59 @@
+from sqlalchemy import select
+from sqlalchemy.ext.asyncio import AsyncSession
+from database.models import User, LanguageLevel
+from typing import Optional
+
+
+class UserService:
+ """Сервис для работы с пользователями"""
+
+ @staticmethod
+ async def get_or_create_user(session: AsyncSession, telegram_id: int, username: Optional[str] = None) -> User:
+ """
+ Получить пользователя или создать нового
+
+ Args:
+ session: Сессия базы данных
+ telegram_id: Telegram ID пользователя
+ username: Username пользователя
+
+ Returns:
+ Объект пользователя
+ """
+ # Попытка найти существующего пользователя
+ result = await session.execute(
+ select(User).where(User.telegram_id == telegram_id)
+ )
+ user = result.scalar_one_or_none()
+
+ if user:
+ return user
+
+ # Создание нового пользователя
+ new_user = User(
+ telegram_id=telegram_id,
+ username=username,
+ level=LanguageLevel.A1
+ )
+ session.add(new_user)
+ await session.commit()
+ await session.refresh(new_user)
+
+ return new_user
+
+ @staticmethod
+ async def get_user_by_telegram_id(session: AsyncSession, telegram_id: int) -> Optional[User]:
+ """
+ Получить пользователя по Telegram ID
+
+ Args:
+ session: Сессия базы данных
+ telegram_id: Telegram ID пользователя
+
+ Returns:
+ Объект пользователя или None
+ """
+ result = await session.execute(
+ select(User).where(User.telegram_id == telegram_id)
+ )
+ return result.scalar_one_or_none()
diff --git a/services/vocabulary_service.py b/services/vocabulary_service.py
new file mode 100644
index 0000000..2ecd651
--- /dev/null
+++ b/services/vocabulary_service.py
@@ -0,0 +1,123 @@
+from sqlalchemy import select
+from sqlalchemy.ext.asyncio import AsyncSession
+from database.models import Vocabulary, WordSource, LanguageLevel
+from typing import List, Optional
+
+
+class VocabularyService:
+ """Сервис для работы со словарным запасом"""
+
+ @staticmethod
+ async def add_word(
+ session: AsyncSession,
+ user_id: int,
+ word_original: str,
+ word_translation: str,
+ transcription: Optional[str] = None,
+ examples: Optional[dict] = None,
+ category: Optional[str] = None,
+ difficulty_level: Optional[str] = None,
+ source: WordSource = WordSource.MANUAL,
+ notes: Optional[str] = None
+ ) -> Vocabulary:
+ """
+ Добавить слово в словарь пользователя
+
+ Args:
+ session: Сессия базы данных
+ user_id: ID пользователя
+ word_original: Оригинальное слово
+ word_translation: Перевод
+ transcription: Транскрипция
+ examples: Примеры использования
+ category: Категория слова
+ difficulty_level: Уровень сложности
+ source: Источник добавления
+ notes: Заметки пользователя
+
+ Returns:
+ Созданный объект слова
+ """
+ # Преобразование difficulty_level в enum
+ difficulty_enum = None
+ if difficulty_level:
+ try:
+ difficulty_enum = LanguageLevel[difficulty_level]
+ except KeyError:
+ difficulty_enum = None
+
+ new_word = Vocabulary(
+ user_id=user_id,
+ word_original=word_original,
+ word_translation=word_translation,
+ transcription=transcription,
+ examples=examples,
+ category=category,
+ difficulty_level=difficulty_enum,
+ source=source,
+ notes=notes
+ )
+
+ session.add(new_word)
+ await session.commit()
+ await session.refresh(new_word)
+
+ return new_word
+
+ @staticmethod
+ async def get_user_words(session: AsyncSession, user_id: int, limit: int = 50) -> List[Vocabulary]:
+ """
+ Получить все слова пользователя
+
+ Args:
+ session: Сессия базы данных
+ user_id: ID пользователя
+ limit: Максимальное количество слов
+
+ Returns:
+ Список слов пользователя
+ """
+ result = await session.execute(
+ select(Vocabulary)
+ .where(Vocabulary.user_id == user_id)
+ .order_by(Vocabulary.created_at.desc())
+ .limit(limit)
+ )
+ return list(result.scalars().all())
+
+ @staticmethod
+ async def get_words_count(session: AsyncSession, user_id: int) -> int:
+ """
+ Получить количество слов в словаре пользователя
+
+ Args:
+ session: Сессия базы данных
+ user_id: ID пользователя
+
+ Returns:
+ Количество слов
+ """
+ result = await session.execute(
+ select(Vocabulary).where(Vocabulary.user_id == user_id)
+ )
+ return len(list(result.scalars().all()))
+
+ @staticmethod
+ async def find_word(session: AsyncSession, user_id: int, word: str) -> Optional[Vocabulary]:
+ """
+ Найти слово в словаре пользователя
+
+ Args:
+ session: Сессия базы данных
+ user_id: ID пользователя
+ word: Слово для поиска
+
+ Returns:
+ Объект слова или None
+ """
+ result = await session.execute(
+ select(Vocabulary)
+ .where(Vocabulary.user_id == user_id)
+ .where(Vocabulary.word_original.ilike(f"%{word}%"))
+ )
+ return result.scalar_one_or_none()