diff --git a/aiogram/utils/help/__init__.py b/aiogram/utils/help/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/aiogram/utils/help/engine.py b/aiogram/utils/help/engine.py deleted file mode 100644 index c91c8899..00000000 --- a/aiogram/utils/help/engine.py +++ /dev/null @@ -1,48 +0,0 @@ -from abc import ABC, abstractmethod -from collections import Generator -from typing import Dict, List - -from aiogram.utils.help.record import CommandRecord - - -class BaseHelpBackend(ABC): - @abstractmethod - def add(self, record: CommandRecord) -> None: - pass - - @abstractmethod - def search(self, value: str) -> CommandRecord: - pass - - @abstractmethod - def all(self) -> Generator[CommandRecord, None, None]: - pass - - def __getitem__(self, item: str) -> CommandRecord: - return self.search(item) - - def __iter__(self) -> Generator[CommandRecord, None, None]: - return self.all() - - -class MappingBackend(BaseHelpBackend): - def __init__(self, search_empty_prefix: bool = True) -> None: - self._records: List[CommandRecord] = [] - self._mapping: Dict[str, CommandRecord] = {} - self.search_empty_prefix = search_empty_prefix - - def search(self, value: str) -> CommandRecord: - return self._mapping[value] - - def add(self, record: CommandRecord) -> None: - new_records = {} - for key in record.as_keys(with_empty_prefix=self.search_empty_prefix): - if key in self._mapping: - raise ValueError(f"Key '{key}' is already indexed") - new_records[key] = record - self._mapping.update(new_records) - self._records.append(record) - self._records.sort(key=lambda rec: (rec.priority, rec.commands[0])) - - def all(self) -> Generator[CommandRecord, None, None]: - yield from self._records diff --git a/aiogram/utils/help/manager.py b/aiogram/utils/help/manager.py deleted file mode 100644 index b9aefb0e..00000000 --- a/aiogram/utils/help/manager.py +++ /dev/null @@ -1,113 +0,0 @@ -from typing import Any, Optional, Tuple - -from aiogram import Bot, Router -from aiogram.dispatcher.filters import Command, CommandObject -from aiogram.types import BotCommand, Message -from aiogram.utils.help.engine import BaseHelpBackend, MappingBackend -from aiogram.utils.help.record import DEFAULT_PREFIXES, CommandRecord -from aiogram.utils.help.render import BaseHelpRenderer, SimpleRenderer - - -class HelpManager: - def __init__( - self, - backend: Optional[BaseHelpBackend] = None, - renderer: Optional[BaseHelpRenderer] = None, - ) -> None: - if backend is None: - backend = MappingBackend() - if renderer is None: - renderer = SimpleRenderer() - self._backend = backend - self._renderer = renderer - - def add( - self, - *commands: str, - help: str, - description: Optional[str] = None, - prefix: str = DEFAULT_PREFIXES, - ignore_case: bool = False, - ignore_mention: bool = False, - priority: int = 0, - ) -> CommandRecord: - record = CommandRecord( - commands=commands, - help=help, - description=description, - prefix=prefix, - ignore_case=ignore_case, - ignore_mention=ignore_mention, - priority=priority, - ) - self._backend.add(record) - return record - - def command( - self, - *commands: str, - help: str, - description: Optional[str] = None, - prefix: str = DEFAULT_PREFIXES, - ignore_case: bool = False, - ignore_mention: bool = False, - priority: int = 0, - ) -> Command: - record = self.add( - *commands, - help=help, - description=description, - prefix=prefix, - ignore_case=ignore_case, - ignore_mention=ignore_mention, - priority=priority, - ) - return record.as_filter() - - def mount_help( - self, - router: Router, - *commands: str, - prefix: str = "/", - help: str = "Help", - description: str = "Show help for the commands\n" - "Also you can use '/help command' for get help for specific command", - as_reply: bool = True, - filters: Tuple[Any, ...] = (), - **kw_filters: Any, - ) -> Any: - if not commands: - commands = ("help",) - help_filter = self.command(*commands, prefix=prefix, help=help, description=description) - - async def handle(message: Message, command: CommandObject, **kwargs: Any) -> Any: - return await self._handle_help( - message=message, command=command, as_reply=as_reply, **kwargs - ) - - return router.message.register(handle, help_filter, *filters, **kw_filters) - - async def _handle_help( - self, - message: Message, - bot: Bot, - command: CommandObject, - as_reply: bool = True, - **kwargs: Any, - ) -> Any: - lines = self._renderer.render(backend=self._backend, command=command, **kwargs) - text = "\n".join(line or "" for line in lines) - return await bot.send_message( - chat_id=message.chat.id, - text=text, - reply_to_message_id=message.message_id if as_reply else None, - ) - - async def set_bot_commands(self, bot: Bot) -> bool: - return await bot.set_my_commands( - commands=[ - BotCommand(command=record.commands[0], description=record.help) - for record in self._backend - if "/" in record.prefix - ] - ) diff --git a/aiogram/utils/help/record.py b/aiogram/utils/help/record.py deleted file mode 100644 index beb00468..00000000 --- a/aiogram/utils/help/record.py +++ /dev/null @@ -1,33 +0,0 @@ -from dataclasses import dataclass -from itertools import product -from typing import Generator, Optional, Sequence - -from aiogram.dispatcher.filters import Command - -DEFAULT_PREFIXES = "/" - - -@dataclass -class CommandRecord: - commands: Sequence[str] - help: str - description: Optional[str] = None - prefix: str = DEFAULT_PREFIXES - ignore_case: bool = False - ignore_mention: bool = False - priority: int = 0 - - def as_filter(self) -> Command: - return Command(commands=self.commands, commands_prefix=self.prefix) - - def as_keys(self, with_empty_prefix: bool = False) -> Generator[str, None, None]: - for command in self.commands: - yield command - for prefix in self.prefix: - yield f"{prefix}{command}" - - def as_command(self) -> str: - return f"{self.prefix[0]}{self.commands[0]}" - - def as_aliases(self) -> str: - return ", ".join(f"{p}{c}" for c, p in product(self.commands, self.prefix)) diff --git a/aiogram/utils/help/render.py b/aiogram/utils/help/render.py deleted file mode 100644 index 8b15d0af..00000000 --- a/aiogram/utils/help/render.py +++ /dev/null @@ -1,64 +0,0 @@ -from abc import ABC, abstractmethod -from typing import Any, Generator, Optional - -from aiogram.dispatcher.filters import CommandObject -from aiogram.utils.help.engine import BaseHelpBackend - - -class BaseHelpRenderer(ABC): - @abstractmethod - def render( - self, backend: BaseHelpBackend, command: CommandObject, **kwargs: Any - ) -> Generator[Optional[str], None, None]: - pass - - -class SimpleRenderer(BaseHelpRenderer): - def __init__( - self, - help_title: str = "Commands list:", - help_footer: str = "", - aliases_line: str = "Aliases", - command_title: str = "Help for command:", - unknown_command: str = "Command not found", - ): - self.help_title = help_title - self.help_footer = help_footer - self.aliases_line = aliases_line - self.command_title = command_title - self.unknown_command = unknown_command - - def render_help(self, backend: BaseHelpBackend) -> Generator[Optional[str], None, None]: - yield self.help_title - - for command in backend: - yield f"{command.prefix[0]}{command.commands[0]} - {command.help}" - - if self.help_footer: - yield None - yield self.help_footer - - def render_command_help( - self, backend: BaseHelpBackend, target: str - ) -> Generator[Optional[str], None, None]: - try: - record = backend[target] - except KeyError: - yield f"{self.command_title} {target}" - yield self.unknown_command - return - - yield f"{self.command_title} {record.as_command()}" - if len(record.commands) > 1 or len(record.prefix) > 1: - yield f"{self.aliases_line}: {record.as_aliases()}" - yield record.help - yield None - yield record.description - - def render( - self, backend: BaseHelpBackend, command: CommandObject, **kwargs: Any - ) -> Generator[Optional[str], None, None]: - if command.args: - yield from self.render_command_help(backend=backend, target=command.args) - else: - yield from self.render_help(backend=backend)