Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 63 additions & 12 deletions matrix/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from .scheduler import Scheduler

from .errors import (
GroupAlreadyRegisteredError,
AlreadyRegisteredError,
CommandNotFoundError,
CheckError,
Expand Down Expand Up @@ -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,
Expand All @@ -203,24 +203,21 @@ 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:
cmd = Command(
func,
name=name,
description=description,
prefix=prefix,
prefix=self.prefix,
parent=parent,
usage=usage,
cooldown=cooldown,
Expand All @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
22 changes: 20 additions & 2 deletions matrix/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]"
Expand Down