First commit
This commit is contained in:
19
bot/handlers/commands/settings/__init__.py
Normal file
19
bot/handlers/commands/settings/__init__.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from aiogram import Router
|
||||
|
||||
from .set_description_cmd import router as set_description_cmd_router
|
||||
from .set_name_cmd import router as set_name_cmd_router
|
||||
from .set_widget_cmd import router as set_widget_cmd_router
|
||||
from .settings_cmd import router as settings_cmd_router
|
||||
|
||||
# Настройка экспорта и роутера
|
||||
__all__ = ("router",)
|
||||
router: Router = Router(name=__name__)
|
||||
|
||||
|
||||
# Подключение роутеров
|
||||
router.include_routers(
|
||||
settings_cmd_router,
|
||||
set_name_cmd_router,
|
||||
set_description_cmd_router,
|
||||
set_widget_cmd_router,
|
||||
)
|
||||
167
bot/handlers/commands/settings/set_description_cmd.py
Normal file
167
bot/handlers/commands/settings/set_description_cmd.py
Normal file
@@ -0,0 +1,167 @@
|
||||
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_description".lower()
|
||||
|
||||
# Роутер для обработки команды /set_description
|
||||
router: Router = Router(name=f"{CMD}_cmd_router")
|
||||
|
||||
|
||||
class SetBotDescriptionForm(StatesGroup):
|
||||
"""Состояния FSM для изменения короткого описания бота."""
|
||||
new_description: State = State()
|
||||
|
||||
|
||||
async def handle_set_bot_description(
|
||||
description: str,
|
||||
message: Message | CallbackQuery,
|
||||
state: FSMContext,
|
||||
bot: Bot
|
||||
) -> None:
|
||||
"""
|
||||
Установка короткого описания (short description) бота с обработкой FSM и ошибок API.
|
||||
|
||||
Args:
|
||||
description (str): Новый текст описания (до 120 символов).
|
||||
message (Message | CallbackQuery): Сообщение или callback-запрос.
|
||||
state (FSMContext): Контекст FSM.
|
||||
bot (Bot): Экземпляр бота.
|
||||
"""
|
||||
# Проверка ограничения Telegram
|
||||
if len(description) > 120:
|
||||
await msg(
|
||||
message=message,
|
||||
text=_("❌ Короткое описание бота должно быть не более 120 символов. Текущая длина: {length}").format(
|
||||
length=len(description)
|
||||
),
|
||||
markup=settings_keyboard(),
|
||||
)
|
||||
return
|
||||
|
||||
try:
|
||||
# Установка нового короткого описания
|
||||
await bot.set_my_short_description(short_description=description)
|
||||
|
||||
# Сохраняем текущее значение в BotInfo
|
||||
BotInfo.short_description = description
|
||||
|
||||
# Сбрасываем состояние FSM
|
||||
await state.clear()
|
||||
|
||||
# Отправляем сообщение об успехе
|
||||
await msg(
|
||||
message=message,
|
||||
text=_("✅ Короткое описание бота успешно изменено на: <b>{description}</b>").format(
|
||||
description=description
|
||||
),
|
||||
markup=settings_keyboard(),
|
||||
)
|
||||
|
||||
logger.info(f"Короткое описание бота изменено на: {description}")
|
||||
|
||||
except TelegramRetryAfter as e:
|
||||
retry_text: str = format_retry_time(e.retry_after)
|
||||
logger.warning(f"Превышен лимит запросов при смене short description. Попробуйте через {retry_text}")
|
||||
await msg(
|
||||
message=message,
|
||||
text=_("⚠️ Слишком частая смена короткого описания!\nПопробуйте снова через: <b>{retry_text}</b>").format(
|
||||
retry_text=retry_text
|
||||
),
|
||||
markup=settings_keyboard(),
|
||||
)
|
||||
|
||||
except TelegramAPIError as e:
|
||||
logger.error(f"Ошибка Telegram API при изменении короткого описания: {e}")
|
||||
await msg(
|
||||
message=message,
|
||||
text=_("❌ Ошибка Telegram API при изменении короткого описания: <pre>{error}</pre>").format(error=str(e)),
|
||||
markup=settings_keyboard(),
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Непредвиденная ошибка при изменении короткого описания: {e}")
|
||||
await msg(
|
||||
message=message,
|
||||
text=_("❌ Непредвиденная ошибка при изменении короткого описания: <pre>{error}</pre>").format(error=str(e)),
|
||||
markup=settings_keyboard(),
|
||||
)
|
||||
|
||||
|
||||
@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_description для короткого описания.
|
||||
|
||||
Поддерживает:
|
||||
1. Немедленное изменение через аргумент (/set_description TEXT).
|
||||
2. Callback-запрос.
|
||||
3. FSM-ввод.
|
||||
"""
|
||||
current_description: str = BotInfo.description
|
||||
|
||||
# Вариант 1: если пользователь передал аргумент к команде
|
||||
if command and command.args:
|
||||
description: str = command.args.strip()
|
||||
if len(description) > 120:
|
||||
await msg(
|
||||
message=message,
|
||||
text=_("❌ Короткое описание не должно превышать 120 символов. Текущая длина: {length}").format(
|
||||
length=len(description)
|
||||
),
|
||||
markup=settings_keyboard(),
|
||||
)
|
||||
return
|
||||
|
||||
await handle_set_bot_description(description, message, state, bot)
|
||||
return
|
||||
|
||||
# Вариант 2: без аргумента → включаем FSM
|
||||
await status_clear(message=message, state=state)
|
||||
text: str = _(
|
||||
"📝 <b>Смена короткого описания бота</b>\n\n"
|
||||
"Текущее короткое описание: <i>{current}</i>\n\n"
|
||||
"Введите новое короткое описание (максимум 120 символов):"
|
||||
).format(current=current_description)
|
||||
|
||||
await msg(message=message, text=text, markup=settings_keyboard())
|
||||
await state.set_state(SetBotDescriptionForm.new_description)
|
||||
|
||||
|
||||
@router.message(SetBotDescriptionForm.new_description, IsOwner())
|
||||
async def process_new_bot_description(
|
||||
message: Message,
|
||||
state: FSMContext,
|
||||
bot: Bot
|
||||
) -> None:
|
||||
"""
|
||||
Обработка ввода нового короткого описания через FSM.
|
||||
"""
|
||||
description: str = message.text.strip()
|
||||
|
||||
if not description:
|
||||
await message.answer(_("❌ Пожалуйста, введите корректное короткое описание."))
|
||||
return
|
||||
|
||||
await handle_set_bot_description(description, message, state, bot)
|
||||
151
bot/handlers/commands/settings/set_name_cmd.py
Normal file
151
bot/handlers/commands/settings/set_name_cmd.py
Normal file
@@ -0,0 +1,151 @@
|
||||
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 configs import COMMANDS
|
||||
from middleware.loggers import logger
|
||||
|
||||
__all__ = ("router",)
|
||||
CMD: str = "set_name".lower()
|
||||
router: Router = Router(name=f"{CMD}_cmd_router")
|
||||
|
||||
|
||||
class SetNameForm(StatesGroup):
|
||||
new_name: State = State()
|
||||
|
||||
|
||||
def format_retry_time(retry_after: int) -> str:
|
||||
"""Форматирование времени повторной попытки в читаемом виде"""
|
||||
hours, remainder = divmod(retry_after, 3600)
|
||||
minutes, seconds = divmod(remainder, 60)
|
||||
|
||||
if hours > 0:
|
||||
return f"{hours} часов, {minutes} минут, {seconds} секунд"
|
||||
elif minutes > 0:
|
||||
return f"{minutes} минут, {seconds} секунд"
|
||||
else:
|
||||
return f"{seconds} секунд"
|
||||
|
||||
|
||||
async def handle_set_name(
|
||||
new_name: str,
|
||||
message: Message | CallbackQuery,
|
||||
state: FSMContext,
|
||||
bot: Bot
|
||||
) -> None:
|
||||
"""
|
||||
Установка имени бота с проверкой длины, обработкой перегрузки и логированием
|
||||
"""
|
||||
if len(new_name) > 64:
|
||||
await msg(
|
||||
message=message,
|
||||
text=_("❌ Имя бота должно быть не более 64 символов. Текущая длина: {length}").format(
|
||||
length=len(new_name)
|
||||
),
|
||||
markup=settings_keyboard(),
|
||||
)
|
||||
return
|
||||
|
||||
try:
|
||||
await bot.set_my_name(new_name)
|
||||
BotInfo.first_name = new_name
|
||||
await state.clear()
|
||||
await msg(
|
||||
message=message,
|
||||
text=_("✅ Имя бота успешно изменено на: <b>{new_name}</b>").format(new_name=new_name),
|
||||
markup=settings_keyboard(),
|
||||
)
|
||||
logger.info(f"Имя бота изменено на: {new_name}")
|
||||
|
||||
except TelegramRetryAfter as e:
|
||||
retry_text: str = format_retry_time(e.retry_after)
|
||||
logger.warning(f"Превышен контроль перегрузки при смене имени. Попробуйте через {retry_text}")
|
||||
await msg(
|
||||
message=message,
|
||||
text=_("⚠️ Слишком частая смена имени!\nПопробуйте снова через: <b>{retry_text}</b>").format(
|
||||
retry_text=retry_text
|
||||
),
|
||||
markup=settings_keyboard(),
|
||||
)
|
||||
|
||||
except TelegramAPIError as e:
|
||||
logger.error(f"Ошибка Telegram API при изменении имени: {e}")
|
||||
await msg(
|
||||
message=message,
|
||||
text=_("❌ Ошибка Telegram API: <pre>{error}</pre>").format(error=str(e)),
|
||||
markup=settings_keyboard(),
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Непредвиденная ошибка при изменении имени: {e}")
|
||||
await msg(
|
||||
message=message,
|
||||
text=_("❌ Непредвиденная ошибка: <pre>{error}</pre>").format(error=str(e)),
|
||||
markup=settings_keyboard(),
|
||||
)
|
||||
|
||||
|
||||
@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
|
||||
):
|
||||
"""
|
||||
Обработчик команды /set_name с поддержкой:
|
||||
1. Immediate установки через аргумент команды
|
||||
2. Callback query
|
||||
3. FSM ввод
|
||||
"""
|
||||
current_name = getattr(BotInfo, "first_name", "") or _("Не установлено")
|
||||
|
||||
# Immediate установка через аргумент команды
|
||||
if command and command.args:
|
||||
new_name = command.args.strip()
|
||||
if len(new_name) > 64:
|
||||
await msg(
|
||||
message=message,
|
||||
text=_("❌ Имя не должно превышать 64 символа. Текущая длина: {length}").format(
|
||||
length=len(new_name)
|
||||
),
|
||||
markup=settings_keyboard(),
|
||||
)
|
||||
return
|
||||
await handle_set_name(new_name, message, state, bot)
|
||||
return
|
||||
|
||||
# Для callback query или пустой команды — показываем текущее имя и запускаем FSM
|
||||
await state.clear()
|
||||
if isinstance(message, CallbackQuery):
|
||||
await message.answer()
|
||||
text: str = _(
|
||||
"🤖 <b>Смена имени бота</b>\n\n"
|
||||
"Текущее имя: <i>{current}</i>\n\n"
|
||||
"Пожалуйста, введите новое имя для бота (максимум 64 символа):"
|
||||
).format(current=current_name)
|
||||
await msg(message=message, text=text, markup=settings_keyboard())
|
||||
await state.set_state(SetNameForm.new_name)
|
||||
|
||||
|
||||
@router.message(SetNameForm.new_name, IsOwner())
|
||||
async def process_new_name(message: Message, state: FSMContext, bot: Bot):
|
||||
"""
|
||||
Обработка ввода нового имени через FSM
|
||||
"""
|
||||
new_name: str = message.text.strip()
|
||||
|
||||
if not new_name:
|
||||
await message.answer(_("❌ Пожалуйста, введите корректное имя."))
|
||||
return
|
||||
|
||||
await handle_set_name(new_name, message, state, bot)
|
||||
168
bot/handlers/commands/settings/set_widget_cmd.py
Normal file
168
bot/handlers/commands/settings/set_widget_cmd.py
Normal file
@@ -0,0 +1,168 @@
|
||||
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(
|
||||
message=message,
|
||||
text=_("❌ Виджет бота должен быть не более 512 символов. Текущая длина: {length}").format(
|
||||
length=len(new_widget)
|
||||
),
|
||||
markup=settings_keyboard(),
|
||||
)
|
||||
return
|
||||
|
||||
try:
|
||||
# Устанавливаем описание через Telegram API
|
||||
await bot.set_my_description(description=new_widget)
|
||||
|
||||
# Сохраняем в BotInfo для локального использования
|
||||
BotInfo.widget = new_widget
|
||||
|
||||
# Очищаем состояние FSM
|
||||
await state.clear()
|
||||
|
||||
# Отправляем уведомление пользователю
|
||||
await msg(
|
||||
message=message,
|
||||
text=_("✅ Виджет бота успешно изменён на: <b>{new_widget}</b>").format(
|
||||
new_widget=new_widget
|
||||
),
|
||||
markup=settings_keyboard(),
|
||||
)
|
||||
|
||||
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(
|
||||
message=message,
|
||||
text=_("⚠️ Слишком частая смена виджета!\nПопробуйте снова через: <b>{retry_text}</b>").format(
|
||||
retry_text=retry_text
|
||||
),
|
||||
markup=settings_keyboard(),
|
||||
)
|
||||
|
||||
except TelegramAPIError as e:
|
||||
# Ошибка Telegram API
|
||||
logger.error(f"Ошибка Telegram API при изменении виджета: {e}")
|
||||
await msg(
|
||||
message=message,
|
||||
text=_("❌ Ошибка Telegram API при изменении виджета: <pre>{error}</pre>").format(error=str(e)),
|
||||
markup=settings_keyboard(),
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
# Непредвиденная ошибка
|
||||
logger.error(f"Непредвиденная ошибка при изменении виджета: {e}")
|
||||
await msg(
|
||||
message=message,
|
||||
text=_("❌ Непредвиденная ошибка при изменении виджета: <pre>{error}</pre>").format(error=str(e)),
|
||||
markup=settings_keyboard(),
|
||||
)
|
||||
|
||||
|
||||
@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.widget
|
||||
|
||||
# Вариант 1: пользователь ввёл аргумент сразу (/set_widget TEXT)
|
||||
if command and command.args:
|
||||
new_widget: str = command.args.strip()
|
||||
if len(new_widget) > 512:
|
||||
await msg(
|
||||
message=message,
|
||||
text=_("❌ Виджет не должен превышать 512 символов. Текущая длина: {length}").format(
|
||||
length=len(new_widget)
|
||||
),
|
||||
markup=settings_keyboard(),
|
||||
)
|
||||
return
|
||||
|
||||
await handle_set_widget(new_widget, message, state, bot)
|
||||
return
|
||||
|
||||
# Вариант 2: Callback query или пустая команда → запускаем FSM
|
||||
await status_clear(message=message, state=state)
|
||||
text: str = _(
|
||||
"📝 <b>Смена виджета бота</b>\n\n"
|
||||
"Текущий виджет: <i>{current}</i>\n\n"
|
||||
"Пожалуйста, введите новый виджет для бота (максимум 512 символов):"
|
||||
).format(current=current_widget)
|
||||
|
||||
await msg(message=message, text=text, markup=settings_keyboard())
|
||||
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)
|
||||
48
bot/handlers/commands/settings/settings_cmd.py
Normal file
48
bot/handlers/commands/settings/settings_cmd.py
Normal file
@@ -0,0 +1,48 @@
|
||||
from aiogram import Router, F
|
||||
from aiogram.filters import Command
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.types import Message, CallbackQuery, InlineKeyboardButton
|
||||
from aiogram.utils.i18n import gettext as _
|
||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||
|
||||
from bot.core.bots import BotInfo
|
||||
from bot.filters import IsOwner
|
||||
from bot.templates import msg
|
||||
from bot.utils import status_clear
|
||||
from configs import COMMANDS
|
||||
|
||||
# Настройки экспорта и роутера
|
||||
__all__ = ("router", "settings_keyboard",)
|
||||
CMD: str = "settings".lower()
|
||||
router: Router = Router(name=f"{CMD}_cmd_router")
|
||||
|
||||
|
||||
def settings_keyboard() -> InlineKeyboardBuilder:
|
||||
"""Клавиатура настроек"""
|
||||
ikb: InlineKeyboardBuilder = InlineKeyboardBuilder()
|
||||
ikb.row(InlineKeyboardButton(text="🔙 Вернуться", callback_data="settings"))
|
||||
return ikb
|
||||
|
||||
|
||||
@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) -> None:
|
||||
"""Обработчик команды /settings"""
|
||||
await status_clear(message=message, state=state)
|
||||
|
||||
# Создание инлайн-клавиатуры
|
||||
ikb: InlineKeyboardBuilder = InlineKeyboardBuilder()
|
||||
ikb.row(InlineKeyboardButton(text="Имя бота⚜️", callback_data='set_name'))
|
||||
ikb.row(InlineKeyboardButton(text="Описание бота📝", callback_data='set_description'))
|
||||
ikb.row(InlineKeyboardButton(text="Виджет🧩", callback_data='set_widget'))
|
||||
ikb.row(InlineKeyboardButton(text="Назад◀️", callback_data='menu'))
|
||||
|
||||
# Формируем приветственное сообщение
|
||||
text: str = _("""
|
||||
⚙️ Настройки
|
||||
"""
|
||||
).format(
|
||||
)
|
||||
|
||||
# Отправляем сообщение
|
||||
await msg(message=message, text=text, markup=ikb)
|
||||
Reference in New Issue
Block a user