> ## Documentation Index
> Fetch the complete documentation index at: https://docs.confess.best/llms.txt
> Use this file to discover all available pages before exploring further.

# alterself Commands: Definitions, Arguments, and Checks

> Define prefix commands, parse arguments, use built-in and custom checks for access control, and organise handlers using the alterself command system.

alterself processes only messages that you send yourself. It checks that `message.author.id == client.me.id` before attempting to match any prefix or command. This means your commands are entirely private; other users cannot trigger them. The command system handles prefix matching, argument tokenisation, check evaluation, and invocation for you so you can focus on writing the handler logic.

## Defining Commands

You can register a command in two ways: with the `@bot.command()` decorator bound to your client instance, or with the standalone `@alterself.cmd()` decorator that works inside [Cogs](/concepts/cogs).

<CodeGroup>
  ```python @bot.command() decorator theme={null}
  import alterself

  bot = alterself.Client(token="...", prefix="!")

  @bot.command(name="greet", aliases=["hello", "hi"], brief="Greet someone")
  async def greet(ctx, name="World", greeting="Hello"):
      await ctx.reply(f"{greeting}, {name}!")
  ```

  ```python @alterself.cmd() decorator (for Cogs) theme={null}
  import alterself

  class GreetCog(alterself.Cog):

      @alterself.cmd(name="greet", aliases=["hello", "hi"], brief="Greet someone")
      async def greet(self, ctx, name="World", greeting="Hello"):
          await ctx.reply(f"{greeting}, {name}!")
  ```
</CodeGroup>

Both decorators accept the same parameters:

| Parameter | Type | Default | Description |
| - | - | - | - |
| `name` | `str` | function name | The primary name used to invoke the command |
| `aliases` | `list[str]` | `[]` | Alternative names that trigger the same handler |
| `brief` | `str` | `""` | Short one-line description shown in help output |

## Arguments

alterself splits the text after the command name on whitespace and passes each token as a positional argument to your handler function. Default parameter values are used when the caller omits an argument.

```python theme={null}
@bot.command(name="greet")
async def greet(ctx, name="World", greeting="Hello"):
    await ctx.reply(f"{greeting}, {name}!")
```

Given the prefix `"."`, the following inputs produce these results:

```text theme={null}
.greet alice           → "Hello, alice!"
.greet alice Goodbye   → "Goodbye, alice!"
.greet                 → "Hello, World!"
```

<Tip>
  To capture everything after the command name as a single string, including spaces, annotate the last parameter with `*` or use a greedy string converter. This is useful for commands that take a sentence or a reason as input.
</Tip>

<Note>
  Arguments are always received as strings. Convert them to `int`, `float`, or other types inside your handler, and handle `ValueError` gracefully if the input might be invalid.
</Note>

## The Context Object

Every command handler receives a `Context` object as its first argument (named `ctx` by convention). It bundles together everything you need to understand and respond to the invocation.

| Attribute / Method | Type | Description |
| - | - | - |
| `ctx.client` | `Client` | The bot instance |
| `ctx.message` | `Message` | The triggering message |
| `ctx.author` | `User` | Message author |
| `ctx.channel` | `Channel \| None` | Channel object, if cached |
| `ctx.guild` | `Guild \| None` | Guild object; `None` in DMs |
| `ctx.channel_id` | `int` | Channel ID (always present) |
| `ctx.guild_id` | `int \| None` | Guild ID; `None` in DMs |
| `ctx.args` | `list[str]` | Parsed positional arguments |
| `await ctx.send(content)` | `Message` | Send a new message to the same channel |
| `await ctx.reply(content)` | `Message` | Reply to the triggering message |
| `await ctx.edit(content)` | `Message` | Edit the triggering message in place |
| `await ctx.delete()` | `None` | Delete the triggering message |
| `await ctx.typing()` | `None` | Trigger the typing indicator in the channel |

A typical handler uses several of these together:

```python theme={null}
@bot.command(name="ping")
async def ping(ctx):
    await ctx.typing()
    await ctx.reply(f"Pong! Latency: {ctx.client.latency * 1000:.1f} ms")
```

## Checks

Checks are predicate functions that run before your handler. If any check fails, the command is silently skipped (or raises a `CheckFailed` you can handle with an error handler). alterself ships with three built-in checks:

<CodeGroup>
  ```python owner_only theme={null}
  from alterself.commands.checks import owner_only
  import alterself

  @bot.command(name="shutdown")
  @alterself.check(owner_only)
  async def shutdown(ctx):
      await ctx.reply("Shutting down…")
      await ctx.client.close()
  ```

  ```python guild_only theme={null}
  from alterself.commands.checks import guild_only
  import alterself

  @bot.command(name="serverinfo")
  @alterself.check(guild_only)
  async def serverinfo(ctx):
      await ctx.reply(f"Guild: {ctx.guild.name}")
  ```

  ```python dm_only theme={null}
  from alterself.commands.checks import dm_only
  import alterself

  @bot.command(name="secret")
  @alterself.check(dm_only)
  async def secret(ctx):
      await ctx.reply("This only works in DMs.")
  ```
</CodeGroup>

You can also write a custom check. A check is any callable that accepts a `Context` and returns a `bool`:

```python theme={null}
import alterself

def is_long_member(ctx):
    """Only allow users who have been in the guild for over 30 days."""
    if not ctx.guild:
        return False
    member = ctx.guild.get_member(ctx.author.id)
    if member is None:
        return False
    import datetime
    age = datetime.datetime.utcnow() - member.joined_at
    return age.days >= 30

@bot.command(name="veteran")
@alterself.check(is_long_member)
async def veteran(ctx):
    await ctx.reply("Welcome, veteran member!")
```

<Note>
  Checks are evaluated in the order they are stacked. Stack multiple `@alterself.check()` decorators to require all conditions to pass simultaneously.
</Note>

## Programmatic Registration

You can add, remove, and look up commands at runtime without using decorators:

```python theme={null}
async def ping(ctx):
    await ctx.reply("Pong!")

# Wrap a function in a Command object and register it
bot.add_command(alterself.Command(ping, name="ping", brief="Check latency"))

# Remove a command by name
bot.remove_command("ping")

# Retrieve a command object by name (returns None if not found)
cmd = bot.get_command("ping")
if cmd:
    print(cmd.name, cmd.brief)
```

<Tip>
  Programmatic registration is useful for loading commands conditionally based on configuration flags, or for writing plugin systems that add and remove commands without touching your main file.
</Tip>
