feat: restructure menu and add file import

- Consolidate "Add word" menu with submenu (Manual, Thematic, Import)
- Add file import support (.txt, .md) with AI batch translation
- Add vocabulary pagination with navigation buttons
- Add "Add word" button in tasks for new words mode
- Fix undefined variables bug in vocabulary confirm handler
- Add localization keys for add_menu in ru/en/ja

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-05 20:15:47 +03:00
parent 2097950c60
commit 63e2615243
12 changed files with 883 additions and 47 deletions

View File

@@ -30,10 +30,6 @@ def main_menu_keyboard(lang: str = 'ru') -> ReplyKeyboardMarkup:
KeyboardButton(text=t(lang, "menu.task")),
KeyboardButton(text=t(lang, "menu.practice")),
],
[
KeyboardButton(text=t(lang, "menu.words")),
KeyboardButton(text=t(lang, "menu.import")),
],
[
KeyboardButton(text=t(lang, "menu.stats")),
KeyboardButton(text=t(lang, "menu.settings")),
@@ -100,12 +96,109 @@ def _menu_match(key: str):
@router.message(_menu_match('menu.add'))
async def btn_add_pressed(message: Message, state: FSMContext):
from bot.handlers.vocabulary import AddWordStates
"""Показать меню добавления слов"""
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, 'add.prompt'))
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'))
@@ -128,8 +221,51 @@ async def btn_practice_pressed(message: Message, state: FSMContext):
@router.message(_menu_match('menu.import'))
async def btn_import_pressed(message: Message, state: FSMContext):
from bot.handlers.import_text import cmd_import
await cmd_import(message, state)
"""Показать меню импорта"""
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'))