Checks⚓︎
Checks are utility decorators that are called before to the execution of commands.
These checks should be predicates that take in a single parameter taking a Context. If the check returns a False-like value then during invocation a CheckFailure exception is raised.
If an exception should be thrown in the predicate then it should be a subclass of CommandError. Any exception not subclassed from it will be propagated.
Usage⚓︎
Check is only a function that, based on the input, either throws an error or returns True/False.
Per-command apply⚓︎
Adding check to any single command
commands.check⚓︎
A decorator that adds a single check to the prefix command
def some_single_check(ctx: commands.Context):
...
@bot.command()
@commands.check(some_single_check)
async def foo(ctx: commands.Context):
await ctx.send('You passed the check!')
commands.check_any⚓︎
A check() that is added that checks if any of the checks passed will pass, i.e. using logical OR.
app_commands.check⚓︎
A decorator that adds a single check to the slash command
Global apply⚓︎
Adding check to all existing prefix commands
To add a kind of global check for slash commands you can override CommandTree.interaction_check
bot.check⚓︎
A decorator that adds a global check to the bot.
A global check is similar to a check() that is applied on a per command basis except it is run before any command checks have been verified and applies to every command the bot has.
bot.check_once⚓︎
Same as bot.check except it is called only once per invoke() call
Regular global checks are called whenever a command is called or Command.can_run() is called. This type of check bypasses that and ensures that it’s called only once, even inside the default help command.
bot.before_invoke⚓︎
A decorator that registers a coroutine as a pre-invoke hook.
@bot.before_invoke
async def handler(ctx: commands.Context):
print(f"Command '{ctx.command.name}' is started")
Note
The bot.before_invoke and bot.after_invoke hooks are only called if all checks and argument parsing procedures pass without error. If any check or argument parsing procedures fail then the hooks are not called.
bot.after_invoke⚓︎
A decorator that registers a coroutine as a post-invoke hook.
@bot.after_invoke
async def handler(ctx: commands.Context):
print(f"Command '{ctx.command.name}' is finished")
Per-cog apply⚓︎
Adding a check on each command inside the cog
cog_check⚓︎
A special method that is registered as a commands.check() for every prefix command and subcommand in this cog.
interaction_check⚓︎
A special method that is registered as a app_commands.check() for every slash command and subcommand in this cog.
Handling check failures⚓︎
When an error inside check happens, the error is propagated to the error handlers.
If you don't raise an exception but return false-like value, then it will get wrapped up into a CheckFailure exception.
Tip
Check out Error Handling page for more examples and explanations about error handling
This is an example of how you can handle check failure for a single command
class CustomException(commands.CommandError): ...
async def check(ctx: commands.Context):
if "1" in ctx.message.content:
raise CustomException()
if "2" in ctx.message.content:
return False
return True
@commands.check(check)
@bot.command()
async def foo(ctx: commands.Context):
await ctx.send("Success!")
@foo.error
async def handler(ctx: commands.Context, error: commands.CommandError):
if isinstance(error, CustomException):
await ctx.send("CustomException was raised inside check!")
elif isinstance(error, commands.CheckFailure):
await ctx.send("Check has failed!")
else:
await ctx.send(f"Got unexpected error: {error}")
Note
This error handler is used here for further demonstration
Built-in checks⚓︎
You may view all of discord.py's relevant checks in the documentation.
Roles⚓︎
has_role⚓︎
Checks if the member invoking the command has the role specified via the name or ID specified.
has_any_role⚓︎
Similar to has_role, but takes unspecified amount of argument and returns True if the member invoking the command has any of the roles specified
bot_has_role⚓︎
Similar to has_role except checks if the bot itself has the role.
bot_has_any_role⚓︎
Similar to has_any_role except checks if the bot itself has the role.
Permissions⚓︎
List of existing permissions
They are attributes of discord.Permissions
add_reactions
, administrator
, attach_files
, ban_members
, change_nickname
, connect
, create_instant_invite
, create_private_threads
, create_public_threads
, deafen_members
, embed_links
, external_emojis
, external_stickers
, kick_members
, manage_channels
, manage_emojis
, manage_emojis_and_stickers
, manage_events
, manage_guild
, manage_messages
, manage_nicknames
, manage_permissions
, manage_roles
, manage_threads
, manage_webhooks
, mention_everyone
, moderate_members
, move_members
, mute_members
, priority_speaker
, read_message_history
, read_messages
, request_to_speak
, send_messages
, send_messages_in_threads
, send_tts_messages
, speak
, stream
, use_application_commands
, use_embedded_activities
, use_external_emojis
, use_external_stickers
, use_voice_activation
, view_audit_log
, view_channel
, view_guild_insights
has_permissions⚓︎
Checks if the member has all of the permissions necessary.
Note
This check operates on the current channel permissions, not the guild wide permissions
has_guild_permissions⚓︎
Similar to has_permissions, but operates on guild wide permissions instead of the current channel permissions.
bot_has_permissions⚓︎
Similar to has_permissions except checks if the bot itself has the permissions listed.
bot_has_guild_permissions⚓︎
Similar to has_guild_permissions except checks if the bot itself has the permissions listed.
Channel⚓︎
dm_only⚓︎
Checks if command is invoked inside a DM
- There is no such check for application commands built-in.
guild_only⚓︎
Checks if command is invoked inside a guild
- There is no such check for application commands built-in.
def guild_only(interaction):
return interaction.guild is not None
@bot.tree.command()
@app_commands.check(guild_only)
async def foo(interaction: discord.Interaction):
await interaction.response.send_message(f"Success!")
Tip
You can use app_commands.guild_only() instead of check
@bot.tree.command()
@discord.app_commands.guild_only()
async def foo(interaction: discord.Interaction):
await interaction.response.send_message(f"Success!")
With it everything will be handled by discord itself
is_nsfw⚓︎
Checks if the channel is a NSFW channel.
Person⚓︎
is_owner⚓︎
Checks if the person invoking this command is the owner of the bot.
This is powered by Bot.is_owner().
- There is no such check for application commands built-in.
Hooks⚓︎
before_invoke⚓︎
Registers a coroutine as a pre-invoke hook.
after_invoke⚓︎
Registers a coroutine as a post-invoke hook.
Cooldowns⚓︎
A cooldown allows a command to only be used a specific amount of times in a specific time frame. These cooldowns can be based either on a per-guild,
per-channel, per-user, per-role or global basis. Denoted by the third argument of type
which must be of enum
type BucketType.
cooldown⚓︎
Adds a cooldown to a Command
dynamic_cooldown⚓︎
Adds a dynamic cooldown to a Command
This differs from cooldown in that it takes a function that accepts a single parameter of
type Context (Interaction for slash) and must return
a Cooldown or None
. If None
is returned then that cooldown
is effectively bypassed.
def cooldown(ctx: commands.Context):
"""A cooldown for 10 seconds for everyone except listed users"""
if ctx.author.id in (656919778572632094, 703327554936766554):
return
return commands.Cooldown(1, 10)
@bot.command()
@commands.dynamic_cooldown(cooldown, commands.BucketType.user)
async def foo(ctx: commands.Context):
await ctx.send("Success!")
def cooldown(interaction: discord.Interaction):
"""A cooldown for 10 seconds for everyone except listed users"""
if interaction.author.id in (656919778572632094, 703327554936766554):
return
return app_commands.Cooldown(1, 10)
@bot.tree.command()
@app_commands.checks.dynamic_cooldown(cooldown, commands.BucketType.user)
async def foo(interaction: discord.Interaction):
await interaction.response.send_message("Success!")
def cooldown(ctx: commands.Context):
"""A cooldown for 10 seconds for everyone except listed users"""
if ctx.author.id in (656919778572632094, 703327554936766554):
return
return commands.Cooldown(1, 10)
@bot.hybrid_command()
@commands.dynamic_cooldown(cooldown, commands.BucketType.user)
async def foo(ctx: commands.Context):
await ctx.send("Success!")
max_concurrency⚓︎
Adds a maximum concurrency to a Command
This enables you to only allow a certain number of command invocations at the same time, for example if a command takes too long or if only one user can use it at a time. This differs from a cooldown in that there is no set waiting period or token bucket – only a set number of people can run the command.
Custom Checks⚓︎
Creating a new check⚓︎
is_me⚓︎
Creating a basic check to see if the command invoker is you.
def check_if_it_is_me(ctx):
return ctx.message.author.id == 85309593344815104
@bot.command()
@commands.check(check_if_it_is_me)
async def only_for_me(ctx):
await ctx.send('I know you!')
safe_content⚓︎
Checks if there are no banned words in the command's message content
banwords = {"rabbit", "horse"}
async def safe_content(ctx):
return not (set(ctx.message.content.lower().split()) & banwords)
@bot.command()
@commands.check(safe_content)
async def check_content(ctx):
await ctx.send("Content is clean!")
Extending existing checks⚓︎
A special attribute named predicate
is bound to the value returned
by commands.check decorator to retrieve the predicate passed to
the decorator.
def owner_or_permissions(**perms):
original = commands.has_permissions(**perms).predicate
async def extended_check(ctx):
if ctx.guild is None:
return False
return ctx.guild.owner_id == ctx.author.id or await original(ctx)
return commands.check(extended_check)
This will create a check that uses commands.has_permissions
with our custom check together to determine whether the user is the guild owner or he has the
required permissions.