Создано: - bot/handlers/settings.py - обработчик команды /settings Реализовано: ✅ /settings - настройки пользователя - Выбор уровня английского (A1-C2) - Выбор языка интерфейса (RU/EN) - Интерактивные inline-кнопки ✅ Новый тип заданий - заполнение пропусков - AI генерирует предложение с пропуском - Показывает перевод для контекста - Проверка ответа через AI ✅ Смешанные задания - Случайное чередование типов (переводы + fill-in) - Более разнообразная практика Изменено: - services/ai_service.py - метод generate_fill_in_sentence() - services/task_service.py - метод generate_mixed_tasks() - services/user_service.py - методы обновления настроек - bot/handlers/tasks.py - использование смешанных заданий - main.py - регистрация роутера настроек Теперь бот предлагает: - Перевод EN→RU - Перевод RU→EN - Заполнение пропусков в предложениях 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
234 lines
8.3 KiB
Python
234 lines
8.3 KiB
Python
from aiogram import Router, F
|
||
from aiogram.filters import Command
|
||
from aiogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
|
||
from aiogram.fsm.context import FSMContext
|
||
from aiogram.fsm.state import State, StatesGroup
|
||
|
||
from database.db import async_session_maker
|
||
from services.user_service import UserService
|
||
from services.task_service import TaskService
|
||
from services.ai_service import ai_service
|
||
|
||
router = Router()
|
||
|
||
|
||
class TaskStates(StatesGroup):
|
||
"""Состояния для прохождения заданий"""
|
||
doing_tasks = State()
|
||
waiting_for_answer = State()
|
||
|
||
|
||
@router.message(Command("task"))
|
||
async def cmd_task(message: Message, state: FSMContext):
|
||
"""Обработчик команды /task"""
|
||
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
|
||
|
||
# Генерируем задания разных типов
|
||
tasks = await TaskService.generate_mixed_tasks(session, user.id, count=5)
|
||
|
||
if not tasks:
|
||
await message.answer(
|
||
"📚 У тебя пока нет слов для практики!\n\n"
|
||
"Добавь несколько слов командой /add, а затем возвращайся."
|
||
)
|
||
return
|
||
|
||
# Сохраняем задания в состоянии
|
||
await state.update_data(
|
||
tasks=tasks,
|
||
current_task_index=0,
|
||
correct_count=0,
|
||
user_id=user.id
|
||
)
|
||
await state.set_state(TaskStates.doing_tasks)
|
||
|
||
# Показываем первое задание
|
||
await show_current_task(message, state)
|
||
|
||
|
||
async def show_current_task(message: Message, state: FSMContext):
|
||
"""Показать текущее задание"""
|
||
data = await state.get_data()
|
||
tasks = data.get('tasks', [])
|
||
current_index = data.get('current_task_index', 0)
|
||
|
||
if current_index >= len(tasks):
|
||
# Все задания выполнены
|
||
await finish_tasks(message, state)
|
||
return
|
||
|
||
task = tasks[current_index]
|
||
|
||
task_text = (
|
||
f"📝 <b>Задание {current_index + 1} из {len(tasks)}</b>\n\n"
|
||
f"{task['question']}\n"
|
||
)
|
||
|
||
if task.get('transcription'):
|
||
task_text += f"🔊 [{task['transcription']}]\n"
|
||
|
||
task_text += f"\n💡 Напиши свой ответ:"
|
||
|
||
await state.set_state(TaskStates.waiting_for_answer)
|
||
await message.answer(task_text)
|
||
|
||
|
||
@router.message(TaskStates.waiting_for_answer)
|
||
async def process_answer(message: Message, state: FSMContext):
|
||
"""Обработка ответа пользователя"""
|
||
user_answer = message.text.strip()
|
||
data = await state.get_data()
|
||
|
||
tasks = data.get('tasks', [])
|
||
current_index = data.get('current_task_index', 0)
|
||
correct_count = data.get('correct_count', 0)
|
||
user_id = data.get('user_id')
|
||
|
||
task = tasks[current_index]
|
||
|
||
# Показываем индикатор проверки
|
||
checking_msg = await message.answer("⏳ Проверяю ответ...")
|
||
|
||
# Проверяем ответ через AI
|
||
check_result = await ai_service.check_answer(
|
||
question=task['question'],
|
||
correct_answer=task['correct_answer'],
|
||
user_answer=user_answer
|
||
)
|
||
|
||
await checking_msg.delete()
|
||
|
||
is_correct = check_result.get('is_correct', False)
|
||
feedback = check_result.get('feedback', '')
|
||
|
||
# Формируем ответ
|
||
if is_correct:
|
||
result_text = f"✅ <b>Правильно!</b>\n\n"
|
||
correct_count += 1
|
||
else:
|
||
result_text = f"❌ <b>Неправильно</b>\n\n"
|
||
|
||
result_text += f"Твой ответ: <i>{user_answer}</i>\n"
|
||
result_text += f"Правильный ответ: <b>{task['correct_answer']}</b>\n\n"
|
||
|
||
if feedback:
|
||
result_text += f"💬 {feedback}\n\n"
|
||
|
||
# Сохраняем результат в БД
|
||
async with async_session_maker() as session:
|
||
await TaskService.save_task_result(
|
||
session=session,
|
||
user_id=user_id,
|
||
task_type=task['type'],
|
||
content={
|
||
'question': task['question'],
|
||
'word': task['word']
|
||
},
|
||
user_answer=user_answer,
|
||
correct_answer=task['correct_answer'],
|
||
is_correct=is_correct,
|
||
ai_feedback=feedback
|
||
)
|
||
|
||
# Обновляем статистику слова
|
||
if 'word_id' in task:
|
||
await TaskService.update_word_statistics(
|
||
session=session,
|
||
word_id=task['word_id'],
|
||
is_correct=is_correct
|
||
)
|
||
|
||
# Обновляем счетчик
|
||
await state.update_data(
|
||
current_task_index=current_index + 1,
|
||
correct_count=correct_count
|
||
)
|
||
|
||
# Показываем результат и кнопку "Далее"
|
||
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
||
[InlineKeyboardButton(text="➡️ Следующее задание", callback_data="next_task")]
|
||
])
|
||
|
||
await message.answer(result_text, reply_markup=keyboard)
|
||
|
||
|
||
@router.callback_query(F.data == "next_task", TaskStates.doing_tasks)
|
||
async def next_task(callback: CallbackQuery, state: FSMContext):
|
||
"""Переход к следующему заданию"""
|
||
await callback.message.delete()
|
||
await show_current_task(callback.message, state)
|
||
await callback.answer()
|
||
|
||
|
||
async def finish_tasks(message: Message, state: FSMContext):
|
||
"""Завершение всех заданий"""
|
||
data = await state.get_data()
|
||
tasks = data.get('tasks', [])
|
||
correct_count = data.get('correct_count', 0)
|
||
total_count = len(tasks)
|
||
|
||
accuracy = int((correct_count / total_count) * 100) if total_count > 0 else 0
|
||
|
||
# Определяем эмодзи на основе результата
|
||
if accuracy >= 90:
|
||
emoji = "🏆"
|
||
comment = "Отличный результат!"
|
||
elif accuracy >= 70:
|
||
emoji = "👍"
|
||
comment = "Хорошая работа!"
|
||
elif accuracy >= 50:
|
||
emoji = "📚"
|
||
comment = "Неплохо, продолжай практиковаться!"
|
||
else:
|
||
emoji = "💪"
|
||
comment = "Повтори эти слова еще раз!"
|
||
|
||
result_text = (
|
||
f"{emoji} <b>Задание завершено!</b>\n\n"
|
||
f"Правильных ответов: <b>{correct_count}</b> из {total_count}\n"
|
||
f"Точность: <b>{accuracy}%</b>\n\n"
|
||
f"{comment}\n\n"
|
||
f"Используй /task для нового задания\n"
|
||
f"Используй /stats для просмотра статистики"
|
||
)
|
||
|
||
await state.clear()
|
||
await message.answer(result_text)
|
||
|
||
|
||
@router.message(Command("stats"))
|
||
async def cmd_stats(message: Message):
|
||
"""Обработчик команды /stats"""
|
||
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
|
||
|
||
# Получаем статистику
|
||
stats = await TaskService.get_user_stats(session, user.id)
|
||
|
||
stats_text = (
|
||
f"📊 <b>Твоя статистика</b>\n\n"
|
||
f"📚 Слов в словаре: <b>{stats['total_words']}</b>\n"
|
||
f"📖 Слов изучено: <b>{stats['reviewed_words']}</b>\n"
|
||
f"✍️ Заданий выполнено: <b>{stats['total_tasks']}</b>\n"
|
||
f"✅ Правильных ответов: <b>{stats['correct_tasks']}</b>\n"
|
||
f"🎯 Точность: <b>{stats['accuracy']}%</b>\n\n"
|
||
)
|
||
|
||
if stats['total_words'] == 0:
|
||
stats_text += "Добавь слова командой /add чтобы начать обучение!"
|
||
elif stats['total_tasks'] == 0:
|
||
stats_text += "Выполни первое задание командой /task!"
|
||
else:
|
||
stats_text += "Продолжай практиковаться! 💪"
|
||
|
||
await message.answer(stats_text)
|