Creating Commands

You’re using a command handler library so naturally you’ll probably be wanting to make some commands for your bot.

If you haven’t made your first command yet, it is recommended that you read the Getting Started page before continuing.

Note

You should note that the order that the decorators are applied is rather important. The lightbulb.implements decorator should always be on the bottom of the stack, followed by the lightbulb.command decorator on top of it. The bot.command decorator must always be on the top of the stack if you are using it.


The Implements Decorator

This decorator acts as the base for every command you will make using Lightbulb.

It defines the type or multiple types of commands that the decorated callback function will implement.

For example:

import lightbulb

@lightbulb.implements(lightbulb.PrefixCommand)
async def foo(ctx: lightbulb.Context) -> None:
    # This command will be invoked using the command prefix(es) that the bot recognises.
    ...

@lightbulb.implements(lightbulb.SlashCommand)
async def bar(ctx: lightbulb.Context) -> None:
    # This command will be created as a slash command.
    ...

@lightbulb.implements(lightbulb.PrefixCommand, lightbulb.SlashCommand)
async def baz(ctx: lightbulb.Context) -> None:
    # This command will be able to be invoked both using the bot's command prefix(es),
    # and as a slash command using interactions.
    ...

The Command Decorator

This decorator converts the decorated function into a lightbulb.commands.base.CommandLike object. This object can be coerced into any of the command classes that Lightbulb supports.

Given the fundamental differences between slash commands and prefix commands, not all kwargs will affect all of the command types that can be created.

For example:

import lightbulb

@lightbulb.command("foo", "test command", aliases=["bar", "baz"])
@lightbulb.implements(lightbulb.PrefixCommand)
async def foo(ctx: lightbulb.Context) -> None:
    ...

@lightbulb.command("foo", "test slash command", guilds=[123453463456, 34569827369])
@lightbulb.implements(lightbulb.SlashCommand)
async def _foo(ctx: lightbulb.Context) -> None:
    ...

The Option Decorator

Basic commands that respond with set messages are cool, but sometimes you might want to take input from the user to allow you to create more complex commands and more complex flows.

Lightbulb provides the lightbulb.decorators.option decorator for this purpose.

For example:

import lightbulb

@lightbulb.option("text", "text to repeat", modifier=lightbulb.OptionModifier.CONSUME_REST)
@lightbulb.command("echo", "repeats the given text")
@lightbulb.implements(lightbulb.PrefixCommand)
async def echo(ctx: lightbulb.Context) -> None:
    await ctx.respond(ctx.options.text)

Converters and Slash Command Option Types

Below is a list of all the acceptable types that you can pass into the type argument of the option decorator. On the left is the type to pass in, the right side is the converter that the type is mapped to, or for slash commands, the hikari OptionType that the type is mapped to.

Prefix command converter mapping:

Acceptable primitives: str, int, float

  • bool - lightbulb.converters.special.BooleanConverter

  • hikari.User - lightbulb.converters.special.UserConverter

  • hikari.Member - lightbulb.converters.special.MemberConverter

  • hikari.GuildChannel - lightbulb.converters.special.GuildChannelConverter

  • hikari.TextableGuildChannel - lightbulb.converters.special.TextableGuildChannelConverter

  • hikari.TextableChannel - lightbulb.converters.special.TextableGuildChannelConverter

  • hikari.GuildCategory - lightbulb.converters.special.GuildCategoryConverter

  • hikari.GuildVoiceChannel - lightbulb.converters.special.GuildVoiceChannelConverter

  • hikari.Role - lightbulb.converters.special.RoleConverter

  • hikari.Emoji - lightbulb.converters.special.EmojiConverter

  • hikari.Guild - lightbulb.converters.special.GuildConverter

  • hikari.Message - lightbulb.converters.special.MessageConverter

  • hikari.Invite - lightbulb.converters.special.InviteConverter

  • hikari.Colour - lightbulb.converters.special.ColourConverter

  • hikari.Color - lightbulb.converters.special.ColourConverter

  • hikari.Snowflake - lightbulb.converters.special.SnowflakeConverter

  • datetime.datetime - lightbulb.converters.special.TimestampConverter

  • hikari.Attachment - No converter, attachment will be pulled from the message attachments.

