feat: мини-игры, premium подписка, улучшенные контексты

Мини-игры (/games):
- Speed Round: 10 раундов, 10 секунд на ответ, очки за скорость
- Match Pairs: 5 слов + 5 переводов, соединить пары

Premium-функции:
- Поля is_premium и premium_until для пользователей
- AI режим проверки ответов (учитывает синонимы)
- Batch проверка всех ответов одним запросом

Улучшения:
- Примеры использования для всех добавляемых слов
- Разбиение переводов по запятой на отдельные записи
- Полные предложения в контекстах (без ___)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-10 19:42:10 +03:00
parent b74ea2170c
commit adc8a6bf8e
18 changed files with 1819 additions and 34 deletions

View File

@@ -285,14 +285,18 @@ async def create_tasks_from_words(
else:
fill_title = "Заполни пропуск:"
# Полное предложение для контекста (без пропуска)
full_sentence = sentence_data.get('full_sentence') or sentence_data.get('sentence', '').replace('___', word_text)
tasks.append({
'type': 'fill_in',
'question': f"{fill_title}\n\n<b>{sentence_data.get('sentence', '___')}</b>\n\n<i>{sentence_data.get('translation', '')}</i>",
'word': word_text,
'word_translation': translation, # Перевод слова для добавления в словарь
'correct_answer': sentence_data.get('answer', word_text),
'transcription': transcription,
'example': example,
'example_translation': example_translation,
'example': full_sentence, # Полное предложение как пример
'example_translation': sentence_data.get('translation', ''),
'difficulty_level': level
})
@@ -308,14 +312,18 @@ async def create_tasks_from_words(
sentence_title = "Переведи предложение:"
word_hint = "Слово"
# Полное предложение (без пропуска) для отображения и контекста
full_sentence = sentence_data.get('full_sentence') or sentence_data.get('sentence', '').replace('___', word_text)
tasks.append({
'type': 'sentence_translate',
'question': f"{sentence_title}\n\n<b>{sentence_data.get('sentence', word_text)}</b>\n\n📝 {word_hint}: <code>{word_text}</code> — {translation}",
'question': f"{sentence_title}\n\n<b>{full_sentence}</b>\n\n📝 {word_hint}: <code>{word_text}</code> — {translation}",
'word': word_text,
'word_translation': translation, # Перевод слова (для добавления в словарь)
'correct_answer': sentence_data.get('translation', translation),
'transcription': transcription,
'example': example,
'example_translation': example_translation,
'example': full_sentence, # Полное предложение как пример
'example_translation': sentence_data.get('translation', ''), # Перевод предложения
'difficulty_level': level
})
@@ -506,12 +514,17 @@ async def add_task_word(callback: CallbackQuery, state: FSMContext):
task = tasks[task_index]
word = task.get('word', '')
translation = task.get('correct_answer', '')
# Для sentence_translate и fill_in берём word_translation, иначе correct_answer
translation = task.get('word_translation') or task.get('correct_answer', '')
transcription = task.get('transcription', '')
example = task.get('example', '') # Пример использования как контекст
example_translation = task.get('example_translation', '') # Перевод примера
difficulty_level = task.get('difficulty_level') # Уровень сложности
# DEBUG: логируем что сохраняем
import logging
logging.info(f"[ADD_WORD] task_type={task.get('type')}, word={word}, example={example}")
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
@@ -542,16 +555,14 @@ async def add_task_word(callback: CallbackQuery, state: FSMContext):
source=WordSource.AI_TASK
)
# Сохраняем перевод в таблицу word_translations
await VocabularyService.add_translations_bulk(
# Сохраняем переводы в таблицу word_translations (разбиваем по запятой)
await VocabularyService.add_translation_split(
session=session,
vocabulary_id=new_word.id,
translations=[{
'translation': translation,
'context': example if example else None,
'context_translation': example_translation if example_translation else None,
'is_primary': True
}]
translation=translation,
context=example if example else None,
context_translation=example_translation if example_translation else None,
is_primary=True
)
await callback.answer(t(lang, 'tasks.word_added', word=word), show_alert=True)