From 007ab7ca51e644b898c9d30afee622ed437539cb Mon Sep 17 00:00:00 2001 From: Kaylynn Morgan <51037748+kaylynn234@users.noreply.github.com> Date: Tue, 1 Mar 2022 17:00:29 +1100 Subject: [PATCH 01/21] Respect the alignment specified by the image directive --- docs/_static/style.css | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/_static/style.css b/docs/_static/style.css index e1d218b30816..eec62eb3800d 100644 --- a/docs/_static/style.css +++ b/docs/_static/style.css @@ -1147,6 +1147,15 @@ table.docutils tbody tr td ol.last { margin-bottom: 0; } +/* added when the `align` attribute is specified in the `image` directive */ +main img.align-left { + margin-left: .5em; +} + +main img.align-right { + margin-right: .5em; +} + .align-default { text-align: left !important; } From 9df753c6278d701b613c21686855727ceb4cf95f Mon Sep 17 00:00:00 2001 From: Heroicos_HM Date: Fri, 29 Apr 2022 04:21:58 -0700 Subject: [PATCH 02/21] Added context_menus.rst --- docs/interactions/context_menus.rst | 71 +++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 docs/interactions/context_menus.rst diff --git a/docs/interactions/context_menus.rst b/docs/interactions/context_menus.rst new file mode 100644 index 000000000000..6113acbc5891 --- /dev/null +++ b/docs/interactions/context_menus.rst @@ -0,0 +1,71 @@ +.. currentmodule:: discord + +.. _ext_commands_context-menus: + +Context Menus +============== + +Context menus allow for commands to be triggered through a "context menu" upon right clicking a related object, then selecting the name of the command to run from the ``Apps`` menu. + +- Context menus can be categorized into two separate types, one for :class:`.User` and :class:`.Member` objects, and one for :class:`.Message` objects. +- The command does not have any arguments, and will return the target object. + + +Basic Usage +~~~~~~~~~~~~ + +.. code-block:: python3 + + @app_commands.context_menu(name="User Context") + async def example(interaction: discord.Interaction, user: Union[discord.User, discord.Member]): + await interaction.response.send_message(f"Interacting with user {user}.", ephemeral=True) + + @app_commands.context_menu(name="Message Context") + async def example(interaction: discord.Interaction, message: discord.Message): + await interaction.response.send_message(f"Interacting with message {message.content}.", ephemeral=True) + +- The ``name`` argument of the :func:`@context_menu <.app_commands.context_menu>` decorator defines the name that appears in the context menu. +- The second argument can be of type :class:`.User`, :class:`.Member`, or Union[:class:`.User`, :class:`.Member`] for user context menus. +- Likewise, using the second argument of type :class:`.Message` will apply to message context menus. +- Any messages sent to the :attr:`~.Interaction.response` attribute of the :class:`.Interaction` will appear in the channel that the user of the context menu currently has open. +- The profile image of the bot which owns a particular command will appear next to the command in the context menu. + +The ``User Context`` menu produces an option that looks like the following: + +.. image:: /images/guide/interactions/user_context_menu.png + +The ``Message Context`` menu produces an option that looks like the following: + +.. image:: /images/guide/interactions/message_context_menu.png + +Checks +~~~~~~~ + +One of the most command current uses of context menus is to implement a variety of extended moderation features. Some ideas might include a ``Toggle Mute``, ``Warn``, ``Report``, or ``Info`` commands. + +For implementing moderation commands like these, it will likely be useful to add checks in order to ensure that only those who are allowed to can use the moderation commands. + +Examples might include ensuring that a user has administrator privileges: + +.. code-block:: python3 + + @app_commands.context_menu(name=...) + @app_commands.default_permissions(administrator=True) + async def example(...): + ... + +Or checking for a specific role, or group of roles: + +.. code-block:: python3 + + # Single Role Check + @app_commands.context_menu(name=...) + @app_commands.has_role('Moderator') + async def example(...): + ... + + # Multiple Role Check + @app_commands.context_menu(name=...) + @app_commands.has_any_role('Moderator', 1234567890) + async def example(...): + ... \ No newline at end of file From 094af9762767b691d88e1e8743376b22b02a65e4 Mon Sep 17 00:00:00 2001 From: Heroicos_HM Date: Fri, 29 Apr 2022 04:25:23 -0700 Subject: [PATCH 03/21] Add images --- .../guide/interactions/message_context_menu.png | Bin 0 -> 28813 bytes .../guide/interactions/user_context_menu.png | Bin 0 -> 26578 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/images/guide/interactions/message_context_menu.png create mode 100644 docs/images/guide/interactions/user_context_menu.png diff --git a/docs/images/guide/interactions/message_context_menu.png b/docs/images/guide/interactions/message_context_menu.png new file mode 100644 index 0000000000000000000000000000000000000000..ad0928063990f2853901cc2ff213a5382c4715e2 GIT binary patch literal 28813 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
    M;<+d_%M?f2#Rnizj?Pr$^VM@?9Ray)f~r=GFc+ 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 AWB>pF literal 0 HcmV?d00001 diff --git a/docs/images/guide/interactions/user_context_menu.png b/docs/images/guide/interactions/user_context_menu.png new file mode 100644 index 0000000000000000000000000000000000000000..1c1994b6c955c0bb22ce9783c89ce1b0b070e016 GIT binary patch literal 26578 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 PJ2)yzn$QYGtLOg%&?7QN literal 0 HcmV?d00001 From a9d6e5a3b15e11aa5bc9377854686ac5fcb539bd Mon Sep 17 00:00:00 2001 From: Heroicos_HM Date: Fri, 29 Apr 2022 04:28:58 -0700 Subject: [PATCH 04/21] Added context_menus.rst --- docs/interactions/context_menus.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/interactions/context_menus.rst b/docs/interactions/context_menus.rst index 6113acbc5891..58b999726ae3 100644 --- a/docs/interactions/context_menus.rst +++ b/docs/interactions/context_menus.rst @@ -38,6 +38,10 @@ The ``Message Context`` menu produces an option that looks like the following: .. image:: /images/guide/interactions/message_context_menu.png + +.. + TODO: Move checks into separate guide. + Checks ~~~~~~~ From d25cd551723fcde1f4e58c67e36a9efdb0db6db0 Mon Sep 17 00:00:00 2001 From: Heroicos_HM Date: Fri, 29 Apr 2022 04:36:27 -0700 Subject: [PATCH 05/21] Fixed checks syntax --- docs/interactions/context_menus.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/interactions/context_menus.rst b/docs/interactions/context_menus.rst index 58b999726ae3..0b8d653f3301 100644 --- a/docs/interactions/context_menus.rst +++ b/docs/interactions/context_menus.rst @@ -54,7 +54,7 @@ Examples might include ensuring that a user has administrator privileges: .. code-block:: python3 @app_commands.context_menu(name=...) - @app_commands.default_permissions(administrator=True) + @app_commands.checks.has_permissions(administrator=True) async def example(...): ... @@ -64,12 +64,12 @@ Or checking for a specific role, or group of roles: # Single Role Check @app_commands.context_menu(name=...) - @app_commands.has_role('Moderator') + @app_commands.checks.has_role('Moderator') async def example(...): ... # Multiple Role Check @app_commands.context_menu(name=...) - @app_commands.has_any_role('Moderator', 1234567890) + @app_commands.checks.has_any_role('Moderator', 1234567890) async def example(...): ... \ No newline at end of file From ac2c81a65316c2c8af6697f62e5bc70bc1090d2a Mon Sep 17 00:00:00 2001 From: Heroicos_HM Date: Fri, 29 Apr 2022 17:39:09 -0700 Subject: [PATCH 06/21] Updated typos, moved checks guide to separate page. --- docs/interactions/context_menus.rst | 37 +++++------------------------ 1 file changed, 6 insertions(+), 31 deletions(-) diff --git a/docs/interactions/context_menus.rst b/docs/interactions/context_menus.rst index 0b8d653f3301..2c537e812e61 100644 --- a/docs/interactions/context_menus.rst +++ b/docs/interactions/context_menus.rst @@ -1,14 +1,14 @@ .. currentmodule:: discord -.. _ext_commands_context-menus: +.. _guide_interactions_context-menus: Context Menus ============== -Context menus allow for commands to be triggered through a "context menu" upon right clicking a related object, then selecting the name of the command to run from the ``Apps`` menu. +Context menus allow for commands to be triggered through a ``context menu`` upon right clicking a related object, then selecting the name of the command to run from the ``Apps`` menu. - Context menus can be categorized into two separate types, one for :class:`.User` and :class:`.Member` objects, and one for :class:`.Message` objects. -- The command does not have any arguments, and will return the target object. +- The command does not have any arguments, and will simply return the target object. Basic Usage @@ -24,7 +24,7 @@ Basic Usage async def example(interaction: discord.Interaction, message: discord.Message): await interaction.response.send_message(f"Interacting with message {message.content}.", ephemeral=True) -- The ``name`` argument of the :func:`@context_menu <.app_commands.context_menu>` decorator defines the name that appears in the context menu. +- The ``name`` argument of the :func:`@context_menu <.app_commands.context_menu>` decorator defines the command name that appears in the context menu. - The second argument can be of type :class:`.User`, :class:`.Member`, or Union[:class:`.User`, :class:`.Member`] for user context menus. - Likewise, using the second argument of type :class:`.Message` will apply to message context menus. - Any messages sent to the :attr:`~.Interaction.response` attribute of the :class:`.Interaction` will appear in the channel that the user of the context menu currently has open. @@ -45,31 +45,6 @@ The ``Message Context`` menu produces an option that looks like the following: Checks ~~~~~~~ -One of the most command current uses of context menus is to implement a variety of extended moderation features. Some ideas might include a ``Toggle Mute``, ``Warn``, ``Report``, or ``Info`` commands. +One of the most common current uses of context menus is to implement a variety of extended moderation features. Some ideas might include a ``Toggle Mute``, ``Warn``, ``Report``, or ``Info`` commands. For implementing moderation commands like these, it will likely be useful to add checks in order to ensure that only those who are allowed to can use the moderation commands. -For implementing moderation commands like these, it will likely be useful to add checks in order to ensure that only those who are allowed to can use the moderation commands. - -Examples might include ensuring that a user has administrator privileges: - -.. code-block:: python3 - - @app_commands.context_menu(name=...) - @app_commands.checks.has_permissions(administrator=True) - async def example(...): - ... - -Or checking for a specific role, or group of roles: - -.. code-block:: python3 - - # Single Role Check - @app_commands.context_menu(name=...) - @app_commands.checks.has_role('Moderator') - async def example(...): - ... - - # Multiple Role Check - @app_commands.context_menu(name=...) - @app_commands.checks.has_any_role('Moderator', 1234567890) - async def example(...): - ... \ No newline at end of file +For examples of checks, view the :ref:`interaction checks guide `. \ No newline at end of file From c6e8da22896ce5c119f28838017b7c343d02d7f2 Mon Sep 17 00:00:00 2001 From: Heroicos_HM Date: Fri, 29 Apr 2022 17:39:28 -0700 Subject: [PATCH 07/21] Add checks guide --- docs/interactions/checks.rst | 182 +++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 docs/interactions/checks.rst diff --git a/docs/interactions/checks.rst b/docs/interactions/checks.rst new file mode 100644 index 000000000000..df405667d9f4 --- /dev/null +++ b/docs/interactions/checks.rst @@ -0,0 +1,182 @@ +.. currentmodule:: discord + +.. _guide_interactions_checks: + +Checks +======= + +Checks are a feature that allows you to set requirements in order for a command to be executed. This can include things like requiring administrator permissions, requiring a user to have a role, making sure that the bot has necessary permissions to execute a task, adding a cooldown to a command, creating a custom check function, or any combination of these. + +These checks will be used as decorators in conjunction with any :func:`@app_commands.command <.app_commands.command>` or :func:`@app_commands.context_menu <.app_commands.context_menu>` decorators. + +Before getting started, the examples shown below are made using both :class:`.app_commands.Command` and :class:`.app_commands.ContextMenu` methods. All of the checks present here can apply to either one. Let's take a look at each of the possible checks. + +Role Checks +------------ + +These checks allow a simple way to ensure that the user triggering a command has a role, or any role from a set of roles. + +.. code-block:: python3 + :emphasize-lines: 2,7 + + # Single role check. + @app_commands.command() + @app_commands.checks.has_role('Role Name') + async def single_role_check(interaction: discord.Interaction): + await interaction.response.send_message('You have the role!', ephemeral=True) + + # Any role from set of roles check. + @app_commands.context_menu() + @app_commands.checks.has_any_role('Role Name', 1234567890, 'AnotherOne') + async def multiple_role_check(interaction: discord.Interaction, user: discord.User): + await interaction.response.send_message('You have all the roles!', ephemeral=True) + +Let's take a quick look through the code here: + +- First, a simple :class:`.app_commands.Command` is registered using the :func:`@app_commands.command() <.app_commands.command>` decorator. +- Then, a :func:`@has_role <.app_commands.checks.has_role>` check is added to ensure that a user has a given role. This role can either be a :class:`str` representing the name of the role, or an :class:`int` representing the id of the role. +- Then, the actual function callback for the command is created. + +A very similar process happens for checking multiple roles: + +- First, a :class:`.app_commands.ContextMenu` is registered using the :func:`@app_commands.context_menu() <.app_commands.context_menu>` decorator. +- Then, a :func:`@has_role <.app_commands.checks.has_any_role>` is added for multiple roles. Again, these roles are either :class:`str` or :class:`int` values. +- Then, the callback for the context menu is created. + +Permission Checks +------------------ + +These checks handle validating whether the user activating a command has certain permissions in a server, or whether the bot user has a specific permission. + +.. code-block:: python3 + :emphasize-lines: 3,9 + + # User permissions check. + @app_commands.command() + @app_commands.has_permissions(administrator=True) + async def user_permission_check(interaction: discord.Interaction): + await interaction.response.send_message('Hello administrator!', ephemeral=True) + + # Bot permissions check. + @app_commands.command() + @app_commands.bot_has_permissions(delete_messages=True, manage_guild=True) + async def bot_permission_check(interaction: discord.Interaction): + await interaction.response.send_message('I can do that!', ephemeral=True) + +These two checks are identical in usage. The only difference is that one checks the permissions of the user who calls the command, and one checks the permissions of the bot user the command is issued to. + +The permissions given to the checks can be any number of permissions, which can be found in :class:`.Permissions`, and must have a boolean value. + +Cooldown Checks +---------------- + +It is also possible to use a check decorator that will directly attach a cooldown to your command. + +There are two cooldown methods, static and dynamic. + +Static Cooldown +~~~~~~~~~~~~~~~~ + +Static cooldowns are methods which limit a command to a certain number of uses per time frame. + +.. code-block:: python3 + :emphasize-lines: 2 + + @app_commands.command() + @app_commands.checks.cooldown(1, 5.0, key=lambda interaction: (i.guild_id, i.user.id)) + async def cooldown_check(interaction: discord.Interaction): + await interaction.response.send_message('Not on cooldown yet!', ephemeral=True) + +The :func:`@cooldown <.app_commands.checks.cooldown>` decorator takes 3 possible arguments: + +- The ``rate`` parameter first takes an integer value. This value is the number of usages that can be used within a time frame before the cooldown is activated. +- The ``per`` parameter then takes a float value. This is the number of seconds to wait for a cooldown once it has been activated. +- The third and final parameter, ``key``, can be used to specify what criteria are used for applying the cooldown. This is given as a function that takes a :class:`.Interaction` as an argument, and can be a coroutine. + +Now that we know what the parameters for a static cooldown are, let's take a closer look at the shown example: + +- The ``rate`` is set to ``1``, so the cooldown will trigger every time the command is used. +- The ``per`` param is set to ``5.0``, so the cooldown will lock the command for 5 seconds when it is used. +- The ``key`` parameter has a ``lambda`` function which accepts the :class:`.Interaction`, and then returns a tuple for the :attr:`~.Interaction.guild_id` and :attr:`user.id <.Interaction.user>` values. This means that the cooldown will be put in effect for each user individually, in each server. + +Putting it all together: an individual user can use the command once every five seconds in any one guild. This means that if they were to then proceed to use the command in another guild, the cooldown from the first guild would not be applied. + +.. note:: + + By default, the "key" parameter will operate on the :class:`.User` level. So if no "key" parameter is specified, the cooldown will be applied per user, globally. Similarly, if the "key" parameter is set to "None", then the cooldown is considered a "global" cooldown for all users and guilds. + +Dynamic Cooldown +~~~~~~~~~~~~~~~~~ + +Dynamic cooldowns allow you to register a custom handler for cooldown checks. + +.. code-block:: python3 + :emphasize-lines: 10 + + def cooldown_skip_owner(interaction: discord.Interaction) -> Optional[app_commands.Cooldown]: + if interactions.user.id == 1234567890: + app_commands.Cooldown(1, 5.0) + if interactions.guild is not None and interactions.guild.owner_id == interactions.user.id: + return None + + return app_commands.Cooldown(1, 10.0) + + @app_commands.command() + @app_commands.checks.dynamic_cooldown(cooldown_skip_owner) + async def dynamic_cooldown_check(interaction: discord.Interaction): + await interaction.response.send_message('Not on cooldown!', ephemeral=True) + +The :func:`@dynamic_cooldown <.app_commands.checks.dynamic_cooldown>` is passed a reference to a function, which can be used to control when to apply a different cooldown to specific use-cases, or ignore it all together. + +In this specific use case, the function is used to apply a ``10.0`` second per usage cooldown to each individual user. However, if the user is the owner of the guild the command is used in, the command cooldown is bypassed. Similarly, if the user is a specific user mentioned by id, a lesser cooldown is applied, of just ``5.0`` seconds per usage. + +Custom Check +------------- + +Custom check commands can also be implemented if further functionality is needed outside of the checks listed above. These will generally come in two forms: a standard check decorator that is passe a custom function, or a custom decorator for common checks. + +.. code-block:: python3 + :emphasize-lines: 6,17 + + # Custom check function. + def check_if_owner(interaction: discord.Interaction) -> bool: + return interaction.guild is not None and interaction.user.id == interaction.guild.owner_id + + @app_commands.command() + @app_commands.check(check_if_owner) + async def owners_only(interaction: discord.Interaction): + await interaction.response.send_message('Hello boss!') + + # Custom check decorator. + def is_me(): + def predicate(interaction: discord.Interaction) -> bool: + return interaction.user.id == 1234567890 + return app_commands.check(predicate) + + @app_commands.command() + @is_me() + async def me_only(interaction: discord.Interaction): + await interaction.response.send_message('I know you!', ephemeral=True) + +In this example, the first check is implemented to only allow the command usage if the user who runs the command is the owner of the guild the command is used in. This is simply a function which takes a :class:`.Interaction` as an argument, which is passed to a :func:`@check <.app_commands.check>` decorator. The second check implemented creates a custom decorator which checks if the user who activates a command has a specific id. This limits a command to only one user. + +Combining Checks +----------------- + +You can combine multiple checks on a command to end up with a vary particular functionality for your needs, in a very concise manner. + +As an example, here is a command which only runs if a user has a certain role, ``send_messages`` permissions, the bot has ``manage_messages`` permissions, and with a ``15`` second global cooldown: + +.. code-block:: python3 + + @app_commands.command() + @app_commands.checks.has_role('Necessary Role') + @app_commands.checks.has_permissions(send_messages=True) + @app_commands.checks.bot_has_permissions(manage_messages=True) + @app_commands.checks.cooldown(1, 15.0, key=None) + async def check_example(interaction: discord.Interaction): + await interaction.send_message('Checks passed!', ephemeral=True) + +.. note:: + + When checks are failed, they will throw an error. By implementing custom error handling for these errors, you can create a system that will do something different when a check is failed. See the :ref:`error handling guide ` for further information on this. \ No newline at end of file From fa8af01a704f126ee24f437059f7155ea0838a28 Mon Sep 17 00:00:00 2001 From: Arya Date: Tue, 7 Jun 2022 19:28:06 -0700 Subject: [PATCH 08/21] Whitespace fix Co-authored-by: GoogleGenius <81032623+GoogleGenius@users.noreply.github.com> --- docs/interactions/checks.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/interactions/checks.rst b/docs/interactions/checks.rst index df405667d9f4..6af41a68a365 100644 --- a/docs/interactions/checks.rst +++ b/docs/interactions/checks.rst @@ -7,7 +7,7 @@ Checks Checks are a feature that allows you to set requirements in order for a command to be executed. This can include things like requiring administrator permissions, requiring a user to have a role, making sure that the bot has necessary permissions to execute a task, adding a cooldown to a command, creating a custom check function, or any combination of these. -These checks will be used as decorators in conjunction with any :func:`@app_commands.command <.app_commands.command>` or :func:`@app_commands.context_menu <.app_commands.context_menu>` decorators. +These checks will be used as decorators in conjunction with any :func:`@app_commands.command <.app_commands.command>` or :func:`@app_commands.context_menu <.app_commands.context_menu>` decorators. Before getting started, the examples shown below are made using both :class:`.app_commands.Command` and :class:`.app_commands.ContextMenu` methods. All of the checks present here can apply to either one. Let's take a look at each of the possible checks. From 5803e4fc27f281be7f1b597decd5598e447ecdbb Mon Sep 17 00:00:00 2001 From: Arya Date: Tue, 7 Jun 2022 19:28:23 -0700 Subject: [PATCH 09/21] Grammar update Co-authored-by: GoogleGenius <81032623+GoogleGenius@users.noreply.github.com> --- docs/interactions/checks.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/interactions/checks.rst b/docs/interactions/checks.rst index 6af41a68a365..8fdba3d4bf6f 100644 --- a/docs/interactions/checks.rst +++ b/docs/interactions/checks.rst @@ -46,7 +46,7 @@ A very similar process happens for checking multiple roles: Permission Checks ------------------ -These checks handle validating whether the user activating a command has certain permissions in a server, or whether the bot user has a specific permission. +These checks handle validating whether the user activating a command has certain permissions in a server, or whether the bot user has specific permissions. .. code-block:: python3 :emphasize-lines: 3,9 From b0f2a273c15066bda0d6c92ff87a105b70b24652 Mon Sep 17 00:00:00 2001 From: Arya Date: Tue, 7 Jun 2022 19:28:31 -0700 Subject: [PATCH 10/21] Typo fix Co-authored-by: GoogleGenius <81032623+GoogleGenius@users.noreply.github.com> --- docs/interactions/checks.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/interactions/checks.rst b/docs/interactions/checks.rst index 8fdba3d4bf6f..c9ad2e9cc827 100644 --- a/docs/interactions/checks.rst +++ b/docs/interactions/checks.rst @@ -133,7 +133,7 @@ In this specific use case, the function is used to apply a ``10.0`` second per u Custom Check ------------- -Custom check commands can also be implemented if further functionality is needed outside of the checks listed above. These will generally come in two forms: a standard check decorator that is passe a custom function, or a custom decorator for common checks. +Custom check commands can also be implemented if further functionality is needed outside of the checks listed above. These will generally come in two forms: a standard check decorator that is passed a custom function, or a custom decorator for common checks. .. code-block:: python3 :emphasize-lines: 6,17 From 216745804e43de18f7084dfbb68c559f9c64b26d Mon Sep 17 00:00:00 2001 From: Arya Date: Tue, 7 Jun 2022 19:28:42 -0700 Subject: [PATCH 11/21] Typo fix Co-authored-by: GoogleGenius <81032623+GoogleGenius@users.noreply.github.com> --- docs/interactions/checks.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/interactions/checks.rst b/docs/interactions/checks.rst index c9ad2e9cc827..26113e38fa52 100644 --- a/docs/interactions/checks.rst +++ b/docs/interactions/checks.rst @@ -163,7 +163,7 @@ In this example, the first check is implemented to only allow the command usage Combining Checks ----------------- -You can combine multiple checks on a command to end up with a vary particular functionality for your needs, in a very concise manner. +You can combine multiple checks on a command to end up with a very particular functionality for your needs, in a very concise manner. As an example, here is a command which only runs if a user has a certain role, ``send_messages`` permissions, the bot has ``manage_messages`` permissions, and with a ``15`` second global cooldown: From c916713e350153113bf54f5d4a824109bbf4d745 Mon Sep 17 00:00:00 2001 From: Heroicos_HM Date: Mon, 11 Sep 2023 00:10:14 -0700 Subject: [PATCH 12/21] Added orphan tag and started updating grammatical fixes. --- docs/{ => guide}/interactions/checks.rst | 6 ++++-- docs/{ => guide}/interactions/context_menus.rst | 16 +++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) rename docs/{ => guide}/interactions/checks.rst (96%) rename docs/{ => guide}/interactions/context_menus.rst (78%) diff --git a/docs/interactions/checks.rst b/docs/guide/interactions/checks.rst similarity index 96% rename from docs/interactions/checks.rst rename to docs/guide/interactions/checks.rst index 26113e38fa52..877c11907f96 100644 --- a/docs/interactions/checks.rst +++ b/docs/guide/interactions/checks.rst @@ -1,3 +1,5 @@ +:orphan: + .. currentmodule:: discord .. _guide_interactions_checks: @@ -115,7 +117,7 @@ Dynamic cooldowns allow you to register a custom handler for cooldown checks. def cooldown_skip_owner(interaction: discord.Interaction) -> Optional[app_commands.Cooldown]: if interactions.user.id == 1234567890: - app_commands.Cooldown(1, 5.0) + return app_commands.Cooldown(1, 5.0) if interactions.guild is not None and interactions.guild.owner_id == interactions.user.id: return None @@ -179,4 +181,4 @@ As an example, here is a command which only runs if a user has a certain role, ` .. note:: - When checks are failed, they will throw an error. By implementing custom error handling for these errors, you can create a system that will do something different when a check is failed. See the :ref:`error handling guide ` for further information on this. \ No newline at end of file + When checks do not pass, they will throw an error. By implementing custom error handling for these errors, you can create a system that will do something different when a check is failed. See the :ref:`error handling guide ` for further information on this. \ No newline at end of file diff --git a/docs/interactions/context_menus.rst b/docs/guide/interactions/context_menus.rst similarity index 78% rename from docs/interactions/context_menus.rst rename to docs/guide/interactions/context_menus.rst index 2c537e812e61..f66836b0422b 100644 --- a/docs/interactions/context_menus.rst +++ b/docs/guide/interactions/context_menus.rst @@ -1,3 +1,5 @@ +:orphan: + .. currentmodule:: discord .. _guide_interactions_context-menus: @@ -5,7 +7,7 @@ Context Menus ============== -Context menus allow for commands to be triggered through a ``context menu`` upon right clicking a related object, then selecting the name of the command to run from the ``Apps`` menu. +Context menus allow for commands to be triggered through a context menu upon right clicking a related object, then selecting the name of the command to run from the ``Apps`` menu. - Context menus can be categorized into two separate types, one for :class:`.User` and :class:`.Member` objects, and one for :class:`.Message` objects. - The command does not have any arguments, and will simply return the target object. @@ -25,23 +27,19 @@ Basic Usage await interaction.response.send_message(f"Interacting with message {message.content}.", ephemeral=True) - The ``name`` argument of the :func:`@context_menu <.app_commands.context_menu>` decorator defines the command name that appears in the context menu. -- The second argument can be of type :class:`.User`, :class:`.Member`, or Union[:class:`.User`, :class:`.Member`] for user context menus. -- Likewise, using the second argument of type :class:`.Message` will apply to message context menus. +- The second argument can be of type :class:`.User`, :class:`.Member`, or :class:`.User` | :class:`.Member` for user context menus. +- Likewise, for message context menus, the second argument would be of type :class:`.Message`. - Any messages sent to the :attr:`~.Interaction.response` attribute of the :class:`.Interaction` will appear in the channel that the user of the context menu currently has open. - The profile image of the bot which owns a particular command will appear next to the command in the context menu. -The ``User Context`` menu produces an option that looks like the following: +The User Context Menu produces an option that looks like the following: .. image:: /images/guide/interactions/user_context_menu.png -The ``Message Context`` menu produces an option that looks like the following: +The Message Context Menu produces an option that looks like the following: .. image:: /images/guide/interactions/message_context_menu.png - -.. - TODO: Move checks into separate guide. - Checks ~~~~~~~ From 7f2bae750c0062b49b059da94c0d5633e2399a94 Mon Sep 17 00:00:00 2001 From: Heroicos_HM Date: Mon, 11 Sep 2023 09:55:59 -0700 Subject: [PATCH 13/21] Updated some wording and content structure --- docs/guide/interactions/checks.rst | 17 ++++++++++++++--- docs/guide/interactions/context_menus.rst | 19 +++++++++---------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/docs/guide/interactions/checks.rst b/docs/guide/interactions/checks.rst index 877c11907f96..36159d3c77b3 100644 --- a/docs/guide/interactions/checks.rst +++ b/docs/guide/interactions/checks.rst @@ -4,8 +4,8 @@ .. _guide_interactions_checks: -Checks -======= +Interaction Checks Guide +========================= Checks are a feature that allows you to set requirements in order for a command to be executed. This can include things like requiring administrator permissions, requiring a user to have a role, making sure that the bot has necessary permissions to execute a task, adding a cooldown to a command, creating a custom check function, or any combination of these. @@ -13,6 +13,14 @@ These checks will be used as decorators in conjunction with any :func:`@app_comm Before getting started, the examples shown below are made using both :class:`.app_commands.Command` and :class:`.app_commands.ContextMenu` methods. All of the checks present here can apply to either one. Let's take a look at each of the possible checks. +.. + TODO: Maybe add a link to the guide for creating commands as well, + but at the time of writing I do not see one to link to. + +.. note:: + + For more information on context menus, see the :ref:`Context Menus Guide `. + Role Checks ------------ @@ -179,6 +187,9 @@ As an example, here is a command which only runs if a user has a certain role, ` async def check_example(interaction: discord.Interaction): await interaction.send_message('Checks passed!', ephemeral=True) +.. + TODO: Add a link to the error handling guide once it has been created. + .. note:: - When checks do not pass, they will throw an error. By implementing custom error handling for these errors, you can create a system that will do something different when a check is failed. See the :ref:`error handling guide ` for further information on this. \ No newline at end of file + When checks do not pass, they will throw an error. By implementing custom error handling for these errors, you can create a system that will do something different when a check is failed. See the :ref:`Error Handling Guide ` for further information on this. \ No newline at end of file diff --git a/docs/guide/interactions/context_menus.rst b/docs/guide/interactions/context_menus.rst index f66836b0422b..dd9129419804 100644 --- a/docs/guide/interactions/context_menus.rst +++ b/docs/guide/interactions/context_menus.rst @@ -4,13 +4,11 @@ .. _guide_interactions_context-menus: -Context Menus -============== +Context Menus Guide +==================== -Context menus allow for commands to be triggered through a context menu upon right clicking a related object, then selecting the name of the command to run from the ``Apps`` menu. - -- Context menus can be categorized into two separate types, one for :class:`.User` and :class:`.Member` objects, and one for :class:`.Message` objects. -- The command does not have any arguments, and will simply return the target object. +Context menus allow for commands to be triggered through a context menu upon right clicking a related object in the Discord UI, +then selecting the name of the command to run from the ``Apps`` menu. Basic Usage @@ -19,17 +17,18 @@ Basic Usage .. code-block:: python3 @app_commands.context_menu(name="User Context") - async def example(interaction: discord.Interaction, user: Union[discord.User, discord.Member]): + async def example(interaction: discord.Interaction, user: discord.User | discord.Member): await interaction.response.send_message(f"Interacting with user {user}.", ephemeral=True) @app_commands.context_menu(name="Message Context") async def example(interaction: discord.Interaction, message: discord.Message): await interaction.response.send_message(f"Interacting with message {message.content}.", ephemeral=True) +- Context menus can be triggered from two possible sources: by right clicking on a user or server member, or by right clicking on a message. - The ``name`` argument of the :func:`@context_menu <.app_commands.context_menu>` decorator defines the command name that appears in the context menu. - The second argument can be of type :class:`.User`, :class:`.Member`, or :class:`.User` | :class:`.Member` for user context menus. - Likewise, for message context menus, the second argument would be of type :class:`.Message`. -- Any messages sent to the :attr:`~.Interaction.response` attribute of the :class:`.Interaction` will appear in the channel that the user of the context menu currently has open. +- Any messages sent to the :attr:`~.Interaction.response` attribute of the :class:`.Interaction` will appear in the channel that the user who activated the context menu currently has open. - The profile image of the bot which owns a particular command will appear next to the command in the context menu. The User Context Menu produces an option that looks like the following: @@ -43,6 +42,6 @@ The Message Context Menu produces an option that looks like the following: Checks ~~~~~~~ -One of the most common current uses of context menus is to implement a variety of extended moderation features. Some ideas might include a ``Toggle Mute``, ``Warn``, ``Report``, or ``Info`` commands. For implementing moderation commands like these, it will likely be useful to add checks in order to ensure that only those who are allowed to can use the moderation commands. +One of the most common current uses of context menus is to implement a variety of extended moderation features. Some ideas might include a ``Toggle Mute``, ``Warn``, ``Report``, or ``Info`` commands. For implementing moderation commands like these, it will be useful to add checks in order to ensure that only those who are allowed to can use said commands. -For examples of checks, view the :ref:`interaction checks guide `. \ No newline at end of file +For examples of checks, view the :ref:`Interaction Checks Guide `. \ No newline at end of file From 76aa478745bd6d5efd1ac2f9955ec681dee92763 Mon Sep 17 00:00:00 2001 From: Heroicos_HM Date: Mon, 11 Sep 2023 11:33:01 -0700 Subject: [PATCH 14/21] Added global interaction check section --- docs/guide/interactions/checks.rst | 44 +++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/docs/guide/interactions/checks.rst b/docs/guide/interactions/checks.rst index 36159d3c77b3..d7a57fcb8695 100644 --- a/docs/guide/interactions/checks.rst +++ b/docs/guide/interactions/checks.rst @@ -187,9 +187,51 @@ As an example, here is a command which only runs if a user has a certain role, ` async def check_example(interaction: discord.Interaction): await interaction.send_message('Checks passed!', ephemeral=True) +Global Interaction Checks +------------------------- + +In addition to adding individual or combined checks onto specific app commands or context menus, it is also possible to create global interaction checks. For these, any interaction +passed through the bot for either app commands or context menus will first run this check. + +In order to implement a global check, we will need to create our own subclass of the :class:`.app_commands.CommandTree` class, and implement the :meth:`.app_commands.CommandTree.interaction_check` method. + +.. code-block:: python3 + + import discord + from discord.ext import commands + + class MyCommandTree(discord.app_commands.CommandTree): + async def interaction_check(self, interaction: discord.Interaction) -> bool: + if interaction.user.id == 1234567890: + return False + + if interaction.guild is None: + return False + + return True + + # Using with a discord.Client implementation + client = discord.Client(...) + tree = MyCommandTree(client) + + # Using with a discord.ext.commands.Bot implementation + bot = commands.Bot(..., tree_cls=MyCommandTree) + + +Alright, let's deconstruct this now: + +- First of all, we create a subclass of :class:`.app_commands.CommandTree` which implements a :meth:`.app_commands.CommandTree.interaction_check` method, which takes our interaction as its sole parameter. +- Next, we can perform any number of logical checks based on the interaction. In this example, the check verifies that the user of the interaction is *not* a specific user ID, and that the interaction is being used in a guild. +- The return value of the interaction determines whether the check is passed. A ``False`` value will fail the check, while a ``True`` value will pass. +- Lastly, we need to tell our bot to use this command tree. + - For a :class:`.Client` implementation that does not use the commands extension, we create the client instance and then instantiate the custom command tree by passing that client in. + - For a :class:`.ext.commands.Bot` implementation, we can pass in our customer command tree class directly when we instantiate the bot. +- After this, any interactions across the entire bot will first have this check run before continuing. + + .. TODO: Add a link to the error handling guide once it has been created. .. note:: - When checks do not pass, they will throw an error. By implementing custom error handling for these errors, you can create a system that will do something different when a check is failed. See the :ref:`Error Handling Guide ` for further information on this. \ No newline at end of file + When checks do not pass, they will throw an error. By implementing custom error handling for these errors, you can create a system that will do something different when a check is failed. See the Error Handling Guide for further information on this. \ No newline at end of file From 0c4fc1e6d046a4c65032646584939ffffa8f163c Mon Sep 17 00:00:00 2001 From: Heroicos_HM Date: Mon, 11 Sep 2023 12:09:12 -0700 Subject: [PATCH 15/21] Added links to other guides --- docs/guide/interactions/checks.rst | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/docs/guide/interactions/checks.rst b/docs/guide/interactions/checks.rst index d7a57fcb8695..e0e411597508 100644 --- a/docs/guide/interactions/checks.rst +++ b/docs/guide/interactions/checks.rst @@ -13,13 +13,9 @@ These checks will be used as decorators in conjunction with any :func:`@app_comm Before getting started, the examples shown below are made using both :class:`.app_commands.Command` and :class:`.app_commands.ContextMenu` methods. All of the checks present here can apply to either one. Let's take a look at each of the possible checks. -.. - TODO: Maybe add a link to the guide for creating commands as well, - but at the time of writing I do not see one to link to. - .. note:: - For more information on context menus, see the :ref:`Context Menus Guide `. + For information on slash commands, see the :ref:`Slash Commands Guide `. For more information on context menus, see the :ref:`Context Menus Guide `. Role Checks ------------ @@ -228,10 +224,8 @@ Alright, let's deconstruct this now: - For a :class:`.ext.commands.Bot` implementation, we can pass in our customer command tree class directly when we instantiate the bot. - After this, any interactions across the entire bot will first have this check run before continuing. - -.. - TODO: Add a link to the error handling guide once it has been created. - .. note:: - When checks do not pass, they will throw an error. By implementing custom error handling for these errors, you can create a system that will do something different when a check is failed. See the Error Handling Guide for further information on this. \ No newline at end of file + When checks do not pass, they will throw an error. By implementing custom error handling for these errors, + you can create a system that will do something different when a check is failed. + See the :ref:`Error Handling Guide ` for further information on this. \ No newline at end of file From d891a48bdf9b0990d88337d560d6ede3beab62f4 Mon Sep 17 00:00:00 2001 From: Heroicos_HM Date: Mon, 11 Sep 2023 15:41:42 -0700 Subject: [PATCH 16/21] Added integration permission guide to checks guide --- docs/guide/interactions/checks.rst | 147 +++++++++++++++++- docs/guide/interactions/context_menus.rst | 2 +- .../interactions/default_permissions_1.png | Bin 0 -> 22835 bytes .../interactions/default_permissions_2.png | Bin 0 -> 6436 bytes 4 files changed, 143 insertions(+), 6 deletions(-) create mode 100644 docs/images/guide/interactions/default_permissions_1.png create mode 100644 docs/images/guide/interactions/default_permissions_2.png diff --git a/docs/guide/interactions/checks.rst b/docs/guide/interactions/checks.rst index e0e411597508..7b2cbcf8660d 100644 --- a/docs/guide/interactions/checks.rst +++ b/docs/guide/interactions/checks.rst @@ -2,7 +2,7 @@ .. currentmodule:: discord -.. _guide_interactions_checks: +.. _guide_interaction_checks: Interaction Checks Guide ========================= @@ -17,6 +17,8 @@ Before getting started, the examples shown below are made using both :class:`.ap For information on slash commands, see the :ref:`Slash Commands Guide `. For more information on context menus, see the :ref:`Context Menus Guide `. +.. _guide_interaction_checks_role-check: + Role Checks ------------ @@ -49,6 +51,8 @@ A very similar process happens for checking multiple roles: - Then, a :func:`@has_role <.app_commands.checks.has_any_role>` is added for multiple roles. Again, these roles are either :class:`str` or :class:`int` values. - Then, the callback for the context menu is created. +.. _guide_interaction_checks_permission-check: + Permission Checks ------------------ @@ -65,13 +69,15 @@ These checks handle validating whether the user activating a command has certain # Bot permissions check. @app_commands.command() - @app_commands.bot_has_permissions(delete_messages=True, manage_guild=True) + @app_commands.bot_has_permissions(manage_messages=True, manage_guild=True) async def bot_permission_check(interaction: discord.Interaction): await interaction.response.send_message('I can do that!', ephemeral=True) These two checks are identical in usage. The only difference is that one checks the permissions of the user who calls the command, and one checks the permissions of the bot user the command is issued to. -The permissions given to the checks can be any number of permissions, which can be found in :class:`.Permissions`, and must have a boolean value. +The permissions given to the check can be any number of permissions, which can be found in :class:`.Permissions`, and must have a boolean value. + +.. _guide_interaction_checks_cooldown-check: Cooldown Checks ---------------- @@ -136,6 +142,8 @@ The :func:`@dynamic_cooldown <.app_commands.checks.dynamic_cooldown>` is passed In this specific use case, the function is used to apply a ``10.0`` second per usage cooldown to each individual user. However, if the user is the owner of the guild the command is used in, the command cooldown is bypassed. Similarly, if the user is a specific user mentioned by id, a lesser cooldown is applied, of just ``5.0`` seconds per usage. +.. _guide_interaction_checks_custom-check: + Custom Check ------------- @@ -166,6 +174,8 @@ Custom check commands can also be implemented if further functionality is needed In this example, the first check is implemented to only allow the command usage if the user who runs the command is the owner of the guild the command is used in. This is simply a function which takes a :class:`.Interaction` as an argument, which is passed to a :func:`@check <.app_commands.check>` decorator. The second check implemented creates a custom decorator which checks if the user who activates a command has a specific id. This limits a command to only one user. +.. _guide_interaction_checks_combining-checks: + Combining Checks ----------------- @@ -183,8 +193,10 @@ As an example, here is a command which only runs if a user has a certain role, ` async def check_example(interaction: discord.Interaction): await interaction.send_message('Checks passed!', ephemeral=True) +.. _guide_interaction_checks_global-checks: + Global Interaction Checks -------------------------- +-------------------------- In addition to adding individual or combined checks onto specific app commands or context menus, it is also possible to create global interaction checks. For these, any interaction passed through the bot for either app commands or context menus will first run this check. @@ -228,4 +240,129 @@ Alright, let's deconstruct this now: When checks do not pass, they will throw an error. By implementing custom error handling for these errors, you can create a system that will do something different when a check is failed. - See the :ref:`Error Handling Guide ` for further information on this. \ No newline at end of file + See the :ref:`Error Handling Guide ` for further information on this. + +.. _guide_integration_permissions: + +Integration Permissions +------------------------ + +In addition to the standard check functionality, there are a few additional functionalities that behave similarly. + +These options include: + +- The :func:`@app_commands.default_permissions() <.app_commands.default_permissions>` decorator. +- The :func:`@app_commands.guild_only() <.app_commands.guild_only>` decorator. +- The ``nsfw`` parameter for :func:`commands <.app_commands.command>` and :func:`context menus <.app_commands.context_menu>`. + +The main difference is that these features are handled by Discord itself, client-side, before the interaction is ever handed off +to the bot to work with. However, the drawback to this is that it is not possible to perform any sort of custom error +handling when these situations arise. + +.. note:: + + If you wish to perform error handling of these kinds of features yourself, + you can implement a :ref:`custom check ` to do this and not utilize these + features. + +.. warning:: + + Due to a limitation of Discord, these features cannot be applied directly to a subcommand. + It must be used on the parent command of a command group. + +.. _guide_integration_permissions_default-perms: + +Default Permissions +~~~~~~~~~~~~~~~~~~~~ + +The :func:`default_permissions <.app_commands.default_permissions>` decorator will set the default permissions necessary +for a user to execute a slash command or interact with a context menu. + +Using this means that a slash command or context menu will by default require the provided permissions. +However, an administrator in the server will be able to override these permissions directly in the Discord client, +without the bot ever being any the wiser. + +.. code-block:: python3 + + @app_commands.command() + @app_commands.default_permissions(administrator=True) + async def default_permissions_slash_command(interaction: discord.Interaction) -> None: + await interaction.response.send_message('Greetings administrator & whoever else was given permission!') + + @app_commands.context_menu(name='User Context') + @app_commands.default_permissions(manage_messages=True, manage_guild=True) + async def default_permissions_slash_command(interaction: discord.Interaction, user: discord.User) -> None: + await interaction.response.send_message('You may or may not have those permissions!' + + +In the first example above, we limit the permissions to use the slash command to administrators only by default. + +.. note:: + + Not passing any parameters to the :func:`default_permissions <.app_commands.default_permissions>` will result + in the default permissions being set to administrator only. + +In the second example, the context menu will be limited to only users with the ``delete_messages`` and ``manage_guild`` +permissions. The provided values can be any number of permissions, which can be found in :class:`.Permissions`, +and must have a boolean value. + +Here is what the context menu above looks like in the Discord client, found inside of the ``Integrations`` section of +the ``Server Settings``. + +.. image:: /images/guide/interactions/default_permissions_1.png + +When you click on the ``View`` button: + +.. image:: /images/guide/interactions/default_permissions_2.png + +As you can see from the first image, there is the option to add specific members, roles, and channels as overrides +to the default permission list. + +.. _guide_integration_permissions_guild-only: + +Guild Only +~~~~~~~~~~~ + +The :func:`guild_only <.app_commands.guild_only>` decorator will mark a slash command or context menu as only being executable within a guild. + + +.. code-block:: python3 + + # Slash Command + @app_commands.command() + @app_commands.guild_only() + async def guild_only_command(interaction: discord.Interaction) -> None: + await interaction.response.send_message('Hello fellow guildmate!') + + # Context Menu + @app_commands.context_menu(name='Greet') + @app_commands.guild_only() + async def guild_only_context(interaction: discord.Interaction, user: discord.Member) -> None: + await interaction.response.send_message(f'{interaction.user.mention} greets {user.mention}!') + + +In this example, both the slash command and the context menu will be unavailable outside of a guild. + + +.. _guide_integration_permissions_nsfw: + +NSFW Only +~~~~~~~~~~ + +The ``nsfw`` parameter is passed directly to the decorator for a slash command or a context menu, and it indicates +whether to limit the availability of the interaction to only NSFW channels. By default this value is ``False``, +allowing the command to be used in all locations. + +.. code-block:: python3 + + # Slash Command + @app_commands.command(nsfw=True) + async def guild_only_command(interaction: discord.Interaction) -> None: + await interaction.response.send_message('This is an NSFW channel!') + + # Context Menu + @app_commands.context_menu(nsfw=True) + async def guild_only_context(interaction: discord.Interaction, user: discord.Member) -> None: + await interaction.response.send_message(f'{user.mention} is in the NSFW channel!') + +Both the slash command and the context menu shown above will only be available in NSFW channels. \ No newline at end of file diff --git a/docs/guide/interactions/context_menus.rst b/docs/guide/interactions/context_menus.rst index dd9129419804..bb6d04aff632 100644 --- a/docs/guide/interactions/context_menus.rst +++ b/docs/guide/interactions/context_menus.rst @@ -44,4 +44,4 @@ Checks One of the most common current uses of context menus is to implement a variety of extended moderation features. Some ideas might include a ``Toggle Mute``, ``Warn``, ``Report``, or ``Info`` commands. For implementing moderation commands like these, it will be useful to add checks in order to ensure that only those who are allowed to can use said commands. -For examples of checks, view the :ref:`Interaction Checks Guide `. \ No newline at end of file +For examples of checks, view the :ref:`Interaction Checks Guide `. \ No newline at end of file diff --git a/docs/images/guide/interactions/default_permissions_1.png b/docs/images/guide/interactions/default_permissions_1.png new file mode 100644 index 0000000000000000000000000000000000000000..eb870339e30f9656d5f5e075839842a3e41a8296 GIT binary patch literal 22835 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 literal 0 HcmV?d00001 diff --git a/docs/images/guide/interactions/default_permissions_2.png b/docs/images/guide/interactions/default_permissions_2.png new file mode 100644 index 0000000000000000000000000000000000000000..58195ee3eaf4c9ee24c374e8527cc5b8134390de GIT binary patch literal 6436 zcmcgwcT|(C(fRGp?Af!szui4&|M|}Mo!s}{`#$%+_kHptU$?Q? zFD@ev008!1y<&O;01#pd-kZck1W$HNmKgx>1M{lsMf-amELsfq==%q4VY@_@2quKc7A8Zbz3(AC2|lYGJyN>N)hR#0M!LkrwV=a zYp#SKo@;8gASU})?iT-~>B*Cw8C6;eu(jlvJ1wr$4H9Zs zyF{;Fd#qiKj~o#{@lybOcU*C``MG-bXT?Pg@+W3yGN-TM7cVFqDtQJiG%W1@)t2Js zWS|hwdzXCszP@S}w@ zgOWFJQln4+PYi7TF_9C;PVo2b&H>BI0(CT_##(%*%C&WVz;+SY&|6RFnud1f{vvdZ zS}74W73!emraS&b;C8eh1M_`9e`a{H@Z6EE6+Z{F;mfDqF0FQojl4qkn6?pkb3Xq* zi~+kbL}b5wPL{fOkn}-YRhL&;3OV#%K!**F>3x}Q-Et9U83E!2yLPa4;?`GJjbJ_p5py55<)?N8%2A}<f6Qw?Hu%rAZNw&pd~!j1!yQ|_k*a^hI*8-^YcPPnBkz5srcLQrq?=E zIdk*f?2(GpZFj0YVSytoI=ogpxS6NPvZEe#?Zlu5t0^bT%LaV2bm{S(i#5P}WWM%C zg#3_^=F3`9h`Rt0S`f#`&XRYSr zW~K*j)jpe{9sauV6R7E99`~`k$6~^*YuL(~?mCU~liDGag*tqCDMac5y1GXZWp)b5 znr|{y&3i-I#PVvTK8fE14FY%Aq2~iSuu4wVPaqM9rb)yq4!HLYR`Q7aJ8J(%o?bxU z+Q~x+fFnkc%*~zKb-bn@&`Am3KvecAuwGcIQqwjrZZnITi{W4SEdlsYAr6uEv zsO}6JNaTFBkOd2S4%ioWP@*96oX`ShVR(%i{1|090=Z3r44n%A$~_`_%%2Xc^xUQQ z)%CaJQL=%49@rFlmodiE6ED%LI_%`1;bTmfo%X%Y4@*Lojy21GD$byW-j@XT^I{Q; z3PCET_I|*8Cg^6DbXcZmc*eXuZg5Im2J~!M?eaG2WPgKUvr9VC#eaFbJJv0ZMe>}e zT{D@iLiBP{RbDC-1Ym&>D`G_H`1OZB7S`UXA;_d}#xwr8;Dypaf(PI$XG||VubQUM#VhyycC1qmA zpRx(93YV}8x<2XT=zcuH#1TV;f>c~3%~s{}L(ucXc#Kq}3om{sF+@&ET z3Z`3Cs-`cgJjT#v+J?rqYVT!j2syRC$c{M7cd~ivrCO33n<4kqagjvW-Kib*8Mrja z(R@vu>6FV%sp}`D;pO`7H{82rFdA*d-~Y9mQk41IgK-8Q9sS!xdul0fjrx(PQ5#bk z18x#p--4uDI1w`-F5q{$*zp&gDkwSkv_k}1fHCBtLMt-VX#97EpNo#F>1v>}LEuHt z&_3jP+MWwZi@9ep9P#%Iqi2r<^G`Vu=YJnmN5Spx=N*ZMLV9l0TzA3c9&8h|0Y z@)2WK`z`?EILXR$ftU1yVsU(>k8+Nx3}?QO)732?ieAE3)gh%)?w9bEwWM=jIXW>L zY~E;++F?vT($Cx~N1Omze=C7K*Jd=zyY;C-5VwKv9?K=2*}PJaK)?RWpSM)Ss|2s@ z&g2f29UB9qB8POMxuuFJ2OTGvcji7Inf+)XOSPqm!s27Ej2ymD`U;dk4m1XuqAPe6 z-%nxZ!c>8328a2~zVXc$Veyg6lzmK|m|va+)VtJ{0cNdhUAJlbB3W)F(OqlUy!nj2 zU6h3O%ZJj9?EQ&~>^k*LP5EJ$?&`ZPPw|mjwF&IEK^Br>%AJfSCxAAcGJj^+`K4?C zA+EDiM}P3>iRsZ8{hgURr&+6il-KrOvR1^>8uUmKgX2GLyICuhSaXLqYdfGY!;gY1 zOG#|(k-7f-=*xsJC_z`Qh=CJJ=!}dwQPt%n#Mj0v#eRvPz?=i90l_B%6qoE||Gb8n zKotMgL6DK(8;FONtwMU=EQP15RrWljsrh|b(`ydAaZp=-vvAqEQO@cpUPPvj+cfkz zd~_{5Fw8BzFQE>Fv&Yj6C;SpZdZ99N3 zctm}zQt4y@F^gmlihq>t>40R-8ujmX3^JY+Z!i|02qtZxa{7r|B7m35+9)ax?3as2 zelKSLu|e${6fm=bE3cb6ChqA-c)ctTFra6TG3FJfPQprun`J5wfBpFzZ~JGtkAPAA zf12)BpzPK%Jdy)UB5FebkKY!y#HPYTop;tOyY=|rt#+0BeI#*HgEhBx$-v*cVz#cX z^3tAn35m+Ta#GhS$qPfr+-~}@X1Y$gD+kByLe$LUw500kypw(PES{m&5e9f!?A79u z&f;3EYZ&W?GFlrf)#|aC&b&2E*K^zKQ^2CRncV2#vdN*pRia|I=AO33p4wRbY>~ZT ztE!{BuW@8+OPZgz z?xn^Y`Z))k@j)2TmhGGh3{JT)-P#)Knuga6>t8E()hZA5>xn69Uoqwn?`kA$F#Fa$ z6JqW@l!bec>E21GiEm z_CG0TzCN%!Gv3nm`0C9d-I{uMNl%s>&U`2KOBkN%?1NJK=vg-%6T#2pe!{#qg7V?- ztuo;_CIvpFGMu#|EMmql(6rC!aKqom(bmTc38Ngf#epIg&xbtR(%Su))LQ?=VL@0G|nH+26S{kS(QaR`$sOA8w&Wp}P{6{RXaH}-8IQVrU>qpNg3 zyny?!)7Zm(>n`C8Z{PvW=&XFN(PmpAw7+rW&I^{`=-Two87w)6yRfQ0>Y>PLYcF|& zGvkjdobQ#vc3>P5HxX%E+kM{!1|+I*fqEYtoGj#^4=W1>r%Et4dKXANRb7K~HAR-D z>6C3G$UZ%VZ(=ruWFc=SzW6>itl&O$cx?=iV3pACL?Ni1FWX7R_n%TUBdpq+pZ1Ebz}M0df-y=x@j z`+1Jf`NegUPZ_)7kwKpP!XP6(--aM4_|5uUpL4{*^4w=r?tPap0Rt-A)}?n7Yvc{r zH+#0bpoq57xL7N0ld*4swm;5`#Q*hjj(r2JG}LDvQ4^F{dJC=*R$W(Y|E?^t(DLFc z=;~PLWN&M1(OmGY6lHhdl)}BpoIF zW`s-!sdE}aJ{W5vr@1ec+-Rg5-*y68WQ>ut?#gAbB{vYP>YjQ@6Yy&pO3*0wT$tR2C`*)^?ea5%!%=jzMZy;P@XgZ& zj3IG5tIGrSW%bWMwkv^ChMcu<>c`?m;yq-=vmTQAnuJB1hO2i2=x6Z#i}f^2oTDj&m3@b z<_wOqPf#!Yf|DP*Qy9$PaYE*xX;XSw&(&#l%hz*@cM>Omg(p!xzLT4Ldv+km$|$J5 z!-fy<#bYIhC1nodP;JJBBR0e5d}&F%bHbI0nxA@nMv7^8>hTI1zBuUW6;*H>=xg!J zY*%GW5R_7wwKEQ6iafxFSS<8xB1-A6`Y$ndT0C@H{zUsttz*?D<5 znCPGlwdyD*P+}RSzoegwYW|78gk<2}NYFCqy3Q#rQOTL>H@S~IQ*4FG&@k?O$KY5b z`FoDYTvleKTM)y!2O9ri`-h2X%Jv=AQPBb%MwNNvm`WNVUM=9LmumT4u-)fp44Oj< zIB0>yuDY8!j-f7AAI)yIVt1F9MS_l(H}}QqfJ~Jyr=!4Tr9T=^VQUUkjizhUf|yR} zY4|c9`(iRmIS6z&mQbqEt7AR}BLH0Jv41j&yq{7Z{ZwLr?u7hh#aVIzm{z@u(d*lfzfiIh;3(} zP;Bo2F_@OwK6jqAW|?s0LR4N__@frQx5Ad@eM-Sl5=6D^xl_6mGSu$<=Zxgk>qFAN zmV#??EOm`6K?o;Y%9~AGitZ3U2T6Z_Qycw~B)uSa3X|=~7@A zaU)=g$CrzqIP3Q*2|#Y9zN(hF>cM>p8y3zownX|{?TwY=({FLQl$>1{xA#=&u| z6(~Y2Y`@{aE&XjYmovXfxdKUWF0ea95t@%?aq#dSh|j#D)_nhJrtpLer_pX0XJk-` z4wi$9^wg=xEobhPIr=}BQT|kmc6P^Ib|=9JJ^%oQbN11;TZZ7w2Z1NPvXc3hC%DYc zZwijs|5d^1l%m9KwWJCGOu97_AzXK|C;$*{{!Q^YAs9119sDW|J^XLPp;~XJkdUYa zjN;ebD*Z*AYSr9{*(q`cIdYA(ZvvAk-iH<}T65;)H! zT!6FSYaiWN*yQ3u&2Hs%uOdSuL2vr<7n2#6nKAMK*XBeBHHw;z7wA-4XA++%ocY5- z^*cU1nv&PGPCZNk^#1B^H=No{N&r^h*3tt=-B&pIN2cJzvayE#0wZ!?WFkp)Wy&)Z zCCE_;U~KrS`fjYZnUk=I5byJY_>Eyj6#<^&ze`Gf=_JcL3uxhW0PNmL&mGPm&jhQ# zTWS&BSgEpb5QG=FmjC9u+KV++EkLQ)WNXQ!af|ue*OKoY|Ig#tNp?CyeI|CHxW24R zj}iZ1*&XNGBj%HTWi@}%Yr8uR-gR`__?$)V%5TSET)*j|Vcncm;}P#CG}@;JtiKtb zlBWdcz$n-m51#V7c*W(v^5a}rLrs;P_RNtq8uLasF5l$yFCW?UHQKzFHiB)>>M>$? z`*v+K<6E`aQ^PtrGZd(g>zAA=hYjILMbI<+cgGb&rAzQPj;ehLV()*c5=2++-gHoN$>&pM2@uQ4+f+jPJYfXW6Yls2Yy2t zs(zkv*CU$#P(8@G#ezb9PWub!!OPlSl@3wy$8n{eFbKk#0E^JAYj){KFM}}R64K^? zw<09mACNw?UgF~uJXfaw4uUFbk~Z#gM*c%E(3%-`Z3?q)0VnQepn00_)MHlvmb?Iy zZ6ROPAZIct@TzAxXdvmqLEgU=J~g&<8sR$BP-7zjCc!E70uu`RNCe|kPmnIJ^c-WK zNrm)ZEn_DM(}j#?7V!{GGhHDUWDHeN+Ys_rc!V@>BomhVDYN0fYN`M5w*=rX_m3V* z*K~50?eRT@5-ND`zwXQ*1*-x7R8z{vemRj0xM&o)xc!g*#q{hD&Rp8F(Obv;2X>Dy zJvtTzi$sGD4gL?bV@HtFHx(SJBp6|5z6#4?-zH-D0cN)W zV(+`O4f>+-wPOM~8!zKiCEx^8)n25@6o$6(r#rSaDwTrq7hc!}T2~hMQ#(On1T*N7 zBS`qjWRkIslt6nAw9T&gSJm5x&x+P?uaD9-R?==KllgBNT0{T^6w8tF7a)qVKtG4c zJMIJ9)e98#oe0v>v2EGmvQ?YnRT~3~v9(}Yv?}Y5;9@Xg(Wh?1AbRAqo$uuA?Wnm% z_#CCOVs0#E6`PVr%*rFOeCwmxz86kshytX{3?0*P0jl%9Eu($9W!|`(XKRVkY&uFV zipOuQ-;acf4#Z>@w~aw&Y<&5wj$b4IN Date: Wed, 13 Sep 2023 13:57:35 -0700 Subject: [PATCH 17/21] Apply suggestions from code review Co-authored-by: Willy <19799671+Willy-C@users.noreply.github.com> Co-authored-by: Noelle Wang <73260931+No767@users.noreply.github.com> --- docs/guide/interactions/checks.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/guide/interactions/checks.rst b/docs/guide/interactions/checks.rst index 7b2cbcf8660d..1362e6f00a0d 100644 --- a/docs/guide/interactions/checks.rst +++ b/docs/guide/interactions/checks.rst @@ -37,7 +37,7 @@ These checks allow a simple way to ensure that the user triggering a command has @app_commands.context_menu() @app_commands.checks.has_any_role('Role Name', 1234567890, 'AnotherOne') async def multiple_role_check(interaction: discord.Interaction, user: discord.User): - await interaction.response.send_message('You have all the roles!', ephemeral=True) + await interaction.response.send_message('You have one of the roles!', ephemeral=True) Let's take a quick look through the code here: @@ -95,7 +95,7 @@ Static cooldowns are methods which limit a command to a certain number of uses p :emphasize-lines: 2 @app_commands.command() - @app_commands.checks.cooldown(1, 5.0, key=lambda interaction: (i.guild_id, i.user.id)) + @app_commands.checks.cooldown(1, 5.0, key=lambda interaction: (interaction.guild_id, interaction.user.id)) async def cooldown_check(interaction: discord.Interaction): await interaction.response.send_message('Not on cooldown yet!', ephemeral=True) @@ -107,8 +107,8 @@ The :func:`@cooldown <.app_commands.checks.cooldown>` decorator takes 3 possible Now that we know what the parameters for a static cooldown are, let's take a closer look at the shown example: -- The ``rate`` is set to ``1``, so the cooldown will trigger every time the command is used. -- The ``per`` param is set to ``5.0``, so the cooldown will lock the command for 5 seconds when it is used. +- The ``rate`` parameter is set to ``1``, so the cooldown will trigger every time the command is used. +- The ``per`` parameter is set to ``5.0``, so the cooldown will lock the command for 5 seconds when it is used. - The ``key`` parameter has a ``lambda`` function which accepts the :class:`.Interaction`, and then returns a tuple for the :attr:`~.Interaction.guild_id` and :attr:`user.id <.Interaction.user>` values. This means that the cooldown will be put in effect for each user individually, in each server. Putting it all together: an individual user can use the command once every five seconds in any one guild. This means that if they were to then proceed to use the command in another guild, the cooldown from the first guild would not be applied. From edf1c1633c60667095f895786075611b4d4eb3aa Mon Sep 17 00:00:00 2001 From: Heroicos_HM Date: Wed, 13 Sep 2023 14:04:21 -0700 Subject: [PATCH 18/21] Example syntax update and context menu details --- docs/guide/interactions/checks.rst | 5 +++-- docs/guide/interactions/context_menus.rst | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/guide/interactions/checks.rst b/docs/guide/interactions/checks.rst index 1362e6f00a0d..ed1d866b88ce 100644 --- a/docs/guide/interactions/checks.rst +++ b/docs/guide/interactions/checks.rst @@ -206,9 +206,10 @@ In order to implement a global check, we will need to create our own subclass of .. code-block:: python3 import discord + from discord import app_commands from discord.ext import commands - class MyCommandTree(discord.app_commands.CommandTree): + class MyCommandTree(app_commands.CommandTree): async def interaction_check(self, interaction: discord.Interaction) -> bool: if interaction.user.id == 1234567890: return False @@ -222,7 +223,7 @@ In order to implement a global check, we will need to create our own subclass of client = discord.Client(...) tree = MyCommandTree(client) - # Using with a discord.ext.commands.Bot implementation + # Using with a commands.Bot implementation bot = commands.Bot(..., tree_cls=MyCommandTree) diff --git a/docs/guide/interactions/context_menus.rst b/docs/guide/interactions/context_menus.rst index bb6d04aff632..6602f895cc4d 100644 --- a/docs/guide/interactions/context_menus.rst +++ b/docs/guide/interactions/context_menus.rst @@ -10,6 +10,11 @@ Context Menus Guide Context menus allow for commands to be triggered through a context menu upon right clicking a related object in the Discord UI, then selecting the name of the command to run from the ``Apps`` menu. +.. note:: + + Context menus are not designed to be put inside of cogs. If you would like to use a context menu within a cog, + you will need to handle creating the :class:`.app_commands.ContextMenu` yourself. + Basic Usage ~~~~~~~~~~~~ From 28de92839f5b8bbb48285a4921228614c52e416d Mon Sep 17 00:00:00 2001 From: "Arya (She/Her)" Date: Thu, 14 Sep 2023 15:51:38 -0700 Subject: [PATCH 19/21] Apply suggestions from code review Added most of the review suggestions from @Gobot1234 Co-authored-by: James Hilton-Balfe --- docs/guide/interactions/checks.rst | 49 +++++++++++------------ docs/guide/interactions/context_menus.rst | 4 +- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/docs/guide/interactions/checks.rst b/docs/guide/interactions/checks.rst index ed1d866b88ce..f99ed4d7721d 100644 --- a/docs/guide/interactions/checks.rst +++ b/docs/guide/interactions/checks.rst @@ -7,11 +7,11 @@ Interaction Checks Guide ========================= -Checks are a feature that allows you to set requirements in order for a command to be executed. This can include things like requiring administrator permissions, requiring a user to have a role, making sure that the bot has necessary permissions to execute a task, adding a cooldown to a command, creating a custom check function, or any combination of these. +Checks allow you to set requirements for a command to be executable. They can enforce a user being an administrator, any number of roles, the bot's permissions, cooldowns, custom checks, or any combination of these. These checks will be used as decorators in conjunction with any :func:`@app_commands.command <.app_commands.command>` or :func:`@app_commands.context_menu <.app_commands.context_menu>` decorators. -Before getting started, the examples shown below are made using both :class:`.app_commands.Command` and :class:`.app_commands.ContextMenu` methods. All of the checks present here can apply to either one. Let's take a look at each of the possible checks. +Before getting started, the examples shown below demonstrate both :class:`.app_commands.Command` and :class:`.app_commands.ContextMenu` checks. All of the checks present here can apply to either one. Let's take a look at each of the possible checks. .. note:: @@ -22,7 +22,7 @@ Before getting started, the examples shown below are made using both :class:`.ap Role Checks ------------ -These checks allow a simple way to ensure that the user triggering a command has a role, or any role from a set of roles. +These checks ensure that the user triggering a command has a role, or any role from a set of roles. .. code-block:: python3 :emphasize-lines: 2,7 @@ -48,7 +48,7 @@ Let's take a quick look through the code here: A very similar process happens for checking multiple roles: - First, a :class:`.app_commands.ContextMenu` is registered using the :func:`@app_commands.context_menu() <.app_commands.context_menu>` decorator. -- Then, a :func:`@has_role <.app_commands.checks.has_any_role>` is added for multiple roles. Again, these roles are either :class:`str` or :class:`int` values. +- Then, a :func:`@has_role <.app_commands.checks.has_any_role>` is added for multiple roles. Again, these roles are either :class:`str` or :class:`int` values with the types having the same meanings. - Then, the callback for the context menu is created. .. _guide_interaction_checks_permission-check: @@ -56,7 +56,7 @@ A very similar process happens for checking multiple roles: Permission Checks ------------------ -These checks handle validating whether the user activating a command has certain permissions in a server, or whether the bot user has specific permissions. +These checks handle validating whether the user activating a command has certain permissions in a guild, or whether the bot user has specific permissions. .. code-block:: python3 :emphasize-lines: 3,9 @@ -73,7 +73,7 @@ These checks handle validating whether the user activating a command has certain async def bot_permission_check(interaction: discord.Interaction): await interaction.response.send_message('I can do that!', ephemeral=True) -These two checks are identical in usage. The only difference is that one checks the permissions of the user who calls the command, and one checks the permissions of the bot user the command is issued to. +These two checks are identical in usage. The only difference is that the first checks the permissions of the user who calls the command, and the other checks the permissions of the bot user the command is issued to. The permissions given to the check can be any number of permissions, which can be found in :class:`.Permissions`, and must have a boolean value. @@ -84,12 +84,12 @@ Cooldown Checks It is also possible to use a check decorator that will directly attach a cooldown to your command. -There are two cooldown methods, static and dynamic. +There are two cooldown types, static and dynamic. Static Cooldown ~~~~~~~~~~~~~~~~ -Static cooldowns are methods which limit a command to a certain number of uses per time frame. +Static cooldowns limit a command to a fixed number of uses, per time frame. .. code-block:: python3 :emphasize-lines: 2 @@ -101,21 +101,21 @@ Static cooldowns are methods which limit a command to a certain number of uses p The :func:`@cooldown <.app_commands.checks.cooldown>` decorator takes 3 possible arguments: -- The ``rate`` parameter first takes an integer value. This value is the number of usages that can be used within a time frame before the cooldown is activated. -- The ``per`` parameter then takes a float value. This is the number of seconds to wait for a cooldown once it has been activated. -- The third and final parameter, ``key``, can be used to specify what criteria are used for applying the cooldown. This is given as a function that takes a :class:`.Interaction` as an argument, and can be a coroutine. +- The ``rate`` parameter first takes an :class:`int`, which determines the number of times a command can be used within a time frame before the cooldown is activated. +- The ``per`` parameter then takes a :class:`float` which determines the number of seconds to wait for a cooldown once it has been activated. +- The third and final parameter, ``key``, can be used to specify what criteria are used for applying the cooldown. This is given as a function that takes a :class:`.Interaction` as an argument, and can return a coroutine. Now that we know what the parameters for a static cooldown are, let's take a closer look at the shown example: - The ``rate`` parameter is set to ``1``, so the cooldown will trigger every time the command is used. - The ``per`` parameter is set to ``5.0``, so the cooldown will lock the command for 5 seconds when it is used. -- The ``key`` parameter has a ``lambda`` function which accepts the :class:`.Interaction`, and then returns a tuple for the :attr:`~.Interaction.guild_id` and :attr:`user.id <.Interaction.user>` values. This means that the cooldown will be put in effect for each user individually, in each server. +- The ``key`` parameter has a ``lambda`` function which accepts the :class:`.Interaction`, and then returns a :class:`tuple` for the :attr:`~.Interaction.guild_id` and :attr:`user.id <.Interaction.user>` values. This means that the cooldown will be put in effect for each user individually, in each guild. -Putting it all together: an individual user can use the command once every five seconds in any one guild. This means that if they were to then proceed to use the command in another guild, the cooldown from the first guild would not be applied. +Putting it all together: an individual user can use the command once every 5 seconds per guild. This means that if they were to then proceed to use the command in another guild, the cooldown from the first guild would not be applied. .. note:: - By default, the "key" parameter will operate on the :class:`.User` level. So if no "key" parameter is specified, the cooldown will be applied per user, globally. Similarly, if the "key" parameter is set to "None", then the cooldown is considered a "global" cooldown for all users and guilds. + By default, the "key" parameter will operate on the :class:`.User` level. So if no "key" parameter is specified, the cooldown will be applied per user, globally. Similarly, if the "key" parameter is set to ``None``, then the cooldown is considered a "global" cooldown for all users and guilds. Dynamic Cooldown ~~~~~~~~~~~~~~~~~ @@ -140,14 +140,14 @@ Dynamic cooldowns allow you to register a custom handler for cooldown checks. The :func:`@dynamic_cooldown <.app_commands.checks.dynamic_cooldown>` is passed a reference to a function, which can be used to control when to apply a different cooldown to specific use-cases, or ignore it all together. -In this specific use case, the function is used to apply a ``10.0`` second per usage cooldown to each individual user. However, if the user is the owner of the guild the command is used in, the command cooldown is bypassed. Similarly, if the user is a specific user mentioned by id, a lesser cooldown is applied, of just ``5.0`` seconds per usage. +In this specific use case, the function is used to apply a 10 second per usage cooldown to each individual user. However, if the user is the owner of the guild the command is used in, the command cooldown is bypassed. Similarly, if the user is a specific user mentioned by id, a lesser cooldown is applied, of just 5 seconds per usage. .. _guide_interaction_checks_custom-check: Custom Check ------------- -Custom check commands can also be implemented if further functionality is needed outside of the checks listed above. These will generally come in two forms: a standard check decorator that is passed a custom function, or a custom decorator for common checks. +Custom checks can also be implemented if further functionality is needed outside of the checks listed above or in the :ref:`API reference `. These generally come in two forms: a standard check decorator that is passed a custom function, or a custom decorator for common checks. .. code-block:: python3 :emphasize-lines: 6,17 @@ -172,16 +172,16 @@ Custom check commands can also be implemented if further functionality is needed async def me_only(interaction: discord.Interaction): await interaction.response.send_message('I know you!', ephemeral=True) -In this example, the first check is implemented to only allow the command usage if the user who runs the command is the owner of the guild the command is used in. This is simply a function which takes a :class:`.Interaction` as an argument, which is passed to a :func:`@check <.app_commands.check>` decorator. The second check implemented creates a custom decorator which checks if the user who activates a command has a specific id. This limits a command to only one user. +In this example, the first check is implemented to only allow the command usage if the user who runs the command is the owner of the guild the command is used in. This is a function which takes a :class:`.Interaction` as an argument, which is passed to the :func:`@check <.app_commands.check>` decorator. The second check implemented creates a custom decorator which checks if the user who activates a command has a specific id. This limits a command to only one user. .. _guide_interaction_checks_combining-checks: Combining Checks ----------------- -You can combine multiple checks on a command to end up with a very particular functionality for your needs, in a very concise manner. +You can combine multiple checks on a command which requires all to pass for invocation to succeed. -As an example, here is a command which only runs if a user has a certain role, ``send_messages`` permissions, the bot has ``manage_messages`` permissions, and with a ``15`` second global cooldown: +As an example, here is a command which only runs if a user has a certain role, ``send_messages`` permissions, the bot has ``manage_messages`` permissions, and with a 15 second global cooldown: .. code-block:: python3 @@ -248,9 +248,9 @@ Alright, let's deconstruct this now: Integration Permissions ------------------------ -In addition to the standard check functionality, there are a few additional functionalities that behave similarly. +In addition to the previously mentioned checks, there are a few other "check-like" functionalities provided. -These options include: +These include: - The :func:`@app_commands.default_permissions() <.app_commands.default_permissions>` decorator. - The :func:`@app_commands.guild_only() <.app_commands.guild_only>` decorator. @@ -263,8 +263,7 @@ handling when these situations arise. .. note:: If you wish to perform error handling of these kinds of features yourself, - you can implement a :ref:`custom check ` to do this and not utilize these - features. + you can implement a :ref:`custom check `. .. warning:: @@ -281,7 +280,7 @@ for a user to execute a slash command or interact with a context menu. Using this means that a slash command or context menu will by default require the provided permissions. However, an administrator in the server will be able to override these permissions directly in the Discord client, -without the bot ever being any the wiser. +without the bot's knowledge. .. code-block:: python3 @@ -304,7 +303,7 @@ In the first example above, we limit the permissions to use the slash command to in the default permissions being set to administrator only. In the second example, the context menu will be limited to only users with the ``delete_messages`` and ``manage_guild`` -permissions. The provided values can be any number of permissions, which can be found in :class:`.Permissions`, +permissions. The provided values can be any number of :class:`.Permissions`, and must have a boolean value. Here is what the context menu above looks like in the Discord client, found inside of the ``Integrations`` section of diff --git a/docs/guide/interactions/context_menus.rst b/docs/guide/interactions/context_menus.rst index 6602f895cc4d..0f08ae03adc7 100644 --- a/docs/guide/interactions/context_menus.rst +++ b/docs/guide/interactions/context_menus.rst @@ -8,7 +8,7 @@ Context Menus Guide ==================== Context menus allow for commands to be triggered through a context menu upon right clicking a related object in the Discord UI, -then selecting the name of the command to run from the ``Apps`` menu. +then selecting the name of the command to run from the "Apps" menu. .. note:: @@ -47,6 +47,6 @@ The Message Context Menu produces an option that looks like the following: Checks ~~~~~~~ -One of the most common current uses of context menus is to implement a variety of extended moderation features. Some ideas might include a ``Toggle Mute``, ``Warn``, ``Report``, or ``Info`` commands. For implementing moderation commands like these, it will be useful to add checks in order to ensure that only those who are allowed to can use said commands. +Context menus can be used to implement a variety of extended moderation features. Some ideas might include a ``Toggle Mute``, ``Warn``, ``Report``, or ``Info`` commands. For implementing moderation commands like these, it is useful to add checks to ensure that only those who are allowed to can the commands. For examples of checks, view the :ref:`Interaction Checks Guide `. \ No newline at end of file From 27ab440752a19fbc7f0c34974854301c562a4b9e Mon Sep 17 00:00:00 2001 From: "Arya (She/Her)" Date: Thu, 14 Sep 2023 15:52:17 -0700 Subject: [PATCH 20/21] Apply suggestions from code review --- docs/guide/interactions/checks.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guide/interactions/checks.rst b/docs/guide/interactions/checks.rst index f99ed4d7721d..8090fd33e75c 100644 --- a/docs/guide/interactions/checks.rst +++ b/docs/guide/interactions/checks.rst @@ -120,7 +120,7 @@ Putting it all together: an individual user can use the command once every 5 sec Dynamic Cooldown ~~~~~~~~~~~~~~~~~ -Dynamic cooldowns allow you to register a custom handler for cooldown checks. +Dynamic cooldowns allow you to register a custom check and a cooldown with different ``rate`` and ``per`` parameters for different scenarios. .. code-block:: python3 :emphasize-lines: 10 @@ -138,7 +138,7 @@ Dynamic cooldowns allow you to register a custom handler for cooldown checks. async def dynamic_cooldown_check(interaction: discord.Interaction): await interaction.response.send_message('Not on cooldown!', ephemeral=True) -The :func:`@dynamic_cooldown <.app_commands.checks.dynamic_cooldown>` is passed a reference to a function, which can be used to control when to apply a different cooldown to specific use-cases, or ignore it all together. +The :func:`@dynamic_cooldown <.app_commands.checks.dynamic_cooldown>` is passed a function, which controls when to apply a cooldown and its ``rate`` and ``per`` parameters, or ignore it all together. In this specific use case, the function is used to apply a 10 second per usage cooldown to each individual user. However, if the user is the owner of the guild the command is used in, the command cooldown is bypassed. Similarly, if the user is a specific user mentioned by id, a lesser cooldown is applied, of just 5 seconds per usage. From a094f343b56f1a92494d6446a1560e08fdf5ee75 Mon Sep 17 00:00:00 2001 From: "Arya (She/Her)" Date: Thu, 14 Sep 2023 16:06:31 -0700 Subject: [PATCH 21/21] Apply suggestions from code review --- docs/guide/interactions/checks.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/interactions/checks.rst b/docs/guide/interactions/checks.rst index 8090fd33e75c..189b07a6ddb2 100644 --- a/docs/guide/interactions/checks.rst +++ b/docs/guide/interactions/checks.rst @@ -9,7 +9,7 @@ Interaction Checks Guide Checks allow you to set requirements for a command to be executable. They can enforce a user being an administrator, any number of roles, the bot's permissions, cooldowns, custom checks, or any combination of these. -These checks will be used as decorators in conjunction with any :func:`@app_commands.command <.app_commands.command>` or :func:`@app_commands.context_menu <.app_commands.context_menu>` decorators. +Checks are decorators used with any :func:`@app_commands.command <.app_commands.command>` or :func:`@app_commands.context_menu <.app_commands.context_menu>` decorator. Before getting started, the examples shown below demonstrate both :class:`.app_commands.Command` and :class:`.app_commands.ContextMenu` checks. All of the checks present here can apply to either one. Let's take a look at each of the possible checks.