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 always_defer
or default_to_ephemeral
.
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 |
|
Adding Options!
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 SlashContext
, a 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 say
string.
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 with_x_as_slash_option
decorators.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Aside for Message Commands
While 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 MessageCommands
use 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 MessageCommand
.
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 with_argument
and with_option
.
Important Note:
Notice that the arguments/options are added in the reverse order in this example!!
With decorators, arguments are added
sort
first andquery
second.ShlexParser
addsquery
first andsort
second. This is because theShlexParser
interprets 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 AnyHooks().set_on_error()
.
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!
Adding groups
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 SlashCommand
.
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 name
and description
.
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 /tester test-no-dec
!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Chaining
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 load_from_scope()
and 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 |
|