From 2f14248c0b52dc168592379fc626bc88db29c083 Mon Sep 17 00:00:00 2001 From: penguinboi Date: Fri, 26 Dec 2025 14:56:24 -0500 Subject: [PATCH] Added missing decorator and registrer function for groups --- matrix/bot.py | 75 +++++++++++++++++++++++++++++++++++++++++-------- matrix/group.py | 22 +++++++++++++-- 2 files changed, 83 insertions(+), 14 deletions(-) diff --git a/matrix/bot.py b/matrix/bot.py index 2b1b6ff..bb1222d 100644 --- a/matrix/bot.py +++ b/matrix/bot.py @@ -33,6 +33,7 @@ from .scheduler import Scheduler from .errors import ( + GroupAlreadyRegisteredError, AlreadyRegisteredError, CommandNotFoundError, CheckError, @@ -192,7 +193,6 @@ def command( name: Optional[str] = None, *, description: Optional[str] = None, - prefix: Optional[str] = None, parent: Optional[str] = None, usage: Optional[str] = None, cooldown: Optional[tuple[int, float]] = None, @@ -203,16 +203,13 @@ def command( The command name defaults to the function name unless explicitly provided. - :param name: The name of the command. If omitted, the function name is used. - :param description: A brief description of the command. - :param prefix: The command prefix. If omitted, the bot's default prefix is used. - :param parent: The parent command name for subcommands. - :param usage: A usage string describing command arguments. - :param cooldown: A tuple defining (max_calls, per_seconds) for rate limiting. - :raises TypeError: If the decorated function is not a coroutine. - :raises ValueError: If a command with the same name is registered. - :return: Decorator that registers the command handler. - :rtype: Callback + ## Example + + ```python + @bot.command(description="Returns pong!") + async def ping(ctx): + await ctx.reply("Pong!") + ``` """ def wrapper(func: Callback) -> Command: @@ -220,7 +217,7 @@ def wrapper(func: Callback) -> Command: func, name=name, description=description, - prefix=prefix, + prefix=self.prefix, parent=parent, usage=usage, cooldown=cooldown, @@ -229,6 +226,52 @@ def wrapper(func: Callback) -> Command: return wrapper + def group( + self, + name: Optional[str] = None, + *, + description: Optional[str] = None, + parent: Optional[str] = None, + usage: Optional[str] = None, + cooldown: Optional[tuple[int, float]] = None, + ) -> GroupCallable: + """Decorator to register a coroutine function as a group handler. + + The group name defaults to the function name unless + explicitly provided. + + ## Example + + ```python + @bot.group(description="Group of mathematical commands") + async def math(ctx): + await ctx.reply("You called !math") + + + @math.command() + async def add(ctx, a: int, b: int): + await ctx.reply(f"{a} + {b} = {a + b}") + + + @math.command() + async def subtract(ctx, a: int, b: int): + await ctx.reply(f"{a} - {b} = {a - b}") + """ + + def wrapper(func: Callback) -> Group: + group = Group( + func, + name=name, + description=description, + prefix=self.prefix, + parent=parent, + usage=usage, + cooldown=cooldown, + ) + return self.register_group(group) + + return wrapper + def schedule(self, cron: str) -> Callable[..., Callback]: """ Decorator to register a coroutine function as a scheduled task. @@ -261,6 +304,14 @@ def register_command(self, cmd: Command) -> Command: return cmd + def register_group(self, group: Group) -> Group: + if group in self.commands: + raise GroupAlreadyRegisteredError(group) + + self.commands[group.name] = group + self.log.debug("group '%s' registered", group) + return group + def error(self, exception: Optional[type[Exception]] = None) -> Callable: """ Decorator to register a custom error handler for commands. diff --git a/matrix/group.py b/matrix/group.py index bf4ac32..3447878 100644 --- a/matrix/group.py +++ b/matrix/group.py @@ -14,10 +14,28 @@ class Group(Command): - def __init__(self, callback: Callback, **kwargs: Any): + def __init__( + self, + callback: Callback, + *, + name: Optional[str] = None, + description: Optional[str] = None, + prefix: Optional[str] = None, + parent: Optional[str] = None, + usage: Optional[str] = None, + cooldown: Optional[tuple[int, float]] = None, + ): self.commands: Dict[str, Command] = {} - super().__init__(callback, **kwargs) + super().__init__( + callback, + name=name, + description=description, + prefix=prefix, + parent=parent, + usage=usage, + cooldown=cooldown, + ) def _build_usage(self) -> str: return f"{self.prefix}{self.name} [subcommand]"