Slash command option type mapping:

  • str - hikari.OptionType.STRING

  • int - hikari.OptionType.INTEGER

  • float - hikari.OptionType.FLOAT

  • bool - hikari.OptionType.BOOLEAN

  • hikari.User - hikari.OptionType.USER

  • hikari.Member - hikari.OptionType.USER

  • hikari.GuildChannel - hikari.OptionType.CHANNEL

  • hikari.TextableGuildChannel - hikari.OptionType.CHANNEL

  • hikari.TextableChannel - hikari.OptionType.CHANNEL

  • hikari.GuildCategory - hikari.OptionType.CHANNEL

  • hikari.GuildVoiceChannel - hikari.OptionType.CHANNEL

  • hikari.Role - hikari.OptionType.ROLE

  • hikari.Emoji - hikari.OptionType.STRING

  • hikari.Guild - hikari.OptionType.STRING

  • hikari.Message - hikari.OptionType.STRING

  • hikari.Invite - hikari.OptionType.STRING

  • hikari.Colour - hikari.OptionType.STRING

  • hikari.Color - hikari.OptionType.STRING

  • hikari.Snowflake - hikari.OptionType.STRING

  • datetime.datetime - hikari.OptionType.STRING

  • hikari.Attachment - hikari.OptionType.ATTACHMENT

Note

Slash command options that resolve to type hikari.OptionType.STRING will also have the appropriate converter run upon invocation. If this causes the command to take too long to run then you can pass auto_defer=True to the lightbulb.command decorator. The deferral will be processed prior to the conversion of options.


Adding Checks to Commands

Checks prevent commands from being invoked if the user invoking the command does not meet the specified criteria. For example, you can prevent commands from being used in DMs, restrict them to only the owner of the bot, or restrict commands to only users that have specific permissions.

See checks for all of the checks that are provided by Lightbulb.

To add checks to a command, you need to use the lightbulb.decorators.add_checks decorator. The decorator takes an arbitrary number of lightbulb.checks.Check objects and will add all of them to the command.

For example:

import lightbulb

@lightbulb.add_checks(lightbulb.owner_only)
@lightbulb.command("foo", "test command")
@lightbulb.implements(lightbulb.PrefixCommand)
async def foo(ctx: lightbulb.Context) -> None:
    await ctx.respond("You are the owner of this bot.")

You can also create custom checks by creating your own instance of the lightbulb.checks.Check class and passing in your custom check function to the constructor. A check function should take a single argument, which will be the Context instance for the command that is attempting to be invoked. Your check should either raise an error or return False on failure and must return True if it passes. Your check may be a syncronous or asyncronous function.

For example:

import lightbulb

# OPTIONAL: Converting the check function into a Check object
@lightbulb.Check
# Defining the custom check function
def check_author_is_me(context: lightbulb.Context) -> bool:
    # Returns True if the author's ID is the same as the given one
    return context.author.id == 1455657467

# Adding the check to a command
@lightbulb.add_checks(check_author_is_me)
# Or if you do not use the @lightbulb.Check decorator
@lightbulb.add_checks(lightbulb.Check(check_author_is_me))

Adding Commands to the Bot

To add commands to the bot, you need to use the lightbulb.app.BotApp.command method, either as a decorator, or by calling it with the lightbulb.commands.base.CommandLike object to add to the bot as a command.

This method instantiates the different command objects for the given CommandLike object and registers them to the correct bot attribute.

For example:

import lightbulb

bot = lightbulb.BotApp(...)

@bot.command  # valid
@lightbulb.command("foo", "test command")
@lightbulb.implements(lightbulb.PrefixCommand)
async def foo(ctx: lightbulb.Context) -> None:
    ...

@bot.command()  # also valid
@lightbulb.command("bar", "test command")
@lightbulb.implements(lightbulb.PrefixCommand)
async def bar(ctx: lightbulb.Context) -> None:
    ...

@lightbulb.command("baz", "test command")
@lightbulb.implements(lightbulb.PrefixCommand)
async def baz(ctx: lightbulb.Context) -> None:
    ...

bot.command(baz)  # also valid