feat: мини-истории, слово дня, меню практики
- Добавлены мини-истории для чтения с выбором жанра и вопросами - Кнопка показа/скрытия перевода истории - Количество вопросов берётся из настроек пользователя - Слово дня генерируется глобально в 00:00 UTC - Кнопка "Практика" открывает меню выбора режима - Убран автоматический create_all при запуске (только миграции) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
139
bot/handlers/wordofday.py
Normal file
139
bot/handlers/wordofday.py
Normal file
@@ -0,0 +1,139 @@
|
||||
"""Handler для функции 'Слово дня'."""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from aiogram import Router, F
|
||||
from aiogram.filters import Command
|
||||
from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton
|
||||
|
||||
from database.db import async_session_maker
|
||||
from database.models import WordOfDay, WordSource
|
||||
from services.user_service import UserService
|
||||
from services.vocabulary_service import VocabularyService
|
||||
from services.wordofday_service import wordofday_service
|
||||
from utils.i18n import t, get_user_lang, get_user_translation_lang
|
||||
from utils.levels import get_user_level_for_language
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
def format_word_of_day(wod: WordOfDay, lang: str) -> str:
|
||||
"""Форматировать слово дня для отображения."""
|
||||
date_str = wod.date.strftime("%d.%m.%Y")
|
||||
|
||||
text = f"🌅 <b>{t(lang, 'wod.title')}</b> — {date_str}\n\n"
|
||||
text += f"📝 <b>{wod.word}</b>\n"
|
||||
|
||||
if wod.transcription:
|
||||
text += f"🔊 [{wod.transcription}]\n"
|
||||
|
||||
text += f"\n💬 {wod.translation}\n"
|
||||
|
||||
# Примеры
|
||||
if wod.examples:
|
||||
text += f"\n📖 <b>{t(lang, 'wod.examples')}:</b>\n"
|
||||
for ex in wod.examples[:2]:
|
||||
sentence = ex.get('sentence', '')
|
||||
translation = ex.get('translation', '')
|
||||
text += f"• <i>{sentence}</i>\n"
|
||||
if translation:
|
||||
text += f" ({translation})\n"
|
||||
|
||||
# Синонимы
|
||||
if wod.synonyms:
|
||||
text += f"\n🔗 <b>{t(lang, 'wod.synonyms')}:</b> {wod.synonyms}\n"
|
||||
|
||||
# Этимология/интересный факт
|
||||
if wod.etymology:
|
||||
text += f"\n💡 {wod.etymology}\n"
|
||||
|
||||
return text
|
||||
|
||||
|
||||
@router.message(Command("wordofday"))
|
||||
async def cmd_wordofday(message: Message):
|
||||
"""Обработчик команды /wordofday."""
|
||||
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(t('ru', 'common.start_first'))
|
||||
return
|
||||
|
||||
lang = get_user_lang(user)
|
||||
learning_lang = user.learning_language or 'en'
|
||||
level = get_user_level_for_language(user)
|
||||
|
||||
# Получаем слово дня из глобальной таблицы
|
||||
wod = await wordofday_service.get_word_of_day(
|
||||
learning_lang=learning_lang,
|
||||
level=level
|
||||
)
|
||||
|
||||
if not wod:
|
||||
# Слово ещё не сгенерировано - показываем сообщение
|
||||
await message.answer(t(lang, 'wod.not_available'))
|
||||
return
|
||||
|
||||
# Форматируем и отправляем
|
||||
text = format_word_of_day(wod, lang)
|
||||
|
||||
# Кнопка добавления в словарь
|
||||
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
||||
[InlineKeyboardButton(
|
||||
text=t(lang, 'wod.add_btn'),
|
||||
callback_data=f"wod_add_{wod.id}"
|
||||
)]
|
||||
])
|
||||
|
||||
await message.answer(text, reply_markup=keyboard)
|
||||
|
||||
|
||||
@router.callback_query(F.data.startswith("wod_add_"))
|
||||
async def wod_add_callback(callback: CallbackQuery):
|
||||
"""Добавить слово дня в словарь."""
|
||||
await callback.answer()
|
||||
|
||||
wod_id = int(callback.data.replace("wod_add_", ""))
|
||||
|
||||
async with async_session_maker() as session:
|
||||
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
|
||||
lang = get_user_lang(user)
|
||||
|
||||
# Получаем слово дня
|
||||
wod = await session.get(WordOfDay, wod_id)
|
||||
if not wod:
|
||||
await callback.answer(t(lang, 'wod.not_found'), show_alert=True)
|
||||
return
|
||||
|
||||
# Проверяем, нет ли уже в словаре
|
||||
existing = await VocabularyService.get_word_by_original(
|
||||
session, user.id, wod.word, source_lang=wod.learning_lang
|
||||
)
|
||||
|
||||
if existing:
|
||||
await callback.answer(t(lang, 'words.already_exists', word=wod.word), show_alert=True)
|
||||
return
|
||||
|
||||
# Добавляем в словарь
|
||||
translation_lang = get_user_translation_lang(user)
|
||||
|
||||
await VocabularyService.add_word(
|
||||
session=session,
|
||||
user_id=user.id,
|
||||
word_original=wod.word,
|
||||
word_translation=wod.translation,
|
||||
source_lang=wod.learning_lang,
|
||||
translation_lang=translation_lang,
|
||||
transcription=wod.transcription,
|
||||
difficulty_level=wod.level,
|
||||
source=WordSource.SUGGESTED
|
||||
)
|
||||
|
||||
await session.commit()
|
||||
|
||||
# Обновляем сообщение
|
||||
text = format_word_of_day(wod, lang)
|
||||
text += f"\n✅ <i>{t(lang, 'wod.added')}</i>"
|
||||
|
||||
await callback.message.edit_text(text, reply_markup=None)
|
||||
Reference in New Issue
Block a user