Tanjun uses a lot of decorators! The decorators are wonderful in helping simplify and cut down how much code we end users have to write, but it can hide what's really happening. This post will walk you through making a slash command and slash command group with no decorators at all!
A simple Slash Command
Below we have the most simple Slash command we can make. All it does is respond with "Pong!" when called, but this time we use no decorators.
On line 3 we define our callback. Like with the regular decorator method, this callback needs to accept a
SlashContext parameter as well as any extra parameters you define. For now, we are not using any extra parameters, so just a
ctx parameter is used. There is no real change to this function outside of the lack of decorators.
To make our callback function work as an actual command we need create a
tanjun.SlashCommand. On line 6 we instantiate this class and pass it 3 parameters; the function to act as a callback, a string to act as the commands name, and lastly a string to act as the commands description. Look familiar? This is all
tanjun.as_slash_command does when used as a decorator on a function, just with a little added validation. Just like with
as_slash_command you can also pass in other key-word options like
Finally on line 12 we setup the wonderfully simple one-liner that both registers all commands/groups in the local scope to the new component and simultaneously generates a load/unload function for us. If you load the component into your Tanjun client now you should have a Slash Command without using a single decorator!
1 2 3 4 5 6 7 8 9 10 11 12
One of our favorite features of Slash Commands are the options. Decorators like
tanjun.with_str_slash_option allow members to provide client-side validated input. As you've seen in the previous example decorators are generally just a small wrapper that we can easily replicate. Options are no different!
tanjun.SlashCommand offers methods that are exact mirrors of those
with_x_as_slash_option. In fact, the
with_x_as_slash_option calls this method on the
SlashCommand is decorating! With this information, let's update that code.
On line 3 the callback has been changed to accept 3 parameters; the default
say string, and a
repeat int. The callback body has been updated too. It now uses
repeat to make a for loop, and responds with the
Line 7's definition of the
SlashCommand remains unchanged from the previous example.
Line 13-14 add the new options. By using
slash_cmd.add_x_option(...) we are able to setup options using the same arguments and parameters as the
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Aside for Message Commands
SlashCommands have a nice client-side validation via Discord,
MessageCommands do not have this benefit. Instead Tanjun provides a
ShlexParser to add Unix style options and arguments. Below we will look at a simple
MessageCommand to query Spotify.
On line 7 we create a placeholder converter function.
Line 10 we define a placeholder callback to be run when the command is sent.
Line 13 - 16 creates the arguments for our
MessageCommand. Notice that we decorate with the "sort" option/argument first, then the "query" option/argument. This will be important later. Even though we said that
ShlexParaser, we don't use it here at all. That's intentional actually! If no "parser" is set when you add your first
with_option decorator, tanjun will automatically apply the builtin
ShlexParser for you.
Line 17 we use
@tanjun.as_message_command to decorate our callback.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
Now let's see what kind of changes you can expect for
Line 13 begins by instantiating a new
MessageCommand. We provide the callback with the command name to bind to.
On line 15 we instantiate the
tanjun.ShlexParser() previously mentioned.
Lines 16 - 17 adds our arguments and options to the
ShlexParser. Like we established before, most decorators have direct method/functions parallels, and that holds true here. We use the
ShlexParser().add_argument(...).add_option(...) instead of
Notice that the arguments/options are added in the reverse order in this example!!
With decorators, arguments are added
sortsecond. This is because the
ShlexParserinterprets top-down. This means the first command we see runs first. All decorators execute in reverse order or bottom-up. This means the bottom most decorator runs first, then that result passes to the decorator above it.
Finally line 19 adds the
ShlexParser to the
spotify_command. Without this call the argument/options remain totally separate from the command itself.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Checks & Hooks
You might be starting to see a trend here. Most decorators just call methods on the object they are decorating. Hooks and Checks are no exception to this!
Below we've added some placeholder functions on lines 4 and 7. You can just replace this with your own valid hook or check, or one that Tanjun provides.
The definitions from lines 11-18 remain the same as the first example, though it will work with options too!
Line 20 calls
tanjun.SlashCommand.add_check() to add your check callback to the command. This method is what
tanjun.with_check() calls under the hood.
Finally line 22 calls
tanjun.SlashCommand.set_hooks(). In this method we create a new
tanjun.AnyHooks() object and then call
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
Note: If you're not sure how to use or write Hooks and Checks stay tuned! Those will be coming up soon!
Lastly you can also add Command Groups without decorators.
Lines 1-14 remain the same as the first example. A simple callback and creating a
Line 17 we create a new
tanjun.SlashCommandGroup() directly, instead of using the decorator
@tanjun.slash_command_group(). The class accepts all the same parameters as the decorator does, with the first two parameters being
On line 18 we use
SlashCommandGroup().add_commad(), which makes our
slash_cmd part of this command group.
If you load this plugin, your command should now start appearing as
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
While this article was primarily trying to give examples that are "simple" and demystify what exactly the decorators are doing, there is a better way of writing decorator-less code! Tanjun aims to be more "functional" than most Python libraries and how well it chains code is an example of that.
Let's consider the example from Adding Groups. Instead of creating a
slash_cmd variable, then separately calling
slash_cmd.add_x_option, then separately adding another option, we can do this all in one call!
Notice line 7
slash_cmd = (; this means execute everything within the parens and assign the value to
slash_cmd. Tanjun is written in a way that
add_x_option(...) always returns the
SlashCommand being added to. This design choice allows us to chain all options into one expression. We can also do this with
make_loaders(), which always returns the
Component() the methods were called from.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19