diff --git a/commands/settings.py b/commands/settings.py index 47bbe78..2345a15 100644 --- a/commands/settings.py +++ b/commands/settings.py @@ -1,8 +1,11 @@ +from typing import Optional, Tuple, List from aiogram import Router, types -from aiogram.filters import Command -from aiogram.types import InlineKeyboardMarkup, \ - InlineKeyboardButton, \ - CallbackQuery +from aiogram.filters import Command, Text +from aiogram.types import ( + InlineKeyboardMarkup, + InlineKeyboardButton, + CallbackQuery, +) import json from bot import db @@ -11,34 +14,57 @@ from i18n.localization import I18n router = Router() i18n = I18n() -lang_options = [ +LangOption = Tuple[str, str] +PeriodOption = Tuple[str, str] + +LANG_OPTIONS: List[LangOption] = [ ("en", "English"), ("ru", "Русский"), ] -period_options = [ +PERIOD_OPTIONS: List[PeriodOption] = [ ("week", "week"), ("month", "month"), ("quarter", "quarter"), ("year", "year"), ] + +async def get_user_locale(user_id: int) -> dict: + data = await db.fetch( + 'SELECT lang FROM users WHERE user_id = $1', user_id + ) + if not data: + await db.insert( + 'INSERT INTO users (user_id) VALUES (?)', user_id + ) + data = await db.fetch( + 'SELECT lang FROM users WHERE user_id = $1', user_id + ) + lang = data.get('lang', 'en') + return i18n.get_locale(lang) + + def build_options_keyboard( - options: list[tuple[str, str]], + options: List[Tuple[str, str]], current_value: str, callback_prefix: str, locale: dict, buttons_per_row: int = 2, - back_callback: str = None + back_callback: Optional[str] = None, ) -> InlineKeyboardMarkup: - buttons = [] - row = [] + buttons: List[List[InlineKeyboardButton]] = [] + row: List[InlineKeyboardButton] = [] + for code, label_key in options: label = locale.get(label_key, label_key) - text = f"[X] {label}" if code == current_value else label - row.append(InlineKeyboardButton( - text=text, - callback_data=f"{callback_prefix}_{code}") + text = ( + f"[X] {label}" if code == current_value else label + ) + row.append( + InlineKeyboardButton( + text=text, callback_data=f"{callback_prefix}_{code}" + ) ) if len(row) == buttons_per_row: buttons.append(row) @@ -46,203 +72,182 @@ def build_options_keyboard( if row: buttons.append(row) if back_callback: - buttons.append([ - InlineKeyboardButton( - text=locale.get("back"), - callback_data=back_callback - ) - ]) + buttons.append( + [ + InlineKeyboardButton( + text=locale.get("back"), + callback_data=back_callback, + ) + ] + ) return InlineKeyboardMarkup(inline_keyboard=buttons) + def get_chart_toggle_keyboard( - chart_enabled: bool, - locale: dict + chart_enabled: bool, locale: dict ) -> InlineKeyboardMarkup: - toggle_text = locale.get("chart_disable") \ - if chart_enabled \ + toggle_text = ( + locale.get("chart_disable") + if chart_enabled else locale.get("chart_enable") + ) + return InlineKeyboardMarkup( + inline_keyboard=[ + [ + InlineKeyboardButton( + text=toggle_text, callback_data="chart_toggle" + ), + InlineKeyboardButton( + text=locale.get("chart_period"), + callback_data="chart_period", + ), + ], + [ + InlineKeyboardButton( + text=locale.get("back"), + callback_data="back_to_settings", + ), + ], + ] + ) - return InlineKeyboardMarkup(inline_keyboard=[ - [ - InlineKeyboardButton( - text=toggle_text, - callback_data="chart_toggle"), - InlineKeyboardButton( - text=locale.get("chart_period"), - callback_data="chart_period" - ), - ], - [ - InlineKeyboardButton( - text=locale.get("back"), - callback_data="back_to_settings" - ), - ], - ]) -def markup_to_json(markup: InlineKeyboardMarkup | None) -> str | None: +def markup_to_json( + markup: Optional[InlineKeyboardMarkup], +) -> Optional[str]: if markup is None: return None - # model_dump() возвращает dict с данными return json.dumps(markup.model_dump(), sort_keys=True) + async def safe_edit_message_text( callback: CallbackQuery, new_text: str, - new_reply_markup: InlineKeyboardMarkup | None = None, - parse_mode: str | None = None, + new_reply_markup: Optional[InlineKeyboardMarkup] = None, + parse_mode: Optional[str] = None, ) -> None: message = callback.message - current_text = (message.text or "").strip() new_text_clean = new_text.strip() - is_text_same = (current_text == new_text_clean) - - current_markup = message.reply_markup - - is_markup_same = markup_to_json(current_markup) == markup_to_json(new_reply_markup) + is_text_same = current_text == new_text_clean + is_markup_same = ( + markup_to_json(message.reply_markup) + == markup_to_json(new_reply_markup) + ) if is_text_same and is_markup_same: await callback.answer() return await message.edit_text( - new_text, - reply_markup=new_reply_markup, - parse_mode=parse_mode + new_text, reply_markup=new_reply_markup, parse_mode=parse_mode ) await callback.answer() + @router.message(Command("settings")) async def settings_handler(message: types.Message): - data = await db.fetch( - 'SELECT lang FROM users WHERE user_id = $1', - message.from_user.id + locale = await get_user_locale(message.from_user.id) + + settings_keyboard = InlineKeyboardMarkup( + inline_keyboard=[ + [ + InlineKeyboardButton( + text=locale.get("setting_chart"), + callback_data="setting_chart", + ), + InlineKeyboardButton( + text=locale.get("setting_lang"), + callback_data="setting_lang", + ), + ], + ] ) - if not data: - await db.insert( - 'INSERT INTO users (user_id) VALUES (?)', - message.from_user.id - ) - data = await db.fetch( - 'SELECT lang FROM users WHERE user_id = $1', - message.from_user.id - ) - - lang = data.get('lang', 'en') - locale = i18n.get_locale(lang) - - settings_keyboard = InlineKeyboardMarkup(inline_keyboard=[ - [ - InlineKeyboardButton( - text=locale.get("setting_chart"), - callback_data="setting_chart"), - InlineKeyboardButton( - text=locale.get("setting_lang"), - callback_data="setting_lang"), - ], - ]) - await message.answer( - locale.get("settings_title"), - reply_markup=settings_keyboard + locale.get("settings_title"), reply_markup=settings_keyboard ) -@router.callback_query(lambda c: c.data == "setting_lang") + +@router.callback_query(Text("setting_lang")) async def show_language_menu(callback: CallbackQuery): - data = await db.fetch( - 'SELECT lang FROM users WHERE user_id = $1', - callback.from_user.id - ) - lang = data.get('lang', 'en') - locale = i18n.get_locale(lang) + locale = await get_user_locale(callback.from_user.id) keyboard = build_options_keyboard( - options=lang_options, - current_value=lang, + options=LANG_OPTIONS, + current_value=locale.get("lang", "en"), callback_prefix="lang", locale=locale, - buttons_per_row=2, - back_callback="back_to_settings" - ) - await safe_edit_message_text( - callback, - new_text=locale.get("choose_language"), - new_reply_markup=keyboard + back_callback="back_to_settings", ) -@router.callback_query(lambda c: c.data and c.data.startswith("lang_")) + await safe_edit_message_text( + callback, locale.get("choose_language"), keyboard + ) + + +@router.callback_query(Text(startswith="lang_")) async def language_selected(callback: CallbackQuery): lang = callback.data.split("_")[1] await db.update( 'UPDATE users SET lang = $1 WHERE user_id = $2', - lang, callback.from_user.id + lang, + callback.from_user.id, ) locale = i18n.get_locale(lang) keyboard = build_options_keyboard( - options=lang_options, + options=LANG_OPTIONS, current_value=lang, callback_prefix="lang", locale=locale, - buttons_per_row=2, - back_callback="back_to_settings" + back_callback="back_to_settings", ) await safe_edit_message_text( - callback, - new_text=locale.get("choose_language"), - new_reply_markup=keyboard + callback, locale.get("choose_language"), keyboard ) - await callback.answer( locale.get("language_set").format(lang=lang.upper()) ) -@router.callback_query(lambda c: c.data == "back_to_settings") +@router.callback_query(Text("back_to_settings")) async def back_to_settings(callback: CallbackQuery): - data = await db.fetch( - 'SELECT lang FROM users WHERE user_id = $1', - callback.from_user.id - ) - lang = data.get('lang', 'en') - locale = i18n.get_locale(lang) + locale = await get_user_locale(callback.from_user.id) - settings_keyboard = InlineKeyboardMarkup(inline_keyboard=[ - [ - InlineKeyboardButton( - text=locale.get("setting_chart"), - callback_data="setting_chart"), - InlineKeyboardButton( - text=locale.get("setting_lang"), - callback_data="setting_lang"), - ], - ]) + settings_keyboard = InlineKeyboardMarkup( + inline_keyboard=[ + [ + InlineKeyboardButton( + text=locale.get("setting_chart"), + callback_data="setting_chart", + ), + InlineKeyboardButton( + text=locale.get("setting_lang"), + callback_data="setting_lang", + ), + ], + ] + ) await safe_edit_message_text( - callback, - new_text=locale.get("settings_title"), - new_reply_markup=settings_keyboard + callback, locale.get("settings_title"), settings_keyboard ) -@router.callback_query(lambda c: c.data == "setting_chart") +@router.callback_query(Text("setting_chart")) async def show_chart_settings(callback: CallbackQuery): data = await db.fetch( 'SELECT chart, chart_period, lang FROM users WHERE user_id = $1', - callback.from_user.id + callback.from_user.id, ) - lang = data.get('lang', 'en') + lang = data.get("lang", "en") locale = i18n.get_locale(lang) - chart_status = bool(data.get('chart', 1)) - period = data.get('chart_period') - - status_text = locale.get("enabled", "Enabled") \ - if chart_status \ - else locale.get("disabled", "Disabled") + chart_status = bool(data.get("chart", 1)) + period = data.get("chart_period") or "week" + status_text = locale.get("enabled") if chart_status else locale.get("disabled") period_text = locale.get(period, period) text = ( @@ -250,93 +255,85 @@ async def show_chart_settings(callback: CallbackQuery): f"{locale.get('status')}: {status_text}\n" f"{locale.get('period')}: {period_text}" ) - keyboard = get_chart_toggle_keyboard(chart_status, locale) - await safe_edit_message_text( - callback, - new_text=text, - new_reply_markup=keyboard - ) + await safe_edit_message_text(callback, text, keyboard) -@router.callback_query(lambda c: c.data == "chart_toggle") +@router.callback_query(Text("chart_toggle")) async def toggle_chart(callback: CallbackQuery): data = await db.fetch( 'SELECT chart, lang FROM users WHERE user_id = $1', - callback.from_user.id + callback.from_user.id, ) - lang = data.get('lang', 'en') + lang = data.get("lang", "en") locale = i18n.get_locale(lang) - current_status = data.get('chart', True) + current_status = data.get("chart", True) new_status = not current_status await db.update( 'UPDATE users SET chart = $1 WHERE user_id = $2', - new_status, callback.from_user.id + new_status, + callback.from_user.id, ) await callback.answer( - locale.get(f"chart_now_{'enabled' if new_status else 'disabled'}", - f"Chart now {'enabled' if new_status else 'disabled'}") + locale.get( + f"chart_now_{'enabled' if new_status else 'disabled'}", + f"Chart now {'enabled' if new_status else 'disabled'}", + ) ) await show_chart_settings(callback) -@router.callback_query(lambda c: c.data == "chart_period") + +@router.callback_query(Text("chart_period")) async def change_chart_period(callback: CallbackQuery): data = await db.fetch( 'SELECT chart_period, lang FROM users WHERE user_id = $1', - callback.from_user.id + callback.from_user.id, ) - lang = data.get('lang', 'en') + lang = data.get("lang", "en") locale = i18n.get_locale(lang) - current_period = data.get('chart_period') + current_period = data.get("chart_period") keyboard = build_options_keyboard( - options=period_options, + options=PERIOD_OPTIONS, current_value=current_period, callback_prefix="period", locale=locale, - buttons_per_row=2, - back_callback="setting_chart" - ) - await safe_edit_message_text( - callback, - new_text=locale.get("choose_period"), - new_reply_markup=keyboard + back_callback="setting_chart", ) -@router.callback_query(lambda c: c.data and c.data.startswith("period_")) + await safe_edit_message_text( + callback, + locale.get("choose_period"), + keyboard + ) + +@router.callback_query(Text(startswith="period_")) async def set_chart_period(callback: CallbackQuery): period = callback.data.split("_")[1] await db.update( 'UPDATE users SET chart_period = $1 WHERE user_id = $2', - period, callback.from_user.id + period, + callback.from_user.id, ) - data = await db.fetch( - 'SELECT lang FROM users WHERE user_id = $1', - callback.from_user.id - ) - lang = data.get('lang', 'en') - locale = i18n.get_locale(lang) + + locale = await get_user_locale(callback.from_user.id) keyboard = build_options_keyboard( - options=period_options, + options=PERIOD_OPTIONS, current_value=period, callback_prefix="period", locale=locale, - buttons_per_row=2, - back_callback="setting_chart" + back_callback="setting_chart", ) await safe_edit_message_text( callback, - new_text=locale.get("choose_period"), - new_reply_markup=keyboard - ) - - await callback.answer( - locale.get("period_set").format(period=period) + locale.get("choose_period"), + keyboard ) + await callback.answer(locale.get("period_set").format(period=period))