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:
2025-12-09 15:05:38 +03:00
parent 69c651c031
commit f38ff2f18e
22 changed files with 3131 additions and 77 deletions

View File

@@ -13,7 +13,7 @@ from aiogram.fsm.state import State, StatesGroup
from database.db import async_session_maker
from services.user_service import UserService
from utils.i18n import t, get_user_translation_lang
from utils.i18n import t, get_user_translation_lang, get_user_lang
from utils.levels import get_user_level_for_language
router = Router()
@@ -57,16 +57,19 @@ def main_menu_keyboard(lang: str = 'ru') -> ReplyKeyboardMarkup:
return ReplyKeyboardMarkup(
resize_keyboard=True,
keyboard=[
[
KeyboardButton(text=t(lang, "menu.add")),
KeyboardButton(text=t(lang, "menu.vocab")),
],
[
KeyboardButton(text=t(lang, "menu.task")),
KeyboardButton(text=t(lang, "menu.practice")),
],
[
KeyboardButton(text=t(lang, "menu.exercises")),
KeyboardButton(text=t(lang, "menu.vocab")),
],
[
KeyboardButton(text=t(lang, "menu.add")),
KeyboardButton(text=t(lang, "menu.stats")),
],
[
KeyboardButton(text=t(lang, "menu.settings")),
],
],
@@ -326,8 +329,74 @@ async def btn_task_pressed(message: Message, state: FSMContext):
@router.message(_menu_match('menu.practice'))
async def btn_practice_pressed(message: Message, state: FSMContext):
from bot.handlers.practice import cmd_practice
await cmd_practice(message, state)
"""Показать меню практики"""
await state.clear()
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, message.from_user.id)
lang = get_user_lang(user) if user else 'ru'
keyboard = InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(
text=f"📖 {t(lang, 'practice_menu.stories')}",
callback_data="practice_stories"
)],
[InlineKeyboardButton(
text=f"💬 {t(lang, 'practice_menu.ai_chat')}",
callback_data="practice_ai"
)],
])
await message.answer(
f"💬 <b>{t(lang, 'practice_menu.title')}</b>\n\n{t(lang, 'practice_menu.choose')}",
reply_markup=keyboard
)
@router.callback_query(F.data == "practice_stories")
async def practice_stories_callback(callback: CallbackQuery, state: FSMContext):
"""Переход к мини-историям"""
await callback.answer()
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) if user else 'ru'
keyboard = InlineKeyboardMarkup(inline_keyboard=[
[
InlineKeyboardButton(text=f"🗣 {t(lang, 'story.genre.dialogue')}", callback_data="story_genre_dialogue"),
InlineKeyboardButton(text=f"📰 {t(lang, 'story.genre.news')}", callback_data="story_genre_news"),
],
[
InlineKeyboardButton(text=f"🎭 {t(lang, 'story.genre.story')}", callback_data="story_genre_story"),
InlineKeyboardButton(text=f"📧 {t(lang, 'story.genre.letter')}", callback_data="story_genre_letter"),
],
[
InlineKeyboardButton(text=f"🍳 {t(lang, 'story.genre.recipe')}", callback_data="story_genre_recipe"),
],
])
await callback.message.edit_text(
f"📖 <b>{t(lang, 'story.title')}</b>\n\n{t(lang, 'story.choose_genre')}",
reply_markup=keyboard
)
@router.callback_query(F.data == "practice_ai")
async def practice_ai_callback(callback: CallbackQuery, state: FSMContext):
"""Переход к AI практике"""
await callback.answer()
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
if user:
from bot.handlers.practice import PracticeStates
from utils.levels import get_user_level_for_language
await state.update_data(user_id=user.id, level=get_user_level_for_language(user))
await state.set_state(PracticeStates.choosing_scenario)
from bot.handlers.practice import show_practice_menu
await show_practice_menu(callback.message, callback.from_user.id, edit=True)
@router.message(_menu_match('menu.import'))
@@ -391,6 +460,14 @@ async def btn_settings_pressed(message: Message):
await cmd_settings(message)
@router.message(_menu_match('menu.exercises'))
async def btn_exercises_pressed(message: Message, state: FSMContext):
"""Показать меню грамматических упражнений."""
from bot.handlers.exercises import show_exercises_menu
await show_exercises_menu(message, state, telegram_id=message.from_user.id)
@router.message(_menu_match('menu.words'))
async def btn_words_pressed(message: Message, state: FSMContext):
"""Подсказать про тематические слова и показать быстрые темы."""