Реализован 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:
0
services/__init__.py
Normal file
0
services/__init__.py
Normal file
117
services/ai_service.py
Normal file
117
services/ai_service.py
Normal file
@@ -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()
|
||||
59
services/user_service.py
Normal file
59
services/user_service.py
Normal file
@@ -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()
|
||||
123
services/vocabulary_service.py
Normal file
123
services/vocabulary_service.py
Normal file
@@ -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()
|
||||
Reference in New Issue
Block a user