2025-12-04 14:46:30 +03:00
|
|
|
|
from aiogram import Router, F
|
|
|
|
|
|
from aiogram.filters import Command
|
|
|
|
|
|
from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton
|
|
|
|
|
|
from aiogram.fsm.context import FSMContext
|
|
|
|
|
|
|
|
|
|
|
|
from database.db import async_session_maker
|
2025-12-04 19:40:01 +03:00
|
|
|
|
from bot.handlers.start import main_menu_keyboard
|
2025-12-04 14:46:30 +03:00
|
|
|
|
from services.user_service import UserService
|
2025-12-05 14:30:24 +03:00
|
|
|
|
from utils.i18n import t, get_user_lang
|
|
|
|
|
|
from utils.levels import (
|
|
|
|
|
|
get_user_level_for_language,
|
|
|
|
|
|
get_available_levels,
|
|
|
|
|
|
get_level_system,
|
|
|
|
|
|
get_level_key_for_i18n,
|
|
|
|
|
|
)
|
2025-12-04 14:46:30 +03:00
|
|
|
|
|
|
|
|
|
|
router = Router()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_settings_keyboard(user) -> InlineKeyboardMarkup:
|
|
|
|
|
|
"""Создать клавиатуру настроек"""
|
2025-12-05 14:30:24 +03:00
|
|
|
|
lang = get_user_lang(user)
|
|
|
|
|
|
ui_lang_code = getattr(user, 'language_interface', 'ru') or 'ru'
|
|
|
|
|
|
current_level = get_user_level_for_language(user)
|
2025-12-04 14:46:30 +03:00
|
|
|
|
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
|
|
|
|
|
[InlineKeyboardButton(
|
2025-12-05 14:30:24 +03:00
|
|
|
|
text=t(lang, 'settings.level_prefix') + f"{current_level}",
|
2025-12-04 14:46:30 +03:00
|
|
|
|
callback_data="settings_level"
|
|
|
|
|
|
)],
|
|
|
|
|
|
[InlineKeyboardButton(
|
2025-12-05 14:30:24 +03:00
|
|
|
|
text=t(lang, 'settings.learning_prefix') + user.learning_language.upper(),
|
2025-12-04 19:40:01 +03:00
|
|
|
|
callback_data="settings_learning"
|
|
|
|
|
|
)],
|
|
|
|
|
|
[InlineKeyboardButton(
|
2025-12-05 14:30:24 +03:00
|
|
|
|
text=t(lang, 'settings.interface_prefix') + t(lang, f'settings.lang_name.{ui_lang_code}'),
|
2025-12-04 14:46:30 +03:00
|
|
|
|
callback_data="settings_language"
|
|
|
|
|
|
)],
|
|
|
|
|
|
[InlineKeyboardButton(
|
2025-12-05 14:30:24 +03:00
|
|
|
|
text=t(lang, 'settings.close'),
|
2025-12-04 14:46:30 +03:00
|
|
|
|
callback_data="settings_close"
|
|
|
|
|
|
)]
|
|
|
|
|
|
])
|
|
|
|
|
|
return keyboard
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-12-04 19:40:01 +03:00
|
|
|
|
def get_level_keyboard(user=None) -> InlineKeyboardMarkup:
|
2025-12-05 14:30:24 +03:00
|
|
|
|
"""Клавиатура выбора уровня (CEFR или JLPT в зависимости от языка изучения)"""
|
|
|
|
|
|
lang = get_user_lang(user)
|
|
|
|
|
|
learning_lang = getattr(user, 'learning_language', 'en') or 'en'
|
|
|
|
|
|
available_levels = get_available_levels(learning_lang)
|
2025-12-04 14:46:30 +03:00
|
|
|
|
|
|
|
|
|
|
keyboard = []
|
2025-12-05 14:30:24 +03:00
|
|
|
|
for level in available_levels:
|
|
|
|
|
|
# Ключ локализации: settings.level.a1 или settings.jlpt.n5
|
|
|
|
|
|
i18n_key = get_level_key_for_i18n(learning_lang, level)
|
|
|
|
|
|
level_name = t(lang, i18n_key)
|
|
|
|
|
|
keyboard.append([InlineKeyboardButton(text=level_name, callback_data=f"set_level_{level}")])
|
2025-12-04 14:46:30 +03:00
|
|
|
|
|
2025-12-05 14:30:24 +03:00
|
|
|
|
keyboard.append([InlineKeyboardButton(text=t(lang, 'settings.back'), callback_data="settings_back")])
|
2025-12-04 14:46:30 +03:00
|
|
|
|
|
|
|
|
|
|
return InlineKeyboardMarkup(inline_keyboard=keyboard)
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-12-04 19:40:01 +03:00
|
|
|
|
def get_language_keyboard(user=None) -> InlineKeyboardMarkup:
|
2025-12-04 14:46:30 +03:00
|
|
|
|
"""Клавиатура выбора языка интерфейса"""
|
2025-12-05 14:30:24 +03:00
|
|
|
|
lang = get_user_lang(user)
|
2025-12-04 14:46:30 +03:00
|
|
|
|
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
2025-12-05 14:30:24 +03:00
|
|
|
|
[InlineKeyboardButton(text=t(lang, 'settings.lang_name.ru'), callback_data="set_lang_ru")],
|
|
|
|
|
|
[InlineKeyboardButton(text=t(lang, 'settings.lang_name.en'), callback_data="set_lang_en")],
|
|
|
|
|
|
[InlineKeyboardButton(text=t(lang, 'settings.lang_name.ja'), callback_data="set_lang_ja")],
|
|
|
|
|
|
[InlineKeyboardButton(text=t(lang, 'settings.back'), callback_data="settings_back")]
|
2025-12-04 14:46:30 +03:00
|
|
|
|
])
|
|
|
|
|
|
return keyboard
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-12-04 19:40:01 +03:00
|
|
|
|
def get_learning_language_keyboard(user=None) -> InlineKeyboardMarkup:
|
|
|
|
|
|
"""Клавиатура выбора языка изучения"""
|
2025-12-05 14:30:24 +03:00
|
|
|
|
lang = get_user_lang(user)
|
2025-12-04 19:40:01 +03:00
|
|
|
|
|
|
|
|
|
|
options = [
|
2025-12-05 14:30:24 +03:00
|
|
|
|
("en", t(lang, 'settings.learning_lang.en')),
|
|
|
|
|
|
("ja", t(lang, 'settings.learning_lang.ja')),
|
2025-12-05 14:42:19 +03:00
|
|
|
|
# TODO: добавить позже
|
|
|
|
|
|
# ("es", t(lang, 'settings.learning_lang.es')),
|
|
|
|
|
|
# ("de", t(lang, 'settings.learning_lang.de')),
|
|
|
|
|
|
# ("fr", t(lang, 'settings.learning_lang.fr')),
|
2025-12-04 19:40:01 +03:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
keyboard = [[InlineKeyboardButton(text=label, callback_data=f"set_learning_{code}")] for code, label in options]
|
2025-12-05 14:30:24 +03:00
|
|
|
|
keyboard.append([InlineKeyboardButton(text=t(lang, 'settings.back'), callback_data="settings_back")])
|
2025-12-04 19:40:01 +03:00
|
|
|
|
return InlineKeyboardMarkup(inline_keyboard=keyboard)
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-12-04 14:46:30 +03:00
|
|
|
|
@router.message(Command("settings"))
|
|
|
|
|
|
async def cmd_settings(message: Message):
|
|
|
|
|
|
"""Обработчик команды /settings"""
|
|
|
|
|
|
async with async_session_maker() as session:
|
|
|
|
|
|
user = await UserService.get_user_by_telegram_id(session, message.from_user.id)
|
|
|
|
|
|
|
|
|
|
|
|
if not user:
|
2025-12-05 14:30:24 +03:00
|
|
|
|
await message.answer(t('ru', 'common.start_first'))
|
2025-12-04 14:46:30 +03:00
|
|
|
|
return
|
|
|
|
|
|
|
2025-12-05 14:30:24 +03:00
|
|
|
|
lang = get_user_lang(user)
|
|
|
|
|
|
ui_lang_code = getattr(user, 'language_interface', 'ru') or 'ru'
|
|
|
|
|
|
lang_value = t(lang, f'settings.lang_name.{ui_lang_code}')
|
|
|
|
|
|
current_level = get_user_level_for_language(user)
|
|
|
|
|
|
settings_text = (
|
|
|
|
|
|
t(lang, 'settings.title') +
|
|
|
|
|
|
t(lang, 'settings.level_prefix') + f"<b>{current_level}</b>\n" +
|
|
|
|
|
|
t(lang, 'settings.interface_prefix') + f"<b>{lang_value}</b>\n\n" +
|
|
|
|
|
|
t(lang, 'settings.choose')
|
|
|
|
|
|
)
|
2025-12-04 14:46:30 +03:00
|
|
|
|
|
|
|
|
|
|
await message.answer(settings_text, reply_markup=get_settings_keyboard(user))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.callback_query(F.data == "settings_level")
|
|
|
|
|
|
async def settings_level(callback: CallbackQuery):
|
|
|
|
|
|
"""Показать выбор уровня"""
|
2025-12-04 19:40:01 +03:00
|
|
|
|
async with async_session_maker() as session:
|
|
|
|
|
|
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
|
2025-12-05 14:30:24 +03:00
|
|
|
|
lang = get_user_lang(user)
|
|
|
|
|
|
learning_lang = getattr(user, 'learning_language', 'en') or 'en'
|
|
|
|
|
|
level_system = get_level_system(learning_lang)
|
|
|
|
|
|
# Выбираем правильное описание групп уровней
|
|
|
|
|
|
groups_key = 'settings.jlpt_groups' if level_system == 'jlpt' else 'settings.level_groups'
|
|
|
|
|
|
text = t(lang, 'settings.level_title') + t(lang, groups_key) + t(lang, 'settings.level_hint')
|
|
|
|
|
|
await callback.message.edit_text(text, reply_markup=get_level_keyboard(user))
|
2025-12-04 19:40:01 +03:00
|
|
|
|
await callback.answer()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.callback_query(F.data == "settings_learning")
|
|
|
|
|
|
async def settings_learning(callback: CallbackQuery):
|
|
|
|
|
|
"""Показать выбор языка изучения"""
|
|
|
|
|
|
async with async_session_maker() as session:
|
|
|
|
|
|
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
|
2025-12-05 14:30:24 +03:00
|
|
|
|
lang = get_user_lang(user)
|
|
|
|
|
|
await callback.message.edit_text(t(lang, 'settings.learning_title'), reply_markup=get_learning_language_keyboard(user))
|
2025-12-04 19:40:01 +03:00
|
|
|
|
await callback.answer()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.callback_query(F.data.startswith("set_learning_"))
|
|
|
|
|
|
async def set_learning_language(callback: CallbackQuery):
|
|
|
|
|
|
"""Установить язык изучения"""
|
|
|
|
|
|
code = callback.data.split("_")[-1]
|
|
|
|
|
|
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, code)
|
2025-12-05 14:30:24 +03:00
|
|
|
|
lang = get_user_lang(user)
|
|
|
|
|
|
text = t(lang, 'settings.learning_changed', code=code.upper())
|
2025-12-04 19:40:01 +03:00
|
|
|
|
await callback.message.edit_text(
|
|
|
|
|
|
text,
|
2025-12-05 14:30:24 +03:00
|
|
|
|
reply_markup=InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text=t(lang, 'settings.back_to_settings'), callback_data="settings_back")]])
|
2025-12-04 19:40:01 +03:00
|
|
|
|
)
|
2025-12-04 14:46:30 +03:00
|
|
|
|
await callback.answer()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.callback_query(F.data.startswith("set_level_"))
|
|
|
|
|
|
async def set_level(callback: CallbackQuery):
|
2025-12-05 14:30:24 +03:00
|
|
|
|
"""Установить уровень (CEFR или JLPT)"""
|
|
|
|
|
|
level_str = callback.data.split("_")[-1] # A1, A2, B1, B2, C1, C2 или N5, N4, N3, N2, N1
|
2025-12-04 14:46:30 +03:00
|
|
|
|
|
|
|
|
|
|
async with async_session_maker() as session:
|
|
|
|
|
|
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
|
|
|
|
|
|
|
|
|
|
|
|
if user:
|
2025-12-05 14:30:24 +03:00
|
|
|
|
# Передаём строковый уровень, UserService сам разберётся с системой
|
|
|
|
|
|
await UserService.update_user_level(session, user.id, level_str)
|
2025-12-04 14:46:30 +03:00
|
|
|
|
|
2025-12-05 14:30:24 +03:00
|
|
|
|
lang = get_user_lang(user)
|
|
|
|
|
|
msg = t(lang, 'settings.level_changed', level=level_str) + t(lang, 'settings.level_changed_hint')
|
2025-12-04 14:46:30 +03:00
|
|
|
|
await callback.message.edit_text(
|
2025-12-04 19:40:01 +03:00
|
|
|
|
msg,
|
2025-12-05 14:30:24 +03:00
|
|
|
|
reply_markup=InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text=t(lang, 'settings.back_to_settings'), callback_data="settings_back")]])
|
2025-12-04 14:46:30 +03:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
await callback.answer()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.callback_query(F.data == "settings_language")
|
|
|
|
|
|
async def settings_language(callback: CallbackQuery):
|
|
|
|
|
|
"""Показать выбор языка"""
|
2025-12-04 19:40:01 +03:00
|
|
|
|
async with async_session_maker() as session:
|
|
|
|
|
|
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
|
2025-12-05 14:30:24 +03:00
|
|
|
|
lang = get_user_lang(user)
|
2025-12-04 14:46:30 +03:00
|
|
|
|
await callback.message.edit_text(
|
2025-12-05 14:30:24 +03:00
|
|
|
|
t(lang, 'settings.lang_title') + t(lang, 'settings.lang_desc'),
|
2025-12-04 19:40:01 +03:00
|
|
|
|
reply_markup=get_language_keyboard(user)
|
2025-12-04 14:46:30 +03:00
|
|
|
|
)
|
|
|
|
|
|
await callback.answer()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.callback_query(F.data.startswith("set_lang_"))
|
|
|
|
|
|
async def set_language(callback: CallbackQuery):
|
|
|
|
|
|
"""Установить язык"""
|
2025-12-05 14:30:24 +03:00
|
|
|
|
new_lang = callback.data.split("_")[-1] # ru | en | ja
|
2025-12-04 14:46:30 +03:00
|
|
|
|
|
|
|
|
|
|
async with async_session_maker() as session:
|
|
|
|
|
|
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
|
|
|
|
|
|
|
|
|
|
|
|
if user:
|
2025-12-05 14:30:24 +03:00
|
|
|
|
await UserService.update_user_language(session, user.id, new_lang)
|
|
|
|
|
|
# Используем новый язык для сообщений
|
|
|
|
|
|
text = t(new_lang, 'settings.lang_changed')
|
2025-12-04 14:46:30 +03:00
|
|
|
|
await callback.message.edit_text(
|
2025-12-04 19:40:01 +03:00
|
|
|
|
text,
|
2025-12-05 14:30:24 +03:00
|
|
|
|
reply_markup=InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text=t(new_lang, 'settings.back'), callback_data="settings_back")]])
|
2025-12-04 14:46:30 +03:00
|
|
|
|
)
|
2025-12-04 19:40:01 +03:00
|
|
|
|
# Обновляем клавиатуру чата на выбранный язык
|
2025-12-05 14:30:24 +03:00
|
|
|
|
await callback.message.answer(t(new_lang, 'settings.menu_updated'), reply_markup=main_menu_keyboard(new_lang))
|
2025-12-04 14:46:30 +03:00
|
|
|
|
|
|
|
|
|
|
await callback.answer()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.callback_query(F.data == "settings_back")
|
|
|
|
|
|
async def settings_back(callback: CallbackQuery):
|
|
|
|
|
|
"""Вернуться к настройкам"""
|
|
|
|
|
|
async with async_session_maker() as session:
|
|
|
|
|
|
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
|
|
|
|
|
|
|
|
|
|
|
|
if user:
|
2025-12-05 14:30:24 +03:00
|
|
|
|
lang = get_user_lang(user)
|
|
|
|
|
|
ui_lang_code = getattr(user, 'language_interface', 'ru') or 'ru'
|
|
|
|
|
|
lang_value = t(lang, f'settings.lang_name.{ui_lang_code}')
|
|
|
|
|
|
current_level = get_user_level_for_language(user)
|
|
|
|
|
|
settings_text = (
|
|
|
|
|
|
t(lang, 'settings.title') +
|
|
|
|
|
|
t(lang, 'settings.level_prefix') + f"<b>{current_level}</b>\n" +
|
|
|
|
|
|
t(lang, 'settings.interface_prefix') + f"<b>{lang_value}</b>\n\n" +
|
|
|
|
|
|
t(lang, 'settings.choose')
|
|
|
|
|
|
)
|
2025-12-04 14:46:30 +03:00
|
|
|
|
|
|
|
|
|
|
await callback.message.edit_text(settings_text, reply_markup=get_settings_keyboard(user))
|
|
|
|
|
|
|
|
|
|
|
|
await callback.answer()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.callback_query(F.data == "settings_close")
|
|
|
|
|
|
async def settings_close(callback: CallbackQuery):
|
|
|
|
|
|
"""Закрыть настройки"""
|
|
|
|
|
|
await callback.message.delete()
|
|
|
|
|
|
await callback.answer()
|