custom emoji in inline buttons/ CLI 0.3.9/ updated dependencies/ other minor changes

This commit is contained in:
Vladless
2026-02-10 22:35:39 +03:00
parent 3f8db29405
commit cc57c8610b
12 changed files with 133 additions and 36 deletions
+55
View File
@@ -0,0 +1,55 @@
from __future__ import annotations
import aiogram.types
_OriginalInlineKeyboardButton = aiogram.types.InlineKeyboardButton
_button_icon_config: dict[str, dict[str, str]] = {}
def apply_button_icons_patch(config: dict[str, dict[str, str]] | None = None) -> None:
"""
Патчит InlineKeyboardButton, добавляя поддержку глобального конфига для иконок и стилей кнопок по callback_data или url.
"""
if config is not None:
_button_icon_config.clear()
_button_icon_config.update(config)
class _PatchedInlineKeyboardButton(_OriginalInlineKeyboardButton):
def __init__(self, **kwargs: object):
key = kwargs.get("callback_data") or kwargs.get("url")
if key is not None and isinstance(key, str) and key in _button_icon_config:
kwargs = {**kwargs, **_button_icon_config[key]}
super().__init__(**kwargs)
aiogram.types.InlineKeyboardButton = _PatchedInlineKeyboardButton
def set_button_icon_config(config: dict[str, dict[str, str]]) -> None:
"""Подставить конфиг кнопок (вызвать после загрузки handlers, из handlers.buttons.BUTTON_ICON_CONFIG)."""
_button_icon_config.clear()
_button_icon_config.update(config)
def inline_button(
text: str,
callback_data: str | None = None,
url: str | None = None,
web_app: object | None = None,
*,
icon_custom_emoji_id: str | None = None,
style: str | None = None,
) -> _OriginalInlineKeyboardButton:
"""Собирает InlineKeyboardButton с опциональной иконкой (custom emoji) и стилем."""
kwargs: dict = {"text": text}
if callback_data is not None:
kwargs["callback_data"] = callback_data
if url is not None:
kwargs["url"] = url
if web_app is not None:
kwargs["web_app"] = web_app
if icon_custom_emoji_id is not None:
kwargs["icon_custom_emoji_id"] = icon_custom_emoji_id
if style is not None:
kwargs["style"] = style
return aiogram.types.InlineKeyboardButton(**kwargs)
+10
View File
@@ -12,12 +12,18 @@ from .modules_manager import manager
modules_hub = Router(name="modules_hub")
def _is_safe_module_name(name: str) -> bool:
return bool(name and name.isidentifier() and "." not in name and "/" not in name and "\\" not in name)
def load_modules_from_folder(folder: str = "modules") -> list[Router]:
routers = []
base_path = Path(folder)
for _finder, name, _ispkg in pkgutil.iter_modules([str(base_path)]):
if not _is_safe_module_name(name):
logger.warning(f"[Modules] Пропуск недопустимого имени модуля: {name!r}")
continue
if not manager.should_autostart(name):
logger.info(f"[Modules] Пропуск автозапуска модуля '{name}' (отключён).")
continue
@@ -43,6 +49,8 @@ def load_module_webhooks(folder: str = "modules") -> list[dict]:
base_path = Path(folder)
for _finder, name, _ispkg in pkgutil.iter_modules([str(base_path)]):
if not _is_safe_module_name(name):
continue
if not manager.should_autostart(name):
logger.info(f"[Modules] Пропуск вебхуков модуля '{name}' (отключён).")
continue
@@ -65,6 +73,8 @@ def load_module_fast_flow_handlers(folder: str = "modules") -> dict:
base_path = Path(folder)
for _finder, name, _ispkg in pkgutil.iter_modules([str(base_path)]):
if not _is_safe_module_name(name):
continue
if not manager.should_autostart(name):
logger.info(f"[Modules] Пропуск fast-flow модуля '{name}' (отключён).")
continue
+5
View File
@@ -57,7 +57,12 @@ class ModulesManager:
rec.enabled = True
self.registry[name] = rec
def _is_safe_module_name(self, name: str) -> bool:
return bool(name and name.isidentifier() and "." not in name and "/" not in name and "\\" not in name)
async def start(self, name: str) -> None:
if not self._is_safe_module_name(name):
raise ValueError(f"[Modules] Недопустимое имя модуля: {name!r}")
rec = self.registry.get(name) or ModuleRecord(name, self.pkg(name))
if rec.enabled:
logger.info(f"[Modules] {name} уже активен.")