Slash Commands⚓︎
A slash command is one of the types of application commands a bot can create. Unlike prefix and hybrid commands, these do not require the message content intent.
Application commands are native ways to interact with apps in the Discord client. There are 3 types of commands accessible in different interfaces: the chat input, a message's context menu (top-right menu or right-clicking in a message), and a user's context menu (right-clicking on a user).
Note
By default, slash commands are available globally. This means that they can be used in anywhere from servers to DMs. However, you can also limit them to specific guilds and set permissions for them.
The CommandTree⚓︎
The CommandTree
is the main container class defined by discord.py
for slash commands. It is used to register and manage all slash commands and provides an interface for interacting with them.
To use the slash commands registered in the CommandTree
, you also need to sync them with Discord. This is done by calling CommandTree.sync()
. When you sync your commands, the metadata for the commands is sent to Discord and Discord will create the commands for you which you can access from the Discord client.
When invoking a slash command, Discord will send an interaction to your bot and the callback you defined for the command will be called.
When you call CommandTree.sync
, you sync one scope of the CommandTree, either the list of global commands (sync())
or the list for one guild (sync(guild=discord.Object(...)))
.
You can read more about how to sync commands in the Syncing Commands section.
If you are using commands.Bot
, you can directly access the CommandTree
through the commands.Bot.tree
attribute as the tree is already instantiated for you when you create the bot.
Creating a Slash Command⚓︎
To create a slash command, you can use the CommandTree.command
decorator. Unlike prefix and hybrid commands, here instead of a commands.Context
object, you get a discord.Interaction
object. This object contains all the information about the interaction, including the command name, the guild it was used in, the user who used it, etc.
The discord.Interaction
object also has a response
attribute which is a discord.InteractionResponse
object. This object is used to send responses to the interaction. Unlike the commands.Context
object, you can only send one response per interaction and any further responses need to be sent as followups.
Similarly to regular and hybrid commands, you can pass in a name
argument to the decorator to set the name of the command. If you do not pass in a name, the name of the function will be used as the name of the command.
There are two ways to document slash commands. You can either use the app_commands.describe
decorator or docstrings. discord.py
accepts multiple docstring formats, including Google-style, NumPy-style, and Sphinx-style.
import discord
from discord import app_commands
from discord.ext import commands
@bot.tree.command(name="echo", description="Echoes a message.")
@app_commands.describe(message="The message to echo.")
async def echo(interaction: discord.Interaction, message: str) -> None:
await interaction.response.send_message(message)
import discord
from discord.ext import commands
@bot.tree.command()
async def echo(interaction: discord.Interaction, message: str) -> None:
"""
Echoes a message.
Parameters
----------
interaction : discord.Interaction
The interaction object.
message : str
The message to echo.
"""
await interaction.response.send_message(message)
Sending Responses⚓︎
To send a response to an interaction, you can use the discord.InteractionResponse.send_message
method. This method is same as your usual commands.Context.send
method and takes in the same arguments.
@bot.tree.command()
async def echo(interaction: discord.Interaction, message: str) -> None:
await interaction.response.send_message(message)
You can only send one response per interaction. Any further responses will lead to InteractionResponded
error.
@bot.tree.command()
async def ping(inter: discord.Interaction) -> None:
"""Get the bot's latency"""
await inter.response.send_message(f"Pong! ({round(bot.latency * 1000)}ms)")
try:
await inter.response.send_message(f"Trying to send a second message...")
except discord.InteractionResponded:
await inter.followup.send(f"Responding again failed, as expected.")
If you do not send a response, the interaction will be marked as failed and the user will be notified that the interaction failed.
Sending Followups⚓︎
To send followups, you can use the discord.Interaction.followup.send
method. This method is same as your usual commands.Context.reply
method and basically replies to your original response. A followup message can be sent only after you have sent a response.
@bot.tree.command()
async def echo(interaction: discord.Interaction, message: str) -> None:
await interaction.response.send_message(message)
await interaction.followup.send("This is a followup message.")
Warning
You can send as many followups as you want, but you can only have one response per interaction.
@bot.tree.command()
async def ping(inter: discord.Interaction) -> None:
"""Get the bot's latency"""
await inter.response.send_message(f"Pong! ({round(bot.latency * 1000)}ms)")
try:
await inter.response.send_message(f"Trying to send a second message...")
except discord.InteractionResponded:
await inter.followup.send(f"Responding again failed, as expected.")
await inter.followup.send("Trying to send a second message...")
You can send followups only for 15 minutes
after the interaction was created. To check if you can send followups, you can use the discord.Interaction.is_expired()
method.`
Editing Responses⚓︎
To edit a response, you can use the discord.Interaction.edit_original_response
method.
@bot.tree.command()
async def echo(interaction: discord.Interaction, message: str) -> None:
await interaction.response.send_message(message)
await interaction.edit_original_response("This is an edited message.")
Deleting Responses⚓︎
To delete a response, you can use the discord.Interaction.delete_original_response
method.
@bot.tree.command()
async def echo(interaction: discord.Interaction, message: str) -> None:
await interaction.response.send_message(message)
await interaction.delete_original_response()
Deferring Responses⚓︎
You have 3 seconds
to respond to an interaction if you delay the response, otherwise the interaction will be marked as failed and the user will be notified that the interaction failed. To make sure that you can respond to the interaction, you can defer the response. This will make the user see a loading state until you send a response and will give you 15 minutes
to respond to the interaction.
To defer a response, you can use the discord.InteractionResponse.defer
method. After deferring the response, you can no longer use the send_message
method to send a response. Instead, you need to use the edit_original_response
method to edit the deferred response.
import asyncio
@bot.tree.command()
async def echo(interaction: discord.Interaction, message: str) -> None:
await interaction.response.defer()
await asyncio.sleep(5)
await interaction.edit_original_response(content=message)
Ephemeral Responses⚓︎
You can send ephemeral responses to an interaction. These responses are only visible to the user who used the command and are not visible to other users. To send an ephemeral response, you can use the discord.InteractionResponse.send_message
method and pass in ephemeral=True
, or if you are using the discord.InteractionResponse.defer
method, you can pass in ephemeral=True
to defer
method.
Slash Command Parameters⚓︎
Slash commands, have a few parameters that are supported by discord when registering the command.
str [AppCommandOptionType.string]
- A string parameterint [AppCommandOptionType.integer]
- An integer parameterfloat [AppCommandOptionType.number]
- A float parameterbool [AppCommandOptionType.boolean]
- A boolean parameterdiscord.User [AppCommandOptionType.user]
ordiscord.Member
- A user or member parameter. A user annotation can also result in a member parameter.- *
discord.abc.GuildChannel
- A channel parameter discord.Role [AppCommandOptionType.role]
- A role parameterdiscord.Attachment [AppCommandOptionType.attachment]
- An attachment parameter
All other parameters will be treated as a string parameter and can be converted implicitly using transformers.
Info
-
* All channel subclasses and unions (e.g. Union[TextChannel, StageChannel]) are also supported.
-
Some combinations of types are also allowed, including:
- Union[User, Member] (results in AppCommandOptionType.user)
- Union[Member, Role] (results in AppCommandOptionType.mentionable)
- Union[User, Role] (results in AppCommandOptionType.mentionable)
- Union[User, Member, Role] (results in AppCommandOptionType.mentionable)
@bot.tree.command()
async def echo(interaction: discord.Interaction, user: discord.User) -> None:
"""
Echoes a user
Parameters
----------
interaction : discord.Interaction
The interaction object.
user : discord.User
The user to echo.
"""
embed = discord.Embed(title=user.name, description=f"{user.mention} is cool", color=discord.Color.random())
embed.set_thumbnail(url=user.display_avatar)
await interaction.response.send_message(embed=embed)
@bot.tree.command()
async def echo(interaction: discord.Interaction, channel: discord.abc.GuildChannel) -> None:
"""
Echoes a channel
Parameters
----------
interaction : discord.Interaction
The interaction object.
channel : discord.abc.GuildChannel
The channel to echo.
"""
# channel types include StageChannel, TextChannel, VoiceChannel, CategoryChannel, ForumChannel
await interaction.response.send_message(channel.mention)
@bot.tree.command()
async def echo(interaction: discord.Interaction, role: discord.Role) -> None:
"""
Echoes a role
Parameters
----------
interaction : discord.Interaction
The interaction object.
role : discord.Role
The role to echo.
"""
embed = discord.Embed(title=role.name, description=f"{role.mention} is cool", color=role.color)
await interaction.response.send_message(embed=embed)
@bot.tree.command()
async def echo(interaction: discord.Interaction, attachment: discord.Attachment):
"""
Echoes an attachment
Parameters
----------
interaction : discord.Interaction
The interaction object.
attachment: discord.Attachment
The attachment to echo
"""
embed = discord.Embed(title=attachment.filename, description=f"Attachment from {attachment.url}\nSize: {attachment.size / 1024} KB", color=discord.Color.random())
embed.set_image(url=attachment.url)
await interaction.response.send_message(embed=embed)
Parameter Ranges⚓︎
You can also specify a range for integer and float parameters. This can be done by annotating using app_commands.Range
, and for string you can specify the minimum and maximum length.
@bot.tree.command()
async def echo(inter: discord.Interaction, number: app_commands.Range[float, 0.0, 10.0]):
"""
Echoes a number
Parameters
----------
inter: discord.Interaction
The interaction object
number: app_commands.Range[float, 0.0, 10.0]
The number to echo
"""
await inter.response.send_message(number)
@bot.tree.command()
async def echo(inter: discord.Interaction, message: app_commands.Range[str, 1, 10]):
"""
Echoes a message
Parameters
----------
inter: discord.Interaction
The interaction object
message: app_commands.Range[str, 1, 10]
The message to echo
"""
await inter.response.send_message(message)
Choices⚓︎
You can also specify choices for parameters. This gives a dropdown menu with the specified choices for the parameter in discord.
import enum
class Color(str, enum.Enum):
Red = "red"
Green = "green"
Blue = "blue"
@bot.tree.command()
async def echo(inter: discord.Interaction, color: Color):
"""
Echoes a color
Parameters
----------
inter: discord.Interaction
The interaction object
color: Color
The color to echo
"""
await inter.response.send_message(color)
from typing import Literal
@bot.tree.command()
async def echo(inter: discord.Interaction, color: Literal["red", "green", "blue"]):
"""
Echoes a color
Parameters
----------
inter: discord.Interaction
The interaction object
color: Literal["red", "green", "blue"]
The color to echo
"""
await inter.response.send_message(color)
@bot.tree.command()
@app_commands.choices(color=[
app_commands.Choice(name="Red", value="red"),
app_commands.Choice(name="Green", value="green"),
app_commands.Choice(name="Blue", value="blue")
])
async def echo(inter: discord.Interaction, color: str):
"""
Echoes a color
Parameters
----------
inter: discord.Interaction
The interaction object
color: str
The color to echo
"""
await inter.response.send_message(color)
Warning
You can have a maximum of 25 choices.
Autocomplete⚓︎
You can also specify autocomplete options for parameters. This gives a dropdown menu with the specified autocomplete options for the parameter in discord based on the user's input.
Currently, autocomplete is only supported for string, integer and float parameters.
async def color_autocomplete(interaction: discord.Interaction, current: str) -> list[app_commands.Choice[str]]:
options = ["Red", "Green", "Blue"]
return [app_commands.Choice(name=option, value=option) for option in options if option.lower().startswith(current.lower())][:25]
@bot.tree.command()
@app_commands.autocomplete(color=color_autocomplete)
async def echo(inter: discord.Interaction, color: str):
"""
Echoes a color
Parameters
----------
inter: discord.Interaction
The interaction object
color: str
The color to echo
"""
await inter.response.send_message(color)
@bot.tree.command()
async def echo(inter: discord.Interaction, color: str):
"""
Echoes a color
Parameters
----------
inter: discord.Interaction
The interaction object
color: str
The color to echo
"""
await inter.response.send_message(color)
@echo.autocomplete("color")
async def color_autocomplete(interaction: discord.Interaction, current: str) -> list[app_commands.Choice[str]]:
options = ["Red", "Green", "Blue"]
return [app_commands.Choice(name=option, value=option) for option in options if option.lower().startswith(current.lower())][:25]
Warning
You can have a maximum of 25 autocomplete options.
Transformers⚓︎
Transformers are equivalent to converters in regular and hybrid commands. They are used to convert the parameter to the desired type. You can use the app_commands.Transformer
decorator to register a transformer.
The difference between transformers and converters are the following:
- Transformers inherit from
app_commands.Transformer
instead ofcommands.Converter
. - It requires you to implement the
transform
method instead of theconvert
method. - Passed parameters are
discord.Interaction
instead ofcommands.Context
.
class DurationConverter(app_commands.Transformer):
async def transform(self, inter: discord.Interaction, argument: str) -> datetime.timedelta:
multipliers = {
's': 1, # seconds
'm': 60, # minutes
'h': 3600, # hours
'd': 86400, # days
'w': 604800 # weeks
}
try:
amount = int(argument[:-1])
unit = argument[-1]
seconds = amount * multipliers[unit]
delta = datetime.timedelta(seconds=seconds)
return delta
except (ValueError, KeyError):
raise commands.BadArgument("Invalid duration provided.")
@bot.tree.command()
async def echo(inter: discord.Interaction, duration: app_commands.Transform[datetime.timedelta, DurationConverter]) -> None:
"""
Echoes a duration
Parameters
----------
inter: discord.Interaction
The interaction object
duration: app_commands.Transform[datetime.timedelta, DurationConverter]
The duration to echo
"""
# it will work with app_commands.Transform[datetime.timedelta, DurationConverter] as well but with it duration is typed as datetime.timedelta instead of DurationConverter
await inter.response.send_message(duration)
The core principles of transformers are the same as converters. You can read more about them in the Converters section.
Slash Command Groups⚓︎
You can also create slash command groups. These are similar to regular command groups and can be used to group slash commands together. You can create a slash command group using the app_commands.Group
class.
group = app_commands.Group(name="math", description="Math commands")
child = app_commands.Group(name="functions", description="Math functions")
@child.command(name="pow", description="Raise a number to a power")
async def pow(inter: discord.Interaction, a: int, b: int) -> None:
await inter.response.send_message(f"{a} ^ {b} = {a ** b}")
@group.command(name="add", description="Add two numbers")
async def add(inter: discord.Interaction, a: int, b: int) -> None:
await inter.response.send_message(f"{a} + {b} = {a + b}")
@group.command(name="subtract", description="Subtract two numbers")
async def subtract(inter: discord.Interaction, a: int, b: int) -> None:
await inter.response.send_message(f"{a} - {b} = {a - b}")
group.add_command(child)
bot.tree.add_command(group)
class MathGroup(app_commands.Group):
def __init__(self):
super().__init__(name="math", description="Math commands")
@app_commands.command(name="add", description="Add two numbers")
async def add(self, inter: discord.Interaction, a: int, b: int) -> None:
await inter.response.send_message(f"{a} + {b} = {a + b}")
@app_commands.command(name="subtract", description="Subtract two numbers")
async def subtract(self, inter: discord.Interaction, a: int, b: int) -> None:
await inter.response.send_message(f"{a} - {b} = {a - b}")
class FunctionsGroup(app_commands.Group):
def __init__(self):
super().__init__(name="functions", description="Math functions")
@app_commands.command(name="pow", description="Raise a number to a power")
async def pow(self, inter: discord.Interaction, a: int, b: int) -> None:
await inter.response.send_message(f"{a} ^ {b} = {a ** b}")
parent = MathGroup()
parent.add_command(FunctionsGroup())
bot.tree.add_command(parent)
import discord
from discord import app_commands
from discord.ext import commands
class MathGroup(commands.GroupCog, name="math", description="Math commands"):
group = app_commands.Group(name="functions", description="Math functions")
def __init__(self, bot: commands.Bot) -> None:
self.bot = bot
super().__init__()
# available as /math functions pow <a> <b>
@group.command(name="pow", description="Raise a number to a power")
async def pow(self, inter: discord.Interaction, a: int, b: int) -> None:
await inter.response.send_message(f"{a} ^ {b} = {a ** b}")
# available as /math add <a> <b>
@app_commands.command(name="add", description="Add two numbers")
async def add(self, inter: discord.Interaction, a: int, b: int) -> None:
await inter.response.send_message(f"{a} + {b} = {a + b}")
# available as /math subtract <a> <b>
@app_commands.command(name="subtract", description="Subtract two numbers")
async def subtract(self, inter: discord.Interaction, a: int, b: int) -> None:
await inter.response.send_message(f"{a} - {b} = {a - b}")
# available as /math multiply <a> <b>
@commands.hybrid_command(name="multiply", description="Multiply two numbers")
async def multiply(self, ctx: commands.Context[commands.Bot], a: int, b: int) -> None:
await ctx.send(f"{a} * {b} = {a * b}")
# available as <prefix>divide <a> <b>
@commands.command(name="divide", description="Divide two numbers")
async def divide(self, ctx: commands.Context[commands.Bot], a: int, b: int) -> None:
await ctx.send(f"{a} / {b} = {a / b}")
async def setup(bot: commands.Bot) -> None:
await bot.add_cog(MathGroup(bot))
Info
Group cogs are special cogs that are used to create slash command groups.
- They group all slash commands defined in the cog under a single slash command group.
- They group all hybrid commands defined in the cog are also grouped but they are only available as slash commands and not as regular commands.
- Regular commands are not grouped and are left as is.
Warning
You can have only maximum of 1 level of nesting for slash command groups.
Slash Commands in Cogs⚓︎
Here is an example of how you can use slash commands in cogs.
import discord
from discord import app_commands
from discord.ext import commands
class General(commands.Cog):
group = app_commands.Group(name="utility", description="Utility commands")
def __init__(self, bot: commands.Bot) -> None:
self.bot = bot
@group.command(name="ping", description="Get the bot's latency")
async def ping(self, inter: discord.Interaction) -> None:
await inter.response.send_message(f"Pong! {round(self.bot.latency * 1000)}ms")
@app_commands.command(name="echo", description="Echo a message")
async def echo(self, inter: discord.Interaction, message: str) -> None:
await inter.response.send_message(message)
async def setup(bot: commands.Bot) -> None:
await bot.add_cog(General(bot))
Error Handling and Checks⚓︎
Error Handling⚓︎
To handle errors in slash commands globally, you need to override the CommandTree.on_error
method. This method is called whenever an error occurs in a slash command.
from __future__ import annotations
import discord
from discord import app_commands
from discord.ext import commands
bot = commands.Bot(...)
@bot.tree.command()
async def test(inter: discord.Interaction) -> None:
await inter.response.send_message(f"{1 / 0}")
@bot.tree.error
async def on_error(interaction: discord.Interaction[discord.Client], error: app_commands.AppCommandError | Exception) -> None:
if isinstance(error, app_commands.errors.CommandInvokeError):
error = error.original
message = f"\nException: {error.__class__.__name__}, Command: {interaction.command.qualified_name if interaction.command else None}, User: {interaction.user}, Time: {discord.utils.format_dt(interaction.created_at, style='F')}\n"
try:
await interaction.response.send_message(f"An error occurred: {message}")
except discord.InteractionResponded:
await interaction.followup.send(f"An error occurred: {message}")
from __future__ import annotations
import discord
from discord import app_commands
from discord.ext import commands
class CustomTree(app_commands.CommandTree):
async def on_error(self, interaction: discord.Interaction[discord.Client], error: app_commands.AppCommandError | Exception) -> None:
if isinstance(error, app_commands.errors.CommandInvokeError):
error = error.original
message = f"\nException: {error.__class__.__name__}, Command: {interaction.command.qualified_name if interaction.command else None}, User: {interaction.user}, Time: {discord.utils.format_dt(interaction.created_at, style='F')}\n"
try:
await interaction.response.send_message(f"An error occurred: {message}")
except discord.InteractionResponded:
await interaction.followup.send(f"An error occurred: {message}")
bot = commands.Bot(..., tree_cls=CustomTree) # don't instantiate the tree yourself
Note
A benefit of subclassing CommandTree
is that you can also override other methods such as interaction_check
to perform custom checks before invoking the command.
Error Handling in Cogs⚓︎
To handle errors in slash commands in cogs, you need to override the commands.Cog.cog_app_command_error
method. This method is called whenever an error occurs in a slash command in a cog.
from __future__ import annotations
import discord
from discord import app_commands
from discord.ext import commands
class General(commands.Cog):
def __init__(self, bot: commands.Bot) -> None:
self.bot = bot
@bot.tree.command()
async def test(inter: discord.Interaction) -> None:
await inter.response.send_message(f"{1 / 0}")
async def cog_app_command_error(self, inter: discord.Interaction, error: app_commands.AppCommandError | Exception) -> None:
if isinstance(error, app_commands.errors.CommandInvokeError):
error = error.original
message = f"\nException: {error.__class__.__name__}, Command: {inter.command.qualified_name if inter.command else None}, User: {inter.user}, Time: {discord.utils.format_dt(inter.created_at, style='F')}\n"
try:
await inter.response.send_message(f"An error occurred: {message}")
except discord.InteractionResponded:
await inter.followup.send(f"An error occurred: {message}")
async def setup(bot: commands.Bot) -> None:
await bot.add_cog(General(bot))
Per-Command Error Handling⚓︎
You can also handle errors for a specific slash commands similar to regular and hybrid commands. To do this, you need to use the app_commands.error
decorator.
@bot.tree.command()
async def test(inter: discord.Interaction) -> None:
await inter.response.send_message(f"{1 / 0}")
@test.error
async def test_error(inter: discord.Interaction, error: app_commands.AppCommandError | Exception) -> None:
if isinstance(error, app_commands.errors.CommandInvokeError):
error = error.original
message = f"\nException: {error.__class__.__name__}, Command: {inter.command.qualified_name if inter.command else None}, User: {inter.user}, Time: {discord.utils.format_dt(inter.created_at, style='F')}\n"
try:
await inter.response.send_message(f"An error occurred: {message}")
except discord.InteractionResponded:
await inter.followup.send(f"An error occurred: {message}")
Checks⚓︎
You can also use checks to check if a user is allowed to use a slash command. These checks are same as regular and hybrid commands and can be used to check if a user is allowed to use a slash command.
Here is how to make a custom check for slash commands.
from __future__ import annotations
import discord
from discord import app_commands
from discord.ext import commands
bot = commands.Bot(...)
async def is_owner(inter: discord.Interaction) -> bool:
return inter.user.id == 1234567890
@bot.tree.command()
@app_commands.check(is_owner)
async def test(inter: discord.Interaction) -> None:
await inter.response.send_message("You are the owner!")
from __future__ import annotations
import discord
from discord import app_commands
from discord.ext import commands
bot = commands.Bot(...)
def is_owner():
async def predicate(inter: discord.Interaction) -> bool:
return inter.user.id == 1234567890
return app_commands.check(predicate)
@bot.tree.command()
@is_owner()
async def test(inter: discord.Interaction) -> None:
await inter.response.send_message("You are the owner!")
Further documentation on checks can be found in the Checks section.
Permissions⚓︎
app_commands.default_permissions
⚓︎
You can set the default permissions for a hybrid command using the app_commands.default_permissions
decorator. This will set the default permissions for the slash command.
Leaving the permissions
parameter empty will disable the command for everyone except server administrators.
@bot.tree.command()
@app_commands.default_permissions(permissions=discord.Permissions(manage_messages=True))
async def echo(inter: discord.Interaction, message: str):
"""
Echoes a message
Parameters
----------
inter: discord.Interaction
The interaction object
message: str
The message to echo
"""
await interaction.response.send_message(message)
Can be used as a kwarg
for Group
as well.
group = app_commands.Group(name="utility", description="Utility commands", default_permissions=discord.Permissions(manage_messages=True))
app_commands.guilds
⚓︎
You can also specify the guilds where the command should be enabled using the app_commands.guilds
decorator. This will set the guilds where the slash command should be enabled.
@bot.tree.command()
@app_commands.guilds(discord.Object(id=1234567890), ...)
async def echo(inter: discord.Interaction, message: str):
"""
Echoes a message
Parameters
----------
inter: discord.Interaction
The interaction object
message: str
The message to echo
"""
await interaction.response.send_message(message)
Can be used as a kwarg
for Group
as well.
group = app_commands.Group(name="utility", description="Utility commands", guild_ids=[discord.Object(id=1234567890), ...])
app_commands.guild_only
⚓︎
You can also specify that the command should only be enabled in guilds using the app_commands.guild_only
decorator. This will set the command to only be enabled in guilds and will not be available in DMs.
@bot.tree.command()
@app_commands.guild_only()
async def echo(inter: discord.Interaction, message: str):
"""
Echoes a message
Parameters
----------
inter: discord.Interaction
The interaction object
message: str
The message to echo
"""
await interaction.response.send_message(message)
Can be used as a kwarg
for Group
as well and as a decorator for GroupCog
.
@app_commands.guild_only()
class UtilityGroup(commands.GroupCog, name="utility", description="Utility commands"):
...
User Commands⚓︎
You can access these commands by right clicking on a user profile > Apps
> Commands
. These allow a command to be invoked on a user, without any arguments or text input.
@bot.tree.context_menu(name="info")
async def info(inter: discord.Interaction, member: discord.Member) -> None:
embed = discord.Embed(title=member.name, description=f"{member.mention} is cool", color=member.color)
embed.set_thumbnail(url=member.display_avatar)
await inter.response.send_message(embed=embed)
from __future__ import annotations
import discord
from discord import app_commands
from discord.ext import commands
class Cog(commands.Cog):
def __init__(self, bot: commands.Bot) -> None:
self.bot = bot
self.user_menu = app_commands.ContextMenu(callback=self.info, name="info")
self.user_menu.error(self.info_error)
self.bot.tree.add_command(self.user_menu)
async def info(self, inter: discord.Interaction, member: discord.Member) -> None:
embed = discord.Embed(title=member.name, description=f"{member.mention} is cool", color=member.color)
embed.set_thumbnail(url=member.display_avatar)
await inter.response.send_message(embed=embed)
async def info_error(self, inter: discord.Interaction, error: app_commands.AppCommandError | Exception) -> None:
if isinstance(error, app_commands.errors.CommandInvokeError):
error = error.original
message = f"\nException: {error.__class__.__name__}, Command: {inter.command.qualified_name if inter.command else None}, User: {inter.user}, Time: {discord.utils.format_dt(inter.created_at, style='F')}\n"
try:
await inter.response.send_message(f"An error occurred: {message}")
except discord.InteractionResponded:
await inter.followup.send(f"An error occurred: {message}")
async def setup(bot: commands.Bot) -> None:
await bot.add_cog(Cog(bot))
Message Commands⚓︎
You can access these commands by right clicking on a message > Apps
> Commands
. These allow a command to be invoked on a message, without any arguments or text input.
from __future__ import annotations
import discord
from discord import app_commands
from discord.ext import commands
class Cog(commands.Cog):
def __init__(self, bot: commands.Bot) -> None:
self.bot = bot
self.message_menu = app_commands.ContextMenu(callback=self.count, name="count")
self.message_menu.error(self.count_error)
self.bot.tree.add_command(self.message_menu)
async def count(self, inter: discord.Interaction, message: discord.Message) -> None:
await inter.response.send_message(f"Message count: {len(message.content)} - Word count: {len(message.content.split())}")
async def count_error(self, inter: discord.Interaction, error: app_commands.AppCommandError | Exception) -> None:
if isinstance(error, app_commands.errors.CommandInvokeError):
error = error.original
message = f"\nException: {error.__class__.__name__}, Command: {inter.command.qualified_name if inter.command else None}, User: {inter.user}, Time: {discord.utils.format_dt(inter.created_at, style='F')}\n"
try:
await inter.response.send_message(f"An error occurred: {message}")
except discord.InteractionResponded:
await inter.followup.send(f"An error occurred: {message}")
async def setup(bot: commands.Bot) -> None:
await bot.add_cog(Cog(bot))
Localization⚓︎
Localization is supported for slash commands. It allows you to translate all slash command details such as name, description, choices, autocomplete options, etc to different languages for your bot.
from __future__ import annotations
from discord import app_commands
from discord.app_commands.translator import TranslationContextTypes, locale_str
from discord.enums import Locale
from gpytranslate import Translator as GoogleTranslator
class Translator(app_commands.Translator):
async def translate(self, string: locale_str, locale: Locale, context: TranslationContextTypes) -> str | None:
t = GoogleTranslator()
try:
translation = await t.translate(string.message, sourcelang="en", targetlang=locale.value)
return translation.text # type: ignore
except Exception:
return None
bot = commands.Bot(...)
async def setup_hook() -> None:
await bot.tree.set_translator(Translator())
await bot.tree.sync()
bot.setup_hook = setup_hook
@bot.tree.command(name=locale_str("test"), description=locale_str("A test command"))
@app_commands.choices(test=[
app_commands.Choice(name=locale_str('One'), value=1),
app_commands.Choice(name=locale_str('Two'), value=2),
app_commands.Choice(name=locale_str('Three'), value=3),
])
@app_commands.describe(test=locale_str("A test choice"))
async def translations(interaction: discord.Interaction, test: app_commands.Choice[int]) -> None:
await interaction.response.send_message(repr(test))
bot.run(...)
Tip
Here I am using gpytranslate to translate the strings. You can use any translation library you want. Just make sure that your translation happens asynchronously. In case you are utilizing a synchronous translation library, you can use the following code.
It's recommended to translate them one time separately and store them in a dictionary and then use that dictionary to translate the strings so as to prevent getting rate limited by the translation API.
Failure
While translating with the gpytranslate
library, I ran into invalid characters in the translated string in languages romanian
, vietnamese
and turkish
. I am not sure if this is a problem with the library or Google Translate API
itself. If you run into the same error feel free to either open an issue on the library's repository or use a different translation library.
Syncing⚓︎
You can sync your slash commands with discord using the CommandTree.sync
method. This will sync all the slash commands in your tree with discord.
For information on how to sync your slash commands, refer to the Syncing section in the hybrid commands documentation.
Conclusion⚓︎
Slash commands are a great way to interact with your bot. They are easy to use and can be used to create a wide variety of commands with amazing interactivity thanks to interface with discord's user interface.