Skip to content

Command

A Command wraps an async callback together with its metadata (name, description, usage, cooldown, and checks). Commands are normally created via the @bot.command() decorator rather than instantiated directly.

from matrix import Bot, Context

bot = Bot()

@bot.command("greet", description="Greet a user by name", usage="<name>")
async def greet(ctx: Context, name: str):
    await ctx.reply(f"Hello, {name}!")

matrix.command.Command

Command(
    func,
    *,
    name=None,
    description=None,
    prefix=None,
    parent=None,
    usage=None,
    cooldown=None
)

Represents a command that can be executed with a context and arguments.

:param func: The coroutine that is executed when the command is invoked. :type func: Callable[..., Coroutine[Any, Any, Any]]

:param name: Optional name. Defaults to the function's name. :param description: Optional description of what the command does. :param prefix: Optional prefix for the command. :param parent: Optional parent command name for subcommands. :param usage: Optional usage string for the command. :param cooldown: Optional cooldown settings as a tuple of (rate, period).

:raises TypeError: If the provided name is not a string. :raises TypeError: If the provided callback is not a coroutine.

Source code in matrix/command.py
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
def __init__(
    self,
    func: 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,
):
    if name is not None and not isinstance(name, str):
        raise TypeError("Name must be a string.")

    self.name: str = name or func.__name__
    self.callback = func
    self.checks: List[Callback] = []

    self.description: str = description or ""
    self.prefix: str = prefix or ""
    self.parent: str = parent or ""
    self.usage: str = usage or self._build_usage()
    self.help: str = self._build_help()

    self._before_invoke_callback: Optional[Callback] = None
    self._after_invoke_callback: Optional[Callback] = None
    self._on_error: Optional[ErrorCallback] = None
    self._error_handlers: dict[type[Exception], ErrorCallback] = {}

    self.cooldown_rate: Optional[int] = None
    self.cooldown_period: Optional[float] = None
    self.cooldown_calls: DefaultDict[str, deque[float]] = defaultdict(deque)

    if cooldown:
        self.set_cooldown(*cooldown)

name instance-attribute

name = name or __name__

checks instance-attribute

checks = []

description instance-attribute

description = description or ''

prefix instance-attribute

prefix = prefix or ''

parent instance-attribute

parent = parent or ''

usage instance-attribute

usage = usage or _build_usage()

help instance-attribute

help = _build_help()

cooldown_rate instance-attribute

cooldown_rate = None

cooldown_period instance-attribute

cooldown_period = None

cooldown_calls instance-attribute

cooldown_calls = defaultdict(deque)

callback property writable

callback

Returns the coroutine function for this command.

:return: The command's coroutine function. :rtype: Callback

check

check(func)

Register a check callback

:param func: The check callback :type func: Callback

:raises TypeError: If the function is not a coroutine.

Source code in matrix/command.py
185
186
187
188
189
190
191
192
193
194
195
196
197
def check(self, func: Callback) -> None:
    """
    Register a check callback

    :param func: The check callback
    :type func: Callback

    :raises TypeError: If the function is not a coroutine.
    """
    if not inspect.iscoroutinefunction(func):
        raise TypeError("Checks must be coroutine")

    self.checks.append(func)

set_cooldown

set_cooldown(rate, period)
Source code in matrix/command.py
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
def set_cooldown(self, rate: int, period: float) -> None:
    self.cooldown_rate = rate
    self.cooldown_period = period

    async def cooldown_function(ctx: "Context") -> bool:
        if ctx is None or not hasattr(ctx, "sender"):
            return False

        if self.cooldown_period is None or self.cooldown_rate is None:
            return False

        now = monotonic()
        user_id = ctx.sender
        calls = self.cooldown_calls[user_id]

        while calls and now - calls[0] >= self.cooldown_period:
            calls.popleft()

        if len(calls) >= self.cooldown_rate:
            retry = self.cooldown_period - (now - calls[0])
            raise CooldownError(self, cooldown_function, retry)

        calls.append(now)
        return True

    self.checks.append(cooldown_function)

before_invoke

before_invoke(func)

Registers a coroutine to be called before the command is invoked.

:param func: The coroutine function to call before command invocation. :type func: Callback

:raises TypeError: If the function is not a coroutine.

Source code in matrix/command.py
226
227
228
229
230
231
232
233
234
235
236
237
238
239
def before_invoke(self, func: Callback) -> None:
    """
    Registers a coroutine to be called before the command is invoked.

    :param func: The coroutine function to call before command invocation.
    :type func: Callback

    :raises TypeError: If the function is not a coroutine.
    """

    if not inspect.iscoroutinefunction(func):
        raise TypeError("The hook must be a coroutine.")

    self._before_invoke_callback = func

after_invoke

after_invoke(func)

Registers a coroutine to be called after the command is invoked.

:param func: The coroutine function to call after command execution. :type func: Callback

:raises TypeError: If the function is not a coroutine.

Source code in matrix/command.py
241
242
243
244
245
246
247
248
249
250
251
252
253
254
def after_invoke(self, func: Callback) -> None:
    """
    Registers a coroutine to be called after the command is invoked.

    :param func: The coroutine function to call after command execution.
    :type func: Callback

    :raises TypeError: If the function is not a coroutine.
    """

    if not inspect.iscoroutinefunction(func):
        raise TypeError("The hook must be a coroutine.")

    self._after_invoke_callback = func

error

error(exception=None)

Decorator used to register an error handler for this command.

:param exception: Exception type to register the handler for. :type exception: Optional[Exception] :return: A decorator that registers the provided coroutine as an error handler and returns the original function. :rtype: Callable

Source code in matrix/command.py
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
def error(self, exception: Optional[type[Exception]] = None) -> Callable:
    """
    Decorator used to register an error handler for this command.

    :param exception: Exception type to register the handler for.
    :type exception: Optional[Exception]
    :return: A decorator that registers the provided coroutine as an
        error handler and returns the original function.
    :rtype: Callable
    """

    def wrapper(func: ErrorCallback) -> Callable:
        if not inspect.iscoroutinefunction(func):
            raise TypeError("The error handler must be a coroutine.")

        if exception:
            self._error_handlers[exception] = func
        else:
            self._on_error = func
        return func

    return wrapper

on_error async

on_error(ctx, error)

Executes the registered error handler if present.

:param ctx: The command execution context. :type ctx: Context :param error: The exception that was raised. :type error: Exception

Source code in matrix/command.py
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
async def on_error(self, ctx: "Context", error: Exception) -> None:
    """
    Executes the registered error handler if present.

    :param ctx: The command execution context.
    :type ctx: Context
    :param error: The exception that was raised.
    :type error: Exception
    """

    if handler := self._error_handlers.get(type(error)):
        await handler(ctx, error)
        return

    await ctx.bot.on_command_error(ctx, error)

    if self._on_error:
        await self._on_error(ctx, error)
    else:
        await ctx.send_help()

    ctx.logger.exception("error while executing command '%s'", self)

invoke async

invoke(ctx)
Source code in matrix/command.py
302
303
304
async def invoke(self, ctx: "Context") -> None:
    parsed_args = self._parse_arguments(ctx)
    await self.callback(ctx, *parsed_args)