First commit

This commit is contained in:
2026-01-23 04:45:55 +07:00
commit 0b251c5967
118 changed files with 9580 additions and 0 deletions

View 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,
)

View 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)

View 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)

View 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)

View 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)