Commands

Command handlers are a way for the clients to interact with your bot.
The bot listens to all the channels available to it.

Note

Since TeamSpeak Query Clients act like normal clients, they can only get channel messages inside the channel they are in. If you want clients to be able to interact with your bot while in other channels, you need to provide them a method either by allowing them to type in Server Chat or by Direct Message

All commands start with invoker (defaults to !) and proceeded by the command name.
When sending messages to the bot (Direct Message), invoker is not needed.
You can configure invoker symbol when defining TSBot instance.


The text after the command is parsed by the bot and passed in as the arguments to the command handler function.

@bot.command("test")
async def test_command(bot: TSBot, ctx: TSCtx, arg1: str, arg2: str): ...

If a command is defined with raw=True, the message skips parsing and the rest of the message is passed in as one argument.

@bot.command("test", raw=True)
async def test_command(bot: TSBot, ctx: TSCtx, arg1: str): ...

Parsing messages

The message is parsed bit by bit, whitespace as a delimiter.

If a value starts with -, it is interpreted as the name of the argument and the following value is bound to that argument name.

If a value starts with a quote (" or '), the argument is passed as the whole quoted string. This allows the arguments to have whitespace (spaces, tabs and new lines).
The ending quote must match to the one starting the quote and it must have
a whitespace behind it or be the last character to be considered as quoted value. Otherwise the argument will be interpreted as a normal value, quote included.

Parsed output comes in a form of tuple[str, …] and dict[str, str]. This output is bound to the command handler as its args and kwargs and executed.

Examples

Message

Outcome

!test arg1 arg2 arg3

args, kwargs = ('arg1', 'arg2', 'arg3'), {}

!test arg1 -arg3 arg3_value arg2

args, kwargs = ('arg1', 'arg2'), {'arg3': 'arg3_value'}

!test "this is a string with whitespace"

args, kwargs = ('this is a string with whitespace',), {}

!test -arg1 "Text 'quotes' in it"

args, kwargs = (), {'arg1': "Text 'quotes' in it"}

Command syntax

Command parameters are derived from the function definition.
First two parameters are skipped, since those are reserved for the bot and ctx parameters.

@bot.command("test", help_text="Test argument parsing")
async def test(
    bot: TSBot,
    ctx: TSCtx,
    arg1: str,
    /,
    arg2: str,
    arg3: str = "default",
    *,
    kw1: str,
    kw2: str = "default_kw",
):
    await bot.respond(ctx, ", ".join((arg1, arg2, arg3, kw1, kw2)))

Arg

Explanation

arg1

Positional only argument. This argument has to be specified and cannot be passed by name.

arg2

Argument that has to be specified either by position or name.

arg3

Same as arg2 but has a default value.

kw1

Keyword-only argument. This argument must be provided and has to be passed by name.

kw2

Same as kw1, but has default value.

Arguments are passed as a str to the corresponding parameter.
You need to do further parsing manually if other types are needed.

Checks

You can define checks to commands.
Each check is ran concurrently and once all of them have returned, the main command is executed.

You can attach checks to a command when defining the command:

ALLOWED_UIDS = ("v8t+Jw6+qNDl1KHuDfS7zVjKSws=", "v4Ea8iM9AAWko2K5ysuET4f+4Lk=")


async def check_user(bot: TSBot, ctx: TSCtx, *args: str, **kwargs: str):
    if ctx["invokeruid"] not in ALLOWED_UIDS:
        raise TSPermissionError("User not allowed to run this command")


@bot.command("dangerous", checks=[check_user])
async def dangerous_command(bot: TSBot, ctx: TSCtx):
    print("Running dangerous command")
    # ...

If one of the checks fails (raises an exception),
All of the other checks are cancelled and the main command is not executed. The first exception is re-raised and handled by the event system.
You should mainly raise exceptions that are based on TSException:

Exception

Caused by

TSCommandError

Generic command exception

TSPermissionError

Client doesn’t have the proper permissions to run this command

TSInvalidParameterError

Client is calling the command improperly

Each check is passed the same arguments as it were a command.
if you want the check to be more generic, its signature should mainly be:

async def check(bot: TSBot, ctx: TSCtx, *args: str, **kwargs: str):