diff --git a/bot/handlers/commands/settings/set_widget_cmd.py b/bot/handlers/commands/settings/set_widget_cmd.py new file mode 100644 index 0000000..3959c88 --- /dev/null +++ b/bot/handlers/commands/settings/set_widget_cmd.py @@ -0,0 +1,174 @@ +from aiogram import Router, F, Bot +from aiogram.exceptions import TelegramAPIError, TelegramRetryAfter +from aiogram.filters import Command, CommandObject +from aiogram.fsm.context import FSMContext +from aiogram.fsm.state import StatesGroup, State +from aiogram.types import Message, CallbackQuery +from aiogram.utils.i18n import gettext as _ + +from bot.core.bots import BotInfo +from bot.filters import IsOwner +from bot.handlers.commands.settings.settings_cmd import settings_keyboard +from bot.templates import msg +from bot.utils import format_retry_time, status_clear +from configs import COMMANDS +from middleware.loggers import logger + +__all__ = ("router",) +CMD: str = "set_widget".lower() +router: Router = Router(name=f"{CMD}_cmd_router") + + +class SetWidgetForm(StatesGroup): + """Состояния FSM для изменения виджета (описания бота).""" + new_widget: State = State() + + +async def handle_set_widget( + new_widget: str, + message: Message | CallbackQuery, + state: FSMContext, + bot: Bot +) -> None: + """ + Устанавливает новое значение виджета (описания бота). + + Args: + new_widget (str): Новый текст виджета. + message (Message | CallbackQuery): Объект сообщения или callback-запроса. + state (FSMContext): Контекст состояния FSM. + bot (Bot): Экземпляр текущего бота. + """ + # Проверка длины текста (Telegram API ограничивает description до 512 символов) + if len(new_widget) > 512: + await msg( + update=message, + text=_("❌ Виджет бота должен быть не более 512 символов. Текущая длина: {length}").format( + length=len(new_widget) + ), + markup=settings_keyboard(), + state=state + ) + return + + try: + # Устанавливаем описание через Telegram API + await bot.set_my_description(description=new_widget) + + # Сохраняем в BotInfo для локального использования + BotInfo.widget = new_widget + + # Очищаем состояние FSM + await state.clear() + + # Отправляем уведомление пользователю + await msg( + update=message, + text=_("✅ Виджет бота успешно изменён на: {new_widget}").format( + new_widget=new_widget + ), + markup=settings_keyboard(), + state=state + ) + + logger.info(f"Виджет бота изменён на: {new_widget}") + + except TelegramRetryAfter as e: + # Если запрос слишком частый + retry_text: str = format_retry_time(e.retry_after) + logger.warning(f"Превышен лимит запросов при смене виджета. Попробуйте через {retry_text}") + await msg( + update=message, + text=_("⚠️ Слишком частая смена виджета!\nПопробуйте снова через: {retry_text}").format( + retry_text=retry_text + ), + markup=settings_keyboard(), + state=state + ) + + except TelegramAPIError as e: + # Ошибка Telegram API + logger.error(f"Ошибка Telegram API при изменении виджета: {e}") + await msg( + update=message, + text=_("❌ Ошибка Telegram API при изменении виджета:
{error}").format(error=str(e)),
+ markup=settings_keyboard(),
+ state=state
+ )
+
+ except Exception as e:
+ # Непредвиденная ошибка
+ logger.error(f"Непредвиденная ошибка при изменении виджета: {e}")
+ await msg(
+ update=message,
+ text=_("❌ Непредвиденная ошибка при изменении виджета: {error}").format(error=str(e)),
+ markup=settings_keyboard(),
+ state=state
+ )
+
+
+@router.callback_query(F.data.lower() == CMD, IsOwner())
+@router.message(Command(*COMMANDS[CMD], prefix=BotInfo.prefix, ignore_case=True), IsOwner())
+async def settings_cmd(
+ message: Message | CallbackQuery,
+ state: FSMContext,
+ bot: Bot,
+ command: CommandObject | None = None
+) -> None:
+ """
+ Обработчик команды /set_widget.
+
+ Поддерживает:
+ 1. Немедленное изменение через аргумент команды (/set_widget TEXT).
+ 2. Callback-запрос.
+ 3. FSM ввод.
+ """
+ # Получаем текущее значение виджета
+ current_widget: str = BotInfo.short_description
+
+ # Вариант 1: пользователь ввёл аргумент сразу (/set_widget TEXT)
+ if command and command.args:
+ new_widget: str = command.args.strip()
+ if len(new_widget) > 512:
+ await msg(
+ update=message,
+ text=_("❌ Виджет не должен превышать 512 символов. Текущая длина: {length}").format(
+ length=len(new_widget)
+ ),
+ markup=settings_keyboard(),
+ state=state
+ )
+ return
+
+ await handle_set_widget(new_widget, message, state, bot)
+ return
+
+ # Вариант 2: Callback query или пустая команда → запускаем FSM
+ await status_clear(update=message, state=state)
+ text: str = _(
+ "📝 Смена виджета бота\n\n"
+ "Текущий виджет: {current}\n\n"
+ "Пожалуйста, введите новый виджет для бота (максимум 512 символов):"
+ ).format(current=current_widget)
+
+ await msg(update=message, text=text, markup=settings_keyboard(), state=state)
+ await state.set_state(SetWidgetForm.new_widget)
+
+
+@router.message(SetWidgetForm.new_widget, IsOwner())
+async def process_new_widget(
+ message: Message,
+ state: FSMContext,
+ bot: Bot
+) -> None:
+ """
+ Обрабатывает ввод нового текста виджета через FSM.
+ """
+ new_widget: str = message.text.strip()
+
+ # Проверяем, что пользователь что-то ввёл
+ if not new_widget:
+ await message.answer(_("❌ Пожалуйста, введите корректный виджет."))
+ return
+
+ await handle_set_widget(new_widget, message, state, bot)