Files
tg_bot_language/bot/handlers/start.py

571 lines
24 KiB
Python
Raw Normal View History

from aiogram import Router, F
from aiogram.filters import CommandStart, Command
from aiogram.types import (
Message,
InlineKeyboardMarkup,
InlineKeyboardButton,
CallbackQuery,
ReplyKeyboardMarkup,
KeyboardButton,
)
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 utils.i18n import t, get_user_translation_lang, get_user_lang
from utils.levels import get_user_level_for_language
router = Router()
class OnboardingStates(StatesGroup):
"""Состояния онбординга для новых пользователей"""
choosing_interface_lang = State()
choosing_learning_lang = State()
choosing_translation_lang = State()
def onboarding_interface_keyboard() -> InlineKeyboardMarkup:
"""Клавиатура выбора языка интерфейса при онбординге"""
return InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text="🇷🇺 Русский", callback_data="onboard_interface_ru")],
[InlineKeyboardButton(text="🇬🇧 English", callback_data="onboard_interface_en")],
[InlineKeyboardButton(text="🇯🇵 日本語", callback_data="onboard_interface_ja")],
])
def onboarding_learning_keyboard(lang: str) -> InlineKeyboardMarkup:
"""Клавиатура выбора языка изучения при онбординге"""
return InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text=t(lang, 'onboarding.lang_en'), callback_data="onboard_learning_en")],
[InlineKeyboardButton(text=t(lang, 'onboarding.lang_ja'), callback_data="onboard_learning_ja")],
])
def onboarding_translation_keyboard(lang: str) -> InlineKeyboardMarkup:
"""Клавиатура выбора языка перевода при онбординге"""
return InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text=t(lang, 'settings.lang_name.ru'), callback_data="onboard_translation_ru")],
[InlineKeyboardButton(text=t(lang, 'settings.lang_name.en'), callback_data="onboard_translation_en")],
[InlineKeyboardButton(text=t(lang, 'settings.lang_name.ja'), callback_data="onboard_translation_ja")],
])
def main_menu_keyboard(lang: str = 'ru') -> ReplyKeyboardMarkup:
"""Клавиатура с основными командами (кнопки отправляют команды)."""
return ReplyKeyboardMarkup(
resize_keyboard=True,
keyboard=[
[
KeyboardButton(text=t(lang, "menu.task")),
KeyboardButton(text=t(lang, "menu.practice")),
],
[
KeyboardButton(text=t(lang, "menu.exercises")),
KeyboardButton(text=t(lang, "menu.games")),
],
[
KeyboardButton(text=t(lang, "menu.vocab")),
KeyboardButton(text=t(lang, "menu.add")),
],
[
KeyboardButton(text=t(lang, "menu.stats")),
KeyboardButton(text=t(lang, "menu.settings")),
],
],
)
@router.message(CommandStart())
async def cmd_start(message: Message, state: FSMContext):
"""Обработчик команды /start"""
async with async_session_maker() as session:
# Проверяем, существует ли пользователь
existing_user = await UserService.get_user_by_telegram_id(session, message.from_user.id)
is_new_user = existing_user is None
if is_new_user:
# Новый пользователь - начинаем онбординг
# Сначала создаём пользователя с дефолтными значениями
user = await UserService.get_or_create_user(
session,
telegram_id=message.from_user.id,
username=message.from_user.username
Добавлены основные функции MVP: тематические подборки, импорт слов, диалоговая практика, напоминания и тест уровня Новые команды: - /words [тема] - AI-генерация тематических подборок слов (10 слов по теме с учётом уровня) - /import - извлечение до 15 ключевых слов из текста (книги, статьи, песни) - /practice - диалоговая практика с AI в 6 сценариях (ресторан, магазин, путешествие, работа, врач, общение) - /reminder - настройка ежедневных напоминаний по расписанию - /level_test - тест из 7 вопросов для определения уровня английского (A1-C2) Основные изменения: - AI сервис: добавлены методы generate_thematic_words, extract_words_from_text, start_conversation, continue_conversation, generate_level_test - Диалоговая практика: исправление ошибок в реальном времени, подсказки, перевод реплик - Напоминания: APScheduler для ежедневной отправки напоминаний в выбранное время - Тест уровня: автоматическое определение уровня при регистрации, можно пропустить - База данных: добавлены поля reminders_enabled, last_reminder_sent - Vocabulary service: метод get_word_by_original для проверки дубликатов - Зависимости: apscheduler==3.10.4 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 15:46:02 +03:00
)
# Приветствие и первый вопрос - язык интерфейса
await message.answer(
f"👋 Welcome! / Привет! / ようこそ!\n\n"
"🌐 Choose your interface language:\n"
"🌐 Выбери язык интерфейса:\n"
"🌐 インターフェース言語を選択:",
reply_markup=onboarding_interface_keyboard()
)
await state.set_state(OnboardingStates.choosing_interface_lang)
return
# Существующий пользователь
user = existing_user
lang = (user.language_interface or 'ru')
await message.answer(
t(lang, "start.return", first_name=message.from_user.first_name),
reply_markup=main_menu_keyboard(lang),
)
# === Обработчики онбординга ===
@router.callback_query(F.data.startswith("onboard_interface_"), OnboardingStates.choosing_interface_lang)
async def onboard_set_interface(callback: CallbackQuery, state: FSMContext):
"""Установить язык интерфейса при онбординге"""
lang = callback.data.split("_")[-1] # ru | en | ja
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 state.update_data(interface_lang=lang)
# Второй вопрос - язык изучения
await callback.message.edit_text(
t(lang, 'onboarding.step2_title'),
reply_markup=onboarding_learning_keyboard(lang)
)
await state.set_state(OnboardingStates.choosing_learning_lang)
await callback.answer()
@router.callback_query(F.data.startswith("onboard_learning_"), OnboardingStates.choosing_learning_lang)
async def onboard_set_learning(callback: CallbackQuery, state: FSMContext):
"""Установить язык изучения при онбординге"""
learning_lang = callback.data.split("_")[-1] # en | ja
data = await state.get_data()
lang = data.get('interface_lang', 'ru')
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_learning_language(session, user.id, learning_lang)
await state.update_data(learning_lang=learning_lang)
# Третий вопрос - язык перевода
await callback.message.edit_text(
t(lang, 'onboarding.step3_title'),
reply_markup=onboarding_translation_keyboard(lang)
)
await state.set_state(OnboardingStates.choosing_translation_lang)
await callback.answer()
@router.callback_query(F.data.startswith("onboard_translation_"), OnboardingStates.choosing_translation_lang)
async def onboard_set_translation(callback: CallbackQuery, state: FSMContext):
"""Установить язык перевода при онбординге и завершить"""
translation_lang = callback.data.split("_")[-1] # ru | en | ja
data = await state.get_data()
lang = data.get('interface_lang', 'ru')
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_translation_language(session, user.id, translation_lang)
await state.clear()
# Приветствие с выбранными настройками
await callback.message.edit_text(t(lang, 'onboarding.complete'))
# Показываем главное меню и предлагаем тест уровня
await callback.message.answer(
t(lang, "start.new_intro", first_name=callback.from_user.first_name),
reply_markup=main_menu_keyboard(lang),
)
keyboard = InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text=t(lang, 'start.offer_btn'), callback_data="offer_level_test")],
[InlineKeyboardButton(text=t(lang, 'start.skip_btn'), callback_data="skip_level_test")]
])
await callback.message.answer(t(lang, "start.offer_test"), reply_markup=keyboard)
await callback.answer()
@router.message(Command("menu"))
async def cmd_menu(message: Message):
"""Показать клавиатуру с основными командами."""
# Определяем язык пользователя
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, message.from_user.id)
lang = (user.language_interface if user else 'ru') or 'ru'
await message.answer(t(lang, "menu.below"), reply_markup=main_menu_keyboard(lang))
# Обработчики кнопок главного меню (по тексту)
def _menu_match(key: str):
labels = {t('ru', key), t('en', key), t('ja', key)}
return lambda m: m.text in labels
@router.message(_menu_match('menu.add'))
async def btn_add_pressed(message: Message, state: FSMContext):
"""Показать меню добавления слов"""
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, message.from_user.id)
lang = (user.language_interface if user else 'ru') or 'ru'
keyboard = InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text=t(lang, 'add_menu.manual'), callback_data="add_manual")],
[InlineKeyboardButton(text=t(lang, 'add_menu.thematic'), callback_data="add_thematic")],
[InlineKeyboardButton(text=t(lang, 'add_menu.import'), callback_data="add_import")]
])
await message.answer(t(lang, 'add_menu.title'), reply_markup=keyboard)
@router.callback_query(F.data == "add_manual")
async def add_manual_callback(callback: CallbackQuery, state: FSMContext):
"""Добавить слово вручную"""
await callback.answer()
from bot.handlers.vocabulary import AddWordStates
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
lang = (user.language_interface if user else 'ru') or 'ru'
await state.set_state(AddWordStates.waiting_for_word)
await callback.message.edit_text(t(lang, 'add.prompt'))
@router.callback_query(F.data == "add_thematic")
async def add_thematic_callback(callback: CallbackQuery):
"""Тематические слова"""
await callback.answer()
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
lang = (user.language_interface if user else 'ru') or 'ru'
# Показываем подсказку по использованию /words
text = (
t(lang, 'words.help_title') + "\n\n" +
t(lang, 'words.help_usage') + "\n\n" +
t(lang, 'words.help_examples') + "\n\n" +
t(lang, 'words.help_note')
)
# Популярные темы как кнопки
keyboard = InlineKeyboardMarkup(inline_keyboard=[
[
InlineKeyboardButton(text=t(lang, 'words.topic_travel'), callback_data="words_travel"),
InlineKeyboardButton(text=t(lang, 'words.topic_food'), callback_data="words_food")
],
[
InlineKeyboardButton(text=t(lang, 'words.topic_work'), callback_data="words_work"),
InlineKeyboardButton(text=t(lang, 'words.topic_technology'), callback_data="words_technology")
],
[InlineKeyboardButton(text="⬅️ " + t(lang, 'settings.back'), callback_data="back_to_add_menu")]
])
await callback.message.edit_text(text, reply_markup=keyboard)
@router.callback_query(F.data.startswith("words_"))
async def words_topic_callback(callback: CallbackQuery, state: FSMContext):
"""Генерация слов по теме"""
topic = callback.data.replace("words_", "")
await callback.answer()
await callback.message.delete()
from bot.handlers.words import generate_words_for_theme
await generate_words_for_theme(callback.message, state, topic, callback.from_user.id)
@router.callback_query(F.data == "add_import")
async def add_import_callback(callback: CallbackQuery):
"""Показать меню импорта"""
await callback.answer()
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
lang = (user.language_interface if user else 'ru') or 'ru'
keyboard = InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text=t(lang, 'import_menu.from_text'), callback_data="import_from_text")],
[InlineKeyboardButton(text=t(lang, 'import_menu.from_file'), callback_data="import_from_file")],
[InlineKeyboardButton(text="⬅️ " + t(lang, 'settings.back'), callback_data="back_to_add_menu")]
])
await callback.message.edit_text(t(lang, 'import_menu.title'), reply_markup=keyboard)
@router.callback_query(F.data == "back_to_add_menu")
async def back_to_add_menu_callback(callback: CallbackQuery):
"""Вернуться в меню добавления"""
await callback.answer()
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
lang = (user.language_interface if user else 'ru') or 'ru'
keyboard = InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text=t(lang, 'add_menu.manual'), callback_data="add_manual")],
[InlineKeyboardButton(text=t(lang, 'add_menu.thematic'), callback_data="add_thematic")],
[InlineKeyboardButton(text=t(lang, 'add_menu.import'), callback_data="add_import")]
])
await callback.message.edit_text(t(lang, 'add_menu.title'), reply_markup=keyboard)
@router.message(_menu_match('menu.vocab'))
async def btn_vocab_pressed(message: Message):
from bot.handlers.vocabulary import cmd_vocabulary
await cmd_vocabulary(message)
@router.message(_menu_match('menu.task'))
async def btn_task_pressed(message: Message, state: FSMContext):
from bot.handlers.tasks import cmd_task
await cmd_task(message, state)
@router.message(_menu_match('menu.practice'))
async def btn_practice_pressed(message: Message, state: FSMContext):
"""Показать меню практики"""
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'))
async def btn_import_pressed(message: Message, state: FSMContext):
"""Показать меню импорта"""
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, message.from_user.id)
lang = (user.language_interface if user else 'ru') or 'ru'
keyboard = InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text=t(lang, 'import_menu.from_text'), callback_data="import_from_text")],
[InlineKeyboardButton(text=t(lang, 'import_menu.from_file'), callback_data="import_from_file")]
])
await message.answer(t(lang, 'import_menu.title'), reply_markup=keyboard)
@router.callback_query(F.data == "import_from_text")
async def import_from_text_callback(callback: CallbackQuery, state: FSMContext):
"""Импорт из текста"""
await callback.answer()
from bot.handlers.import_text import ImportStates
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
if not user:
await callback.message.edit_text(t('ru', 'common.start_first'))
return
lang = user.language_interface or 'ru'
await state.set_state(ImportStates.waiting_for_text)
await callback.message.edit_text(
t(lang, 'import.title') + "\n\n" +
t(lang, 'import.desc') + "\n\n" +
t(lang, 'import.can_send') + "\n\n" +
t(lang, 'import.cancel_hint')
)
@router.callback_query(F.data == "import_from_file")
async def import_from_file_callback(callback: CallbackQuery):
"""Импорт из файла"""
await callback.answer()
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
lang = (user.language_interface if user else 'ru') or 'ru'
await callback.message.edit_text(t(lang, 'import_menu.file_hint'))
@router.message(_menu_match('menu.stats'))
async def btn_stats_pressed(message: Message):
from bot.handlers.tasks import cmd_stats
await cmd_stats(message)
@router.message(_menu_match('menu.settings'))
async def btn_settings_pressed(message: Message):
from bot.handlers.settings import cmd_settings
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.games'))
async def btn_games_pressed(message: Message, state: FSMContext):
"""Показать меню мини-игр."""
from bot.handlers.minigames import cmd_games
await cmd_games(message, state)
@router.message(_menu_match('menu.words'))
async def btn_words_pressed(message: Message, state: FSMContext):
"""Подсказать про тематические слова и показать быстрые темы."""
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, message.from_user.id)
lang = (user.language_interface if user else 'ru') or 'ru'
text = (
t(lang, 'words.help_title') + "\n\n" +
t(lang, 'words.help_usage') + "\n\n" +
t(lang, 'words.popular')
)
keyboard = InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text=t(lang, 'words.topic_travel'), callback_data="menu_theme_travel")],
[InlineKeyboardButton(text=t(lang, 'words.topic_food'), callback_data="menu_theme_food")],
[InlineKeyboardButton(text=t(lang, 'words.topic_work'), callback_data="menu_theme_work")],
[InlineKeyboardButton(text=t(lang, 'words.topic_nature'), callback_data="menu_theme_nature")],
[InlineKeyboardButton(text=t(lang, 'words.topic_technology'), callback_data="menu_theme_technology")],
])
await message.answer(text, reply_markup=keyboard)
@router.callback_query(F.data.startswith("menu_theme_"))
async def pick_theme_from_menu(callback: CallbackQuery, state: FSMContext):
"""Сгенерировать слова по выбранной теме из меню и показать список."""
from database.db import async_session_maker
from services.user_service import UserService
from services.ai_service import ai_service
from bot.handlers.words import show_words_list, WordsStates
# Сразу отвечаем на callback, чтобы избежать таймаута
await callback.answer()
theme = callback.data.split("menu_theme_")[-1]
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
if not user:
await callback.answer(t('ru', 'common.start_first'), show_alert=True)
return
lang = (user.language_interface or 'ru')
current_level = get_user_level_for_language(user)
generating = await callback.message.answer(t(lang, 'words.generating', theme=theme))
words = await ai_service.generate_thematic_words(
theme=theme,
level=current_level,
count=10,
learning_lang=user.learning_language,
translation_lang=get_user_translation_lang(user),
user_id=user.id
)
await generating.delete()
if not words:
await callback.message.answer(t(lang, 'words.generate_failed'))
return
# Сохраняем в состояние как в /words
await state.update_data(theme=theme, words=words, user_id=user.id, level=current_level)
await state.set_state(WordsStates.viewing_words)
await show_words_list(callback.message, words, theme, user.id)
@router.message(Command("help"))
async def cmd_help(message: Message):
"""Обработчик команды /help"""
# Определяем язык пользователя
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, message.from_user.id)
lang = (user.language_interface if user else 'ru') or 'ru'
await message.answer(t(lang, "start.help"))
Добавлены основные функции MVP: тематические подборки, импорт слов, диалоговая практика, напоминания и тест уровня Новые команды: - /words [тема] - AI-генерация тематических подборок слов (10 слов по теме с учётом уровня) - /import - извлечение до 15 ключевых слов из текста (книги, статьи, песни) - /practice - диалоговая практика с AI в 6 сценариях (ресторан, магазин, путешествие, работа, врач, общение) - /reminder - настройка ежедневных напоминаний по расписанию - /level_test - тест из 7 вопросов для определения уровня английского (A1-C2) Основные изменения: - AI сервис: добавлены методы generate_thematic_words, extract_words_from_text, start_conversation, continue_conversation, generate_level_test - Диалоговая практика: исправление ошибок в реальном времени, подсказки, перевод реплик - Напоминания: APScheduler для ежедневной отправки напоминаний в выбранное время - Тест уровня: автоматическое определение уровня при регистрации, можно пропустить - База данных: добавлены поля reminders_enabled, last_reminder_sent - Vocabulary service: метод get_word_by_original для проверки дубликатов - Зависимости: apscheduler==3.10.4 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 15:46:02 +03:00
@router.callback_query(F.data == "offer_level_test")
async def offer_level_test_callback(callback: CallbackQuery, state: FSMContext):
"""Начать тест уровня из приветствия"""
from bot.handlers.level_test import start_level_test
await callback.message.delete()
await start_level_test(callback.message, state, telegram_id=callback.from_user.id)
Добавлены основные функции MVP: тематические подборки, импорт слов, диалоговая практика, напоминания и тест уровня Новые команды: - /words [тема] - AI-генерация тематических подборок слов (10 слов по теме с учётом уровня) - /import - извлечение до 15 ключевых слов из текста (книги, статьи, песни) - /practice - диалоговая практика с AI в 6 сценариях (ресторан, магазин, путешествие, работа, врач, общение) - /reminder - настройка ежедневных напоминаний по расписанию - /level_test - тест из 7 вопросов для определения уровня английского (A1-C2) Основные изменения: - AI сервис: добавлены методы generate_thematic_words, extract_words_from_text, start_conversation, continue_conversation, generate_level_test - Диалоговая практика: исправление ошибок в реальном времени, подсказки, перевод реплик - Напоминания: APScheduler для ежедневной отправки напоминаний в выбранное время - Тест уровня: автоматическое определение уровня при регистрации, можно пропустить - База данных: добавлены поля reminders_enabled, last_reminder_sent - Vocabulary service: метод get_word_by_original для проверки дубликатов - Зависимости: apscheduler==3.10.4 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 15:46:02 +03:00
await callback.answer()
@router.callback_query(F.data == "skip_level_test")
async def skip_level_test_callback(callback: CallbackQuery):
"""Пропустить тест уровня"""
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
lang = (user.language_interface if user else 'ru') or 'ru'
await callback.message.edit_text(t(lang, 'start.skip_msg'))
Добавлены основные функции MVP: тематические подборки, импорт слов, диалоговая практика, напоминания и тест уровня Новые команды: - /words [тема] - AI-генерация тематических подборок слов (10 слов по теме с учётом уровня) - /import - извлечение до 15 ключевых слов из текста (книги, статьи, песни) - /practice - диалоговая практика с AI в 6 сценариях (ресторан, магазин, путешествие, работа, врач, общение) - /reminder - настройка ежедневных напоминаний по расписанию - /level_test - тест из 7 вопросов для определения уровня английского (A1-C2) Основные изменения: - AI сервис: добавлены методы generate_thematic_words, extract_words_from_text, start_conversation, continue_conversation, generate_level_test - Диалоговая практика: исправление ошибок в реальном времени, подсказки, перевод реплик - Напоминания: APScheduler для ежедневной отправки напоминаний в выбранное время - Тест уровня: автоматическое определение уровня при регистрации, можно пропустить - База данных: добавлены поля reminders_enabled, last_reminder_sent - Vocabulary service: метод get_word_by_original для проверки дубликатов - Зависимости: apscheduler==3.10.4 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 15:46:02 +03:00
await callback.answer()