diff --git a/bot/handlers/settings.py b/bot/handlers/settings.py
new file mode 100644
index 0000000..66fa7c7
--- /dev/null
+++ b/bot/handlers/settings.py
@@ -0,0 +1,178 @@
+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 database.db import async_session_maker
+from database.models import LanguageLevel
+from services.user_service import UserService
+
+router = Router()
+
+
+def get_settings_keyboard(user) -> InlineKeyboardMarkup:
+ """Создать клавиатуру настроек"""
+ keyboard = InlineKeyboardMarkup(inline_keyboard=[
+ [InlineKeyboardButton(
+ text=f"📊 Уровень: {user.level.value}",
+ callback_data="settings_level"
+ )],
+ [InlineKeyboardButton(
+ text=f"🌐 Язык интерфейса: {'🇷🇺 Русский' if user.language_interface == 'ru' else '🇬🇧 English'}",
+ callback_data="settings_language"
+ )],
+ [InlineKeyboardButton(
+ text="❌ Закрыть",
+ callback_data="settings_close"
+ )]
+ ])
+ return keyboard
+
+
+def get_level_keyboard() -> InlineKeyboardMarkup:
+ """Клавиатура выбора уровня"""
+ levels = [
+ ("A1 - Начальный", "set_level_A1"),
+ ("A2 - Элементарный", "set_level_A2"),
+ ("B1 - Средний", "set_level_B1"),
+ ("B2 - Выше среднего", "set_level_B2"),
+ ("C1 - Продвинутый", "set_level_C1"),
+ ("C2 - Профессиональный", "set_level_C2"),
+ ]
+
+ keyboard = []
+ for level_name, callback_data in levels:
+ keyboard.append([InlineKeyboardButton(text=level_name, callback_data=callback_data)])
+
+ keyboard.append([InlineKeyboardButton(text="⬅️ Назад", callback_data="settings_back")])
+
+ return InlineKeyboardMarkup(inline_keyboard=keyboard)
+
+
+def get_language_keyboard() -> InlineKeyboardMarkup:
+ """Клавиатура выбора языка интерфейса"""
+ keyboard = InlineKeyboardMarkup(inline_keyboard=[
+ [InlineKeyboardButton(text="🇷🇺 Русский", callback_data="set_lang_ru")],
+ [InlineKeyboardButton(text="🇬🇧 English (скоро)", callback_data="set_lang_en")],
+ [InlineKeyboardButton(text="⬅️ Назад", callback_data="settings_back")]
+ ])
+ return keyboard
+
+
+@router.message(Command("settings"))
+async def cmd_settings(message: Message):
+ """Обработчик команды /settings"""
+ 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
+
+ settings_text = (
+ "⚙️ Настройки\n\n"
+ f"📊 Уровень английского: {user.level.value}\n"
+ f"🌐 Язык интерфейса: {'Русский' if user.language_interface == 'ru' else 'English'}\n\n"
+ "Выбери, что хочешь изменить:"
+ )
+
+ await message.answer(settings_text, reply_markup=get_settings_keyboard(user))
+
+
+@router.callback_query(F.data == "settings_level")
+async def settings_level(callback: CallbackQuery):
+ """Показать выбор уровня"""
+ await callback.message.edit_text(
+ "📊 Выбери свой уровень английского:\n\n"
+ "A1-A2 - Начинающий\n"
+ "B1-B2 - Средний\n"
+ "C1-C2 - Продвинутый\n\n"
+ "Это влияет на сложность предлагаемых слов и заданий.",
+ reply_markup=get_level_keyboard()
+ )
+ await callback.answer()
+
+
+@router.callback_query(F.data.startswith("set_level_"))
+async def set_level(callback: CallbackQuery):
+ """Установить уровень"""
+ level_str = callback.data.split("_")[-1] # A1, A2, B1, B2, C1, C2
+
+ async with async_session_maker() as session:
+ user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
+
+ if user:
+ # Обновляем уровень
+ await UserService.update_user_level(session, user.id, LanguageLevel[level_str])
+
+ await callback.message.edit_text(
+ f"✅ Уровень изменен на {level_str}\n\n"
+ "Теперь ты будешь получать слова и задания, соответствующие твоему уровню!",
+ reply_markup=InlineKeyboardMarkup(inline_keyboard=[
+ [InlineKeyboardButton(text="⬅️ К настройкам", callback_data="settings_back")]
+ ])
+ )
+
+ await callback.answer()
+
+
+@router.callback_query(F.data == "settings_language")
+async def settings_language(callback: CallbackQuery):
+ """Показать выбор языка"""
+ await callback.message.edit_text(
+ "🌐 Выбери язык интерфейса:\n\n"
+ "Это изменит язык всех сообщений бота.",
+ reply_markup=get_language_keyboard()
+ )
+ await callback.answer()
+
+
+@router.callback_query(F.data.startswith("set_lang_"))
+async def set_language(callback: CallbackQuery):
+ """Установить язык"""
+ lang = callback.data.split("_")[-1] # ru или en
+
+ if lang == "en":
+ await callback.answer("Английский интерфейс скоро будет доступен! 🚧", show_alert=True)
+ return
+
+ async with async_session_maker() as session:
+ user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
+
+ if user:
+ await UserService.update_user_language(session, user.id, lang)
+
+ await callback.message.edit_text(
+ f"✅ Язык интерфейса: {'Русский' if lang == 'ru' else 'English'}",
+ reply_markup=InlineKeyboardMarkup(inline_keyboard=[
+ [InlineKeyboardButton(text="⬅️ К настройкам", callback_data="settings_back")]
+ ])
+ )
+
+ await callback.answer()
+
+
+@router.callback_query(F.data == "settings_back")
+async def settings_back(callback: CallbackQuery):
+ """Вернуться к настройкам"""
+ async with async_session_maker() as session:
+ user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
+
+ if user:
+ settings_text = (
+ "⚙️ Настройки\n\n"
+ f"📊 Уровень английского: {user.level.value}\n"
+ f"🌐 Язык интерфейса: {'Русский' if user.language_interface == 'ru' else 'English'}\n\n"
+ "Выбери, что хочешь изменить:"
+ )
+
+ await callback.message.edit_text(settings_text, reply_markup=get_settings_keyboard(user))
+
+ await callback.answer()
+
+
+@router.callback_query(F.data == "settings_close")
+async def settings_close(callback: CallbackQuery):
+ """Закрыть настройки"""
+ await callback.message.delete()
+ await callback.answer()
diff --git a/bot/handlers/tasks.py b/bot/handlers/tasks.py
index e900da9..3eb2413 100644
--- a/bot/handlers/tasks.py
+++ b/bot/handlers/tasks.py
@@ -28,8 +28,8 @@ async def cmd_task(message: Message, state: FSMContext):
await message.answer("Сначала запусти бота командой /start")
return
- # Генерируем задания
- tasks = await TaskService.generate_translation_tasks(session, user.id, count=5)
+ # Генерируем задания разных типов
+ tasks = await TaskService.generate_mixed_tasks(session, user.id, count=5)
if not tasks:
await message.answer(
diff --git a/main.py b/main.py
index a700358..c7e139a 100644
--- a/main.py
+++ b/main.py
@@ -6,7 +6,7 @@ from aiogram.client.default import DefaultBotProperties
from aiogram.enums import ParseMode
from config.settings import settings
-from bot.handlers import start, vocabulary, tasks
+from bot.handlers import start, vocabulary, tasks, settings as settings_handler
from database.db import init_db
@@ -29,6 +29,7 @@ async def main():
dp.include_router(start.router)
dp.include_router(vocabulary.router)
dp.include_router(tasks.router)
+ dp.include_router(settings_handler.router)
# Инициализация базы данных
await init_db()
diff --git a/services/ai_service.py b/services/ai_service.py
index ce4ae07..e31757d 100644
--- a/services/ai_service.py
+++ b/services/ai_service.py
@@ -14,7 +14,7 @@ class AIService:
f"https://gateway.ai.cloudflare.com/v1/"
f"{settings.cloudflare_account_id}/"
f"{settings.cloudflare_gateway_id}/"
- f"openai"
+ f"compat"
)
self.client = AsyncOpenAI(
api_key=settings.openai_api_key,
@@ -127,6 +127,50 @@ class AIService:
"score": 0
}
+ async def generate_fill_in_sentence(self, word: str) -> Dict:
+ """
+ Сгенерировать предложение с пропуском для заданного слова
+
+ Args:
+ word: Слово, для которого нужно создать предложение
+
+ Returns:
+ Dict с предложением и правильным ответом
+ """
+ prompt = f"""Создай предложение на английском языке, используя слово "{word}".
+Замени это слово на пропуск "___".
+
+Верни ответ в формате JSON:
+{{
+ "sentence": "предложение с пропуском ___",
+ "answer": "{word}",
+ "translation": "перевод предложения на русский"
+}}
+
+Предложение должно быть простым и естественным. Контекст должен четко подсказывать правильное слово."""
+
+ try:
+ response = await self.client.chat.completions.create(
+ model="gpt-4o-mini",
+ messages=[
+ {"role": "system", "content": "Ты - преподаватель английского языка. Создавай простые и понятные упражнения."},
+ {"role": "user", "content": prompt}
+ ],
+ temperature=0.7,
+ response_format={"type": "json_object"}
+ )
+
+ import json
+ result = json.loads(response.choices[0].message.content)
+ return result
+
+ except Exception as e:
+ return {
+ "sentence": f"I like to ___ every day.",
+ "answer": word,
+ "translation": f"Мне нравится {word} каждый день."
+ }
+
# Глобальный экземпляр сервиса
ai_service = AIService()
diff --git a/services/task_service.py b/services/task_service.py
index 79a690c..8e97f87 100644
--- a/services/task_service.py
+++ b/services/task_service.py
@@ -5,6 +5,7 @@ from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from database.models import Task, Vocabulary
+from services.ai_service import ai_service
class TaskService:
@@ -70,6 +71,87 @@ class TaskService:
return tasks
+ @staticmethod
+ async def generate_mixed_tasks(
+ session: AsyncSession,
+ user_id: int,
+ count: int = 5
+ ) -> List[Dict]:
+ """
+ Генерация заданий разных типов (переводы + заполнение пропусков)
+
+ Args:
+ session: Сессия базы данных
+ user_id: ID пользователя
+ count: Количество заданий
+
+ Returns:
+ Список заданий разных типов
+ """
+ # Получаем слова пользователя
+ result = await session.execute(
+ select(Vocabulary)
+ .where(Vocabulary.user_id == user_id)
+ .order_by(Vocabulary.last_reviewed.asc().nullsfirst())
+ .limit(count * 2)
+ )
+ words = list(result.scalars().all())
+
+ if not words:
+ return []
+
+ # Выбираем случайные слова
+ selected_words = random.sample(words, min(count, len(words)))
+
+ tasks = []
+ for word in selected_words:
+ # Случайно выбираем тип задания
+ task_type = random.choice(['translate', 'fill_in'])
+
+ if task_type == 'translate':
+ # Задание на перевод
+ direction = random.choice(['en_to_ru', 'ru_to_en'])
+
+ if direction == 'en_to_ru':
+ task = {
+ 'type': 'translate_to_ru',
+ 'word_id': word.id,
+ 'question': f"Переведи слово: {word.word_original}",
+ 'word': word.word_original,
+ 'correct_answer': word.word_translation,
+ 'transcription': word.transcription
+ }
+ else:
+ task = {
+ 'type': 'translate_to_en',
+ 'word_id': word.id,
+ 'question': f"Переведи слово: {word.word_translation}",
+ 'word': word.word_translation,
+ 'correct_answer': word.word_original,
+ 'transcription': word.transcription
+ }
+ else:
+ # Задание на заполнение пропуска
+ # Генерируем предложение с пропуском через AI
+ sentence_data = await ai_service.generate_fill_in_sentence(word.word_original)
+
+ task = {
+ 'type': 'fill_in',
+ 'word_id': word.id,
+ 'question': (
+ f"Заполни пропуск в предложении:\n\n"
+ f"{sentence_data['sentence']}\n\n"
+ f"{sentence_data.get('translation', '')}"
+ ),
+ 'word': word.word_original,
+ 'correct_answer': sentence_data['answer'],
+ 'sentence': sentence_data['sentence']
+ }
+
+ tasks.append(task)
+
+ return tasks
+
@staticmethod
async def save_task_result(
session: AsyncSession,
diff --git a/services/user_service.py b/services/user_service.py
index 7f08fe9..668901e 100644
--- a/services/user_service.py
+++ b/services/user_service.py
@@ -57,3 +57,41 @@ class UserService:
select(User).where(User.telegram_id == telegram_id)
)
return result.scalar_one_or_none()
+
+ @staticmethod
+ async def update_user_level(session: AsyncSession, user_id: int, level: LanguageLevel):
+ """
+ Обновить уровень английского пользователя
+
+ Args:
+ session: Сессия базы данных
+ user_id: ID пользователя
+ level: Новый уровень
+ """
+ result = await session.execute(
+ select(User).where(User.id == user_id)
+ )
+ user = result.scalar_one_or_none()
+
+ if user:
+ user.level = level
+ await session.commit()
+
+ @staticmethod
+ async def update_user_language(session: AsyncSession, user_id: int, language: str):
+ """
+ Обновить язык интерфейса пользователя
+
+ Args:
+ session: Сессия базы данных
+ user_id: ID пользователя
+ language: Новый язык (ru/en)
+ """
+ result = await session.execute(
+ select(User).where(User.id == user_id)
+ )
+ user = result.scalar_one_or_none()
+
+ if user:
+ user.language_interface = language
+ await session.commit()