From eee7ace0b58be610c7ac00f7f5634d9f73454961 Mon Sep 17 00:00:00 2001 From: Eric Fletcher <64165327+iamericfletcher@users.noreply.github.com> Date: Sat, 2 Apr 2022 12:54:48 -0400 Subject: Migrate Setting Different Statuses to Set Your Bot pin by Python bot to site --- .../setting-different-statuses-on-your-bot.md | 41 ++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 pydis_site/apps/content/resources/guides/python-guides/setting-different-statuses-on-your-bot.md (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/python-guides/setting-different-statuses-on-your-bot.md b/pydis_site/apps/content/resources/guides/python-guides/setting-different-statuses-on-your-bot.md new file mode 100644 index 00000000..a01ce8da --- /dev/null +++ b/pydis_site/apps/content/resources/guides/python-guides/setting-different-statuses-on-your-bot.md @@ -0,0 +1,41 @@ +--- +title: Setting Different Statuses to Set Your Bot +description: How to personalize your Discord bot status +--- + +#### Setting 'Playing' Status +```python +await client.change_presence(activity=discord.Game(name="a game")) +``` + +#### Setting 'Streaming' Status +```python +await client.change_presence(activity=discord.Streaming(name="My Stream", url=my_twitch_url)) +``` + +#### Setting 'Listening' Status +```python +await client.change_presence(activity=discord.Activity(type=discord.ActivityType.listening, name="a song")) +``` + +#### Setting 'Watching' Status +```python +await client.change_presence(activity=discord.Activity(type=discord.ActivityType.watching, name="a movie")) +``` + +#### Add Optional Status as Well: + +* status=discord.Status.\ + +####Available Statuses: + +* do_not_disturb(red icon) + + +* idle(yellow icon) + + +* online(default, green icon) + + +* offline(gray icon) -- cgit v1.2.3 From c0a15c94861cce9e56eebb6307e09bb3cfd46de3 Mon Sep 17 00:00:00 2001 From: Eric Fletcher <64165327+iamericfletcher@users.noreply.github.com> Date: Sun, 10 Apr 2022 11:31:12 -0400 Subject: Migrate Setting Different Statuses to Set Your Bot pin by Python bot to site --- .../guides/python-guides/setting-different-statuses-on-your-bot.md | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/python-guides/setting-different-statuses-on-your-bot.md b/pydis_site/apps/content/resources/guides/python-guides/setting-different-statuses-on-your-bot.md index a01ce8da..53390416 100644 --- a/pydis_site/apps/content/resources/guides/python-guides/setting-different-statuses-on-your-bot.md +++ b/pydis_site/apps/content/resources/guides/python-guides/setting-different-statuses-on-your-bot.md @@ -2,6 +2,12 @@ title: Setting Different Statuses to Set Your Bot description: How to personalize your Discord bot status --- +**Please note:** + +If you want to change the bot status, it is suggested to not do it during the on_ready event, since it would be called +many times and making an API call on that event has a chance to disconnect the bot. +Instead, set the desired status using the activity / status kwarg of commands.Bot, for example +`bot = commands.Bot(command_prefix="!", activity=..., status=...)` #### Setting 'Playing' Status ```python -- cgit v1.2.3 From 4ec0fb3e6102c0f5a99c51afcec38eb5d0dfa6ae Mon Sep 17 00:00:00 2001 From: Blue-Puddle Date: Tue, 28 Jun 2022 06:23:50 +0100 Subject: added discord-app-commands.md --- .../guides/python-guides/discord-app-commands.md | 451 +++++++++++++++++++++ 1 file changed, 451 insertions(+) create mode 100644 pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md b/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md new file mode 100644 index 00000000..79d224a1 --- /dev/null +++ b/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md @@ -0,0 +1,451 @@ +--- +title: Slash Commands with discord.py! +description: A simple guide to creating slash commands within discord.py! +--- +# DISCORD.PY RESUMPTION CHANGES + +--- + +Upon resumption of the most popular discord API wrapper library for python, `discord.py`, while catching on to the latest features of the discord API, there have been numerous changes with addition of features to the library. Some additions to the library are -> Buttons support, Select Menus Support, Forms (AKA Modals), Slash Commands (AKA Application Commands) and a bunch of more handy features! All the changes can be found [here](https://discordpy.readthedocs.io/en/latest/migrating.html). Original discord.py gist regarding resumption can be found [here](https://gist.github.com/Rapptz/c4324f17a80c94776832430007ad40e6). + +# Why this gist? + +--- + +This Gist is being created as an update to slash commands (app commands) with explanation and examples. This Gist mainly focuses on **SLASH COMMANDS** for discord.py 2.0 (and above)! + +# What Are Slash Commands? + +--- + +Slash Commands are the exciting way to build and interact with bots on Discord. With Slash Commands, all you have to do is type / and you're ready to use your favourite bot. You can easily see all the commands a bot has, and validation and error handling help you get the command right the first time. + +# Install the latest version for discord.py + +--- +To use slash commands with discord.py. The latest up-to-date version has to be installed. Make sure that the version is 2.0 or above! +And make sure to uninstall any third party libraries that support slash commands for discord.py (if any) as a few of them [monkey patch](https://en.wikipedia.org/wiki/Monkey_patch#:~:text=A%20monkey%20patch%20is%20a,running%20instance%20of%20the%20program) discord.py! + +The latest and up-to-date usable discord.py version can be installed using `pip install -U git+https://github.com/Rapptz/discord.py`. + +If you get an error such as: `'git' is not recognized...`. [Install git](https://git-scm.com/downloads) for your platform. Go through the required steps for installing git and make sure to enable the `add to path` option while in the installation wizard. After installing git you can run `pip install -U git+https://github.com/Rapptz/discord.py` again to install discord.py 2.0. +**BEFORE MIGRATING TO DISCORD.PY 2.0, PLEASE READ THE CONSEQUENCES OF THE UPDATE [HERE](https://discordpy.readthedocs.io/en/latest/migrating.html)**. + +# Basic Structure for Discord.py Slash Commands! + +--- + +### Note that Slash Commands in discord.py are also referred to as **Application Commmands** and **App Commands** and every *interaction* is a *webhook*. +Slash commands in discord.py are held by a container, [CommandTree](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=commandtree#discord.app_commands.CommandTree). A command tree is required to create slash commands in discord.py. This command tree provides a `command` method which decorates an asynchronous function indicating to discord.py that the decorated function is intended to be a slash command. This asynchronous function expects a default argument which acts as the interaction which took place that invoked the slash command. This default argument is an instance of the **Interaction** class from discord.py. Further up, the command logic takes over the behaviour of the slash command. + +# Fundamentals for this gist! + +--- + + +The fundamental for this gist will remain to be the `setup_hook`. The `setup_hook` is a special asynchronous method of the Client and Bot class which can be overwritten to perform numerous tasks. This method is safe to use as it is always triggered before any events are dispatched, i.e. this method is triggered before the *IDENTIFY* payload is sent to the discord gateway. +Note that methods of the Bot class such as `change_presence` will not work in setup_hook as the current application does not have an active connection to the gateway at this point. + +__**FOLLOWING IS THE EXAMPLE OF HOW A `SETUP_HOOK` FUNCTION CAN BE DEFINED**__ + +```python +import discord + +'''This is one way of creating a "setup_hook" method''' + +class SlashClient(discord.Client): + def __init__(self) -> None: + super().__init__(intents=discord.Intents.default()) + + async def setup_hook(self) -> None: + #perform tasks + +'''Another way of creating a "setup_hook" is as follows''' + +client = discord.Client(intents=discord.Intents.default()) +async def my_setup_hook() -> None: + #perform tasks + +client.setup_hook = my_setup_hook +``` + +# Basic Slash Command application using discord.py. + +#### The `CommandTree` class resides within the `app_commands` of discord.py package. +--- + +## Slash Command Application with a Client + +```python +import discord + +class SlashClient(discord.Client): + def __init__(self) -> None: + super().__init__(intents=discord.Intents.default()) + self.tree = discord.app_commands.CommandTree(self) + + async def setup_hook(self) -> None: + self.tree.copy_global_to(guild=discord.Object(id=12345678900987654)) + await self.tree.sync() + +client = SlashClient() + +@client.tree.command(name="ping", description="...") +async def _ping(interaction: discord.Interaction) -> None: + await interaction.response.send_message("pong") + +client.run("token") +``` + + +__**EXPLANATION**__ + +- `import discord` imports the **discord.py** package. +- `class SlashClient(discord.Client)` is a class subclassing **Client**. Though there is no particular reason except readability to subclass the **Client** class, using the `Client.setup_hook = my_func` is equally valid. +- Next up `super().__init__(...)` runs the `__init__` function of the **Client** class, this is equivalent to `discord.Client(...)`. Then, `self.tree = discord.app_commands.CommandTree(self)` creates a CommandTree which acts as the container for slash commaands. +- Then in the `setup_hook`, `self.tree.copy_global_to(...)` adds the slash command to the guild of which the ID is provided as a `discord.Object` object. Further up, `self.tree.sync()` updates the API with any changes to the slash commands. +- Finishing up with the **Client** subclass, we create an instance of the subclassed Client class which here has been named as `SlashClient` with `client = SlashClient()`. +- Then using the `command` method of the `CommandTree` we decorate a function with it as `client.tree` is an instance of `CommandTree` for the current application. The command function takes a default argument as said, which acts as the interaction that took place. Catching up is `await interaction.response.send_message("pong")` which sends back a message to the slash command invoker. +- And the classic old `client.run("token")` is used to connect the client to the discord gateway. +- Note that the `send_message` is a method of the `InteractionResponse` class and `interaction.response` in this case is an instance of the `InteractionResponse` object. The `send_message` method will not function if the response is not sent within 3 seconds of command invocation. I will discuss about how to handle this issue later following the gist. + +## Slash Command Application with a Bot + +```python +import discord + +class SlashBot(commands.Bot): + def __init__(self) -> None: + super().__init__(command_prefix=".", intents=discord.Intents.default()) + + async def setup_hook(self) -> None: + self.tree.copy_global_to(guild=discord.Object(id=12345678900987654)) + await self.tree.sync() + +bot = SlashBot() + +@bot.tree.command(name="ping", description="...") +async def _ping(interaction: discord.Interaction) -> None: + await interaction.response.send_message("pong") + +bot.run("token") +``` + +The above example shows a basic slash commands within discord.py using the Bot class. + +__**EXPLANATION**__ + +Most of the explanation is the same as the prior example which featured `SlashClient` which was a subclass of **discord.Client**. Though some minor changes are discussed below. + +- The `SlashBot` class now subclasses `discord.ext.commands.Bot` following the passing in of the required arguments to its `__init__` method. +- `discord.ext.commands.Bot` already consists of an instance of the `CommandTree` class which can be accessed using the `tree` property. + +# Slash Commands within a Cog! + +--- + +A cog is a collection of commands, listeners, and optional state to help group commands together. More information on them can be found on the [Cogs](https://discordpy.readthedocs.io/en/latest/ext/commands/cogs.html#ext-commands-cogs) page. + +## An Example to using cogs with discord.py for slash commands! + +```python +import discord +from discord.ext import commands +from discord import app_commands + +class MySlashCog(commands.Cog): + def __init__(self, bot: commands.Bot) -> None: + self.bot = bot + + @app_commands.command(name="ping", description="...") + async def _ping(self, interaction: discord.Interaction): + await interaction.response.send_message("pong!") + +class MySlashBot(commands.Bot): + def __init__(self) -> None: + super().__init__(command_prefix="!", intents=discord.Intents.default()) + + async def setup_hook(self) -> None: + await self.add_cog(MySlashCog(self)) + await self.tree.copy_global_to(discord.Object(id=123456789098765432)) + await self.tree.sync() + +bot = MySlashBot() + +bot.run("token") +``` + +__**EXPLANATION**__ + +- Firstly, `import discord` imports the **discord.py** package. `from discord import app_commands` imports the `app_commands` directory from the **discord.py** root directory. `from discord.ext import commands` imports the commands extension. +- Further up, `class MySlashCog(commands.Cog)` is a class subclassing the `Cog` class. You can read more about this [here](https://discordpy.readthedocs.io/en/latest/ext/commands/cogs.html#ext-commands-cogs). +- `def __init__(self, bot: commands.Bot): self.bot = bot` is the constructor method of the class that is always run when the class is instantiated and that is why we pass in a **Bot** object whenever we create an instance of the cog class. +- Following up is the `@app_commands.command(name="ping", description="...")` decorator. This decorator basically functions the same as a `bot.tree.command` but since the cog currently does not have a **bot**, the `app_commands.command` decorator is used instead. The next two lines follow the same structure for slash commands with **self** added as the first parameter to the function as it is a method of a class. +- The next up lines are mostly the same. +- Talking about the first line inside the `setup_hook` is the `add_cog` method of the **Bot** class. And since **self** acts as the **instance** of the current class, we use **self** to use the `add_cog` method of the **Bot** class as we are inside a subclassed class of the **Bot** class. Then we pass in **self** to the `add_cog` method as the `__init__` function of the **MySlashCog** cog accepts a `Bot` object. +- After that we instantiate the `MySlashBot` class and run the bot using the **run** method which executes our setup_hook function and our commands get loaded and synced. The bot is now ready to use! + +# An Example to using groups with discord.py for slash commands! + +--- + +## An example with optional group! + +```python +import discord +from discord.ext import commands +from discord import app_commands + +class MySlashGroupCog(commands.Cog): + def __init__(self, bot: commands.Bot) -> None: + self.bot = bot + + #-------------------------------------------------------- + group = app_commands.Group(name="uwu", description="...") + #-------------------------------------------------------- + + @app_commands.command(name="ping", description="...") + async def _ping(self, interaction: discord.) -> None: + await interaction.response.send_message("pong!") + + @group.command(name="command", description="...") + async def _cmd(self, interaction: discord.Interaction) -> None: + await interaction.response.send_message("uwu") + +class MySlashBot(commands.Bot): + def __init__(self) -> None: + super().__init__(command_prefix="!", intents=discord.Intents.default()) + + async def setup_hook(self) -> None: + await self.add_cog(MySlashGroupCog(self)) + await self.tree.copy_global_to(discord.Object(id=123456789098765432)) + await self.tree.sync() + +bot = MySlashBot() + +bot.run("token") +``` + +__**EXPLANATION**__ +- The only difference used here is `group = app_commands.Group(name="uwu", description="...")` and `group.command`. `app_commands.Group` is used to initiate a group while `group.command` registers a command under a group. For example, the ping command can be run using **/ping** but this is not the case for group commands. They are registered with the format of `group_name command_name`. So here, the **command** command of the **uwu** group would be run using **/uwu command**. Note that only group commands can have a single space between them. + +--- + +## An example with a **Group** subclass! + +```python +import discord +from discord.ext import commands +from discord import app_commands + +class MySlashGroup(commands.GroupCog, name="uwu"): + def __init__(self, bot: commands.Bot) -> None: + self.bot = bot + super().__init__() + + @app_commands.command(name="ping", description="...") + async def _ping(self, interaction: discord.) -> None: + await interaction.response.send_message("pong!") + + @app_commands.command(name="command", description="...") + async def _cmd(self, interaction: discord.Interaction) -> None: + await interaction.response.send_message("uwu") + +class MySlashBot(commands.Bot): + def __init__(self) -> None: + super().__init__(command_prefix="!", intents=discord.Intents.default()) + + async def setup_hook(self) -> None: + await self.add_cog(MySlashGroup(self)) + await self.tree.copy_global_to(discord.Object(id=123456789098765432)) + await self.tree.sync() + +bot = MySlashBot() + +bot.run("token") +``` + +__**EXPLANATION**__ +- The only difference here too is that the `MySlashGroup` class directly subclasses the **GroupCog** class from discord.ext.commands which automatically registers all the methods within the group class to be commands of that specific group. So now, the commands such as `ping` can be run using **/uwu ping** and `command` using **/uwu command**. + + +# Some common methods and features used for slash commands. + +--- + +### A common function used for slash commands is the `describe` function. This is used to add descriptions to the arguments of a slash command. The command function can decorated with this function. It goes by the following syntax as shown below. + +```python +from discord.ext import commands +from discord import app_commands +import discord + +bot = commands.Bot(command_prefix=".", intents=discord.Intents.default()) +#sync the commands + +@bot.tree.command(name="echo", description="...") +@app_commands.describe(text="The text to send!", channel="The channel to send the message in!") +async def _echo(interaction: discord.Interaction, text: str, channel: discord.TextChannel=None): + channel = interaction.channel or channel + await channel.send(text) +``` + +### Another common issue that most people come across is the time duraction of sending a message with `send_message`. This issue can be tackled by deferring the interaction response using the `defer` method of the `InteractionResponse` class. An example for fixing this issue is shown below. + +```python +import discord +from discord.ext import commands +import asyncio + +bot = commands.Bot(command_prefix="!", intents=discord.Intents.default()) +#sync the commands + +@bot.tree.command(name="time", description="...") +async def _time(interaction: discord.Interaction, time_to_wait: int): + # ------------------------------------------------------------- + await interaction.response.defer(ephemeral=True, thinking=True) + # ------------------------------------------------------------- + await interaction.edit_original_message(content=f"I will notify you after {time_to_wait} seconds have passed!") + await asyncio.sleep(time_to_wait) + await interaction.edit_original_message(content=f"{interaction.user.mention}, {time_to_wait} seconds have already passed!") +``` + +# Checking for Permissions and Roles! + +--- + +To add a permissions check to a command, the methods are imported through `discord.app_commands.checks`. To check for a member's permissions, the function can be decorated with the [discord.app_commands.checks.has_permissions](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=has_permissions#discord.app_commands.checks.has_permissions) method. An example to this as follows. + +```py +from discord import app_commands +from discord.ext import commands +import discord + +bot = commands.Bot(command_prefix="!", intents=discord.Intents.default()) +#sync commands + +@bot.tree.command(name="ping") +@app_commands.checks.has_permissions(manage_messages=True, manage_channels=True) #example permissions +async def _ping(interaction: discord.Interaction): + await interaction.response.send_message("pong!") + +``` + +If the check fails, it will raise a `MissingPermissions` error which can be handled within an app commands error handler! I will discuss about making an error handler later in the gist. All the permissions can be found [here](https://discordpy.readthedocs.io/en/latest/api.html?highlight=discord%20permissions#discord.Permissions). + +Other methods that you can decorate the commands with are - +- `bot_has_permissions` | This checks if the bot has the required permissions for executing the slash command. This raises a [BotMissingPermissions](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.BotMissingPermissions) exception. +- `has_role` | This checks if the slash command user has the required role or not. Only **ONE** role name or role ID can be passed to this. If the name is being passed, make sure to have the exact same name as the role name. This raises a [MissingRole](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.MissingRole) exception. +- To pass in several role names or role IDs, `has_any_role` can be used to decorate a command. This raises two exceptions -> [MissingAnyRole](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.MissingAnyRole) and [NoPrivateMessage](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.NoPrivateMessage) + + +# Adding cooldowns to slash commands! + +--- + +Slash Commands within discord.py can be applied cooldowns to in order to prevent spamming of the commands. This can be done through the `discord.app_commands.checks.cooldown` method which can be used to decorate a slash command function and register a cooldown to the function. This raises a [CommandOnCooldown](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=checks%20cooldown#discord.app_commands.CommandOnCooldown) exception if the command is currently on cooldown. +An example is as follows. + +```python +from discord.ext import commands +import discord + +class Bot(commands.Bot): + def __init__(self): + super().__init__(command_prefix="uwu", intents=discord.Intents.all()) + + + async def setup_hook(self): + self.tree.copy_global_to(guild=discord.Object(id=12345678909876543)) + await self.tree.sync() + + +bot = Bot() + +@bot.tree.command(name="ping") +# ----------------------------------------- +@discord.app_commands.checks.cooldown(1, 30) +# ----------------------------------------- +async def ping(interaction: discord.Interaction): + await interaction.response.send_message("pong!") + +bot.run("token") +``` + +__**EXPLANATION**__ +- The first argument the `cooldown` method takes is the amount of times the command can be run in a specific period of time. +- The second argument it takes is the period of time in which the command can be run the specified number of times. +- The `CommandOnCooldown` exception can be handled using an error handler. I will discuss about making an error handler for slash commands later in the gist. + + +# Handling errors for slash commands! + +--- + +The Slash Commands exceptions can be handled by overwriting the `on_error` method of the `CommandTree`. The error handler takes two arguments. The first argument is the `Interaction` that took place when the error occurred and the second argument is the error that occurred when the slash commands was invoked. The error is an instance of [discord.app_commands.AppCommandError](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=appcommanderror#discord.app_commands.AppCommandError) which is a subclass of [DiscordException](https://discordpy.readthedocs.io/en/latest/api.html?highlight=discordexception#discord.DiscordException). +An example to creating an error handler for slash commands is as follows. + +```python +from discord.ext import commands +from discord import app_commands +import discord + +bot = commands.Bot(command_prefix="!", intents=discord.Intents.default()) +#sync commands + +@bot.tree.command(name="ping") +app_commands.checks.cooldown(1, 30) +async def ping(interaction: discord.Interaction): + await interaction.response.send_message("pong!") + +async def on_tree_error(interaction: discord.Interaction, error: app_commands.AppCommandError): + if isinstance(error, app_commands.CommandOnCooldown): + return await interaction.response.send_message(f"Command is currently on cooldown! Try again in **{error.retry_after:.2f}** seconds!") + + elif isinstance(..., ...): + ... + + else: + raise error + +bot.tree.on_error = on_tree_error + +bot.run("token") +``` + +__**EXPLANATION**__ + +First we create a simple function named as `on_tree_error` here. To which the first two required arguments are passed, `Interaction` which is named as `interaction` here and `AppCommandError` which is named as `error` here. Then using simple functions and keywords, we make an error handler like above. Here I have used the `isinstance` function which takes in an object and a base class as the second argument, this function returns a bool value. After creating the error handler function, we set the function as the error handler for the slash commands. Here, `bot.tree.on_error = on_tree_error` overwrites the default `on_error` method of the **CommandTree** class with our custom error handler which has been named as `on_tree_error` here. + +### Creating an error handler for a specific error! + +```python +from discord.ext import commands +from discord import app_commands +import discord + +bot = commands.Bot(command_prefix="!", intents=discord.Intents.default()) +#sync commands + +@bot.tree.command(name="ping") +app_commands.checks.cooldown(1, 30) +async def ping(interaction: discord.Interaction): + await interaction.response.send_message("pong!") + +@ping.error +async def ping_error(interaction: discord.Interaction, error: app_commands.AppCommandError): + if isinstance(error, app_commands.CommandOnCooldown): + return await interaction.response.send_message(f"Command is currently on cooldown! Try again in **{error.retry_after:.2f}** seconds!") + + elif isinstance(..., ...): + ... + + else: + raise error + +bot.run("token") +``` + +__**EXPLANATION**__ + +Here the command name is simply used to access the `error` method to decorate a function which acts as the `on_error` but for a specific command. Please do not call the `error` method. \ No newline at end of file -- cgit v1.2.3 From 1e59d608eb5f54b238aa55587a7bc9bfa32346d4 Mon Sep 17 00:00:00 2001 From: Robin <74519799+Robin5605@users.noreply.github.com> Date: Tue, 28 Jun 2022 13:20:06 -0500 Subject: Fix EOF Add a newline at the end of file --- .../apps/content/resources/guides/python-guides/discord-app-commands.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md b/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md index 79d224a1..57423fa4 100644 --- a/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md +++ b/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md @@ -448,4 +448,4 @@ bot.run("token") __**EXPLANATION**__ -Here the command name is simply used to access the `error` method to decorate a function which acts as the `on_error` but for a specific command. Please do not call the `error` method. \ No newline at end of file +Here the command name is simply used to access the `error` method to decorate a function which acts as the `on_error` but for a specific command. Please do not call the `error` method. -- cgit v1.2.3 From ce144ef99340339af732d41aa56714022f8a023f Mon Sep 17 00:00:00 2001 From: Robin5605 Date: Tue, 28 Jun 2022 13:47:01 -0500 Subject: Fix trailing whitespaces --- .../guides/python-guides/discord-app-commands.md | 56 +++++++++++----------- 1 file changed, 28 insertions(+), 28 deletions(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md b/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md index 57423fa4..e6095252 100644 --- a/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md +++ b/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md @@ -56,12 +56,12 @@ import discord class SlashClient(discord.Client): def __init__(self) -> None: super().__init__(intents=discord.Intents.default()) - + async def setup_hook(self) -> None: #perform tasks '''Another way of creating a "setup_hook" is as follows''' - + client = discord.Client(intents=discord.Intents.default()) async def my_setup_hook() -> None: #perform tasks @@ -83,7 +83,7 @@ class SlashClient(discord.Client): def __init__(self) -> None: super().__init__(intents=discord.Intents.default()) self.tree = discord.app_commands.CommandTree(self) - + async def setup_hook(self) -> None: self.tree.copy_global_to(guild=discord.Object(id=12345678900987654)) await self.tree.sync() @@ -100,11 +100,11 @@ client.run("token") __**EXPLANATION**__ -- `import discord` imports the **discord.py** package. -- `class SlashClient(discord.Client)` is a class subclassing **Client**. Though there is no particular reason except readability to subclass the **Client** class, using the `Client.setup_hook = my_func` is equally valid. -- Next up `super().__init__(...)` runs the `__init__` function of the **Client** class, this is equivalent to `discord.Client(...)`. Then, `self.tree = discord.app_commands.CommandTree(self)` creates a CommandTree which acts as the container for slash commaands. -- Then in the `setup_hook`, `self.tree.copy_global_to(...)` adds the slash command to the guild of which the ID is provided as a `discord.Object` object. Further up, `self.tree.sync()` updates the API with any changes to the slash commands. -- Finishing up with the **Client** subclass, we create an instance of the subclassed Client class which here has been named as `SlashClient` with `client = SlashClient()`. +- `import discord` imports the **discord.py** package. +- `class SlashClient(discord.Client)` is a class subclassing **Client**. Though there is no particular reason except readability to subclass the **Client** class, using the `Client.setup_hook = my_func` is equally valid. +- Next up `super().__init__(...)` runs the `__init__` function of the **Client** class, this is equivalent to `discord.Client(...)`. Then, `self.tree = discord.app_commands.CommandTree(self)` creates a CommandTree which acts as the container for slash commaands. +- Then in the `setup_hook`, `self.tree.copy_global_to(...)` adds the slash command to the guild of which the ID is provided as a `discord.Object` object. Further up, `self.tree.sync()` updates the API with any changes to the slash commands. +- Finishing up with the **Client** subclass, we create an instance of the subclassed Client class which here has been named as `SlashClient` with `client = SlashClient()`. - Then using the `command` method of the `CommandTree` we decorate a function with it as `client.tree` is an instance of `CommandTree` for the current application. The command function takes a default argument as said, which acts as the interaction that took place. Catching up is `await interaction.response.send_message("pong")` which sends back a message to the slash command invoker. - And the classic old `client.run("token")` is used to connect the client to the discord gateway. - Note that the `send_message` is a method of the `InteractionResponse` class and `interaction.response` in this case is an instance of the `InteractionResponse` object. The `send_message` method will not function if the response is not sent within 3 seconds of command invocation. I will discuss about how to handle this issue later following the gist. @@ -117,7 +117,7 @@ import discord class SlashBot(commands.Bot): def __init__(self) -> None: super().__init__(command_prefix=".", intents=discord.Intents.default()) - + async def setup_hook(self) -> None: self.tree.copy_global_to(guild=discord.Object(id=12345678900987654)) await self.tree.sync() @@ -156,20 +156,20 @@ from discord import app_commands class MySlashCog(commands.Cog): def __init__(self, bot: commands.Bot) -> None: self.bot = bot - + @app_commands.command(name="ping", description="...") async def _ping(self, interaction: discord.Interaction): await interaction.response.send_message("pong!") - + class MySlashBot(commands.Bot): def __init__(self) -> None: super().__init__(command_prefix="!", intents=discord.Intents.default()) - + async def setup_hook(self) -> None: await self.add_cog(MySlashCog(self)) await self.tree.copy_global_to(discord.Object(id=123456789098765432)) await self.tree.sync() - + bot = MySlashBot() bot.run("token") @@ -207,20 +207,20 @@ class MySlashGroupCog(commands.Cog): @app_commands.command(name="ping", description="...") async def _ping(self, interaction: discord.) -> None: await interaction.response.send_message("pong!") - + @group.command(name="command", description="...") async def _cmd(self, interaction: discord.Interaction) -> None: await interaction.response.send_message("uwu") - + class MySlashBot(commands.Bot): def __init__(self) -> None: super().__init__(command_prefix="!", intents=discord.Intents.default()) - + async def setup_hook(self) -> None: await self.add_cog(MySlashGroupCog(self)) await self.tree.copy_global_to(discord.Object(id=123456789098765432)) await self.tree.sync() - + bot = MySlashBot() bot.run("token") @@ -246,20 +246,20 @@ class MySlashGroup(commands.GroupCog, name="uwu"): @app_commands.command(name="ping", description="...") async def _ping(self, interaction: discord.) -> None: await interaction.response.send_message("pong!") - + @app_commands.command(name="command", description="...") async def _cmd(self, interaction: discord.Interaction) -> None: await interaction.response.send_message("uwu") - + class MySlashBot(commands.Bot): def __init__(self) -> None: super().__init__(command_prefix="!", intents=discord.Intents.default()) - + async def setup_hook(self) -> None: await self.add_cog(MySlashGroup(self)) await self.tree.copy_global_to(discord.Object(id=123456789098765432)) await self.tree.sync() - + bot = MySlashBot() bot.run("token") @@ -271,7 +271,7 @@ __**EXPLANATION**__ # Some common methods and features used for slash commands. ---- +--- ### A common function used for slash commands is the `describe` function. This is used to add descriptions to the arguments of a slash command. The command function can decorated with this function. It goes by the following syntax as shown below. @@ -353,13 +353,13 @@ import discord class Bot(commands.Bot): def __init__(self): super().__init__(command_prefix="uwu", intents=discord.Intents.all()) - - + + async def setup_hook(self): - self.tree.copy_global_to(guild=discord.Object(id=12345678909876543)) + self.tree.copy_global_to(guild=discord.Object(id=12345678909876543)) await self.tree.sync() - - + + bot = Bot() @bot.tree.command(name="ping") @@ -374,7 +374,7 @@ bot.run("token") __**EXPLANATION**__ - The first argument the `cooldown` method takes is the amount of times the command can be run in a specific period of time. -- The second argument it takes is the period of time in which the command can be run the specified number of times. +- The second argument it takes is the period of time in which the command can be run the specified number of times. - The `CommandOnCooldown` exception can be handled using an error handler. I will discuss about making an error handler for slash commands later in the gist. -- cgit v1.2.3 From b6ac1f2f7c402411a29f1cfee10d79abb7c001d0 Mon Sep 17 00:00:00 2001 From: Ash <92868529+Ash-02014@users.noreply.github.com> Date: Fri, 1 Jul 2022 15:59:16 +0100 Subject: Update pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md Co-authored-by: Robin <74519799+Robin5605@users.noreply.github.com> --- .../apps/content/resources/guides/python-guides/discord-app-commands.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md b/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md index e6095252..431ab095 100644 --- a/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md +++ b/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md @@ -102,7 +102,7 @@ __**EXPLANATION**__ - `import discord` imports the **discord.py** package. - `class SlashClient(discord.Client)` is a class subclassing **Client**. Though there is no particular reason except readability to subclass the **Client** class, using the `Client.setup_hook = my_func` is equally valid. -- Next up `super().__init__(...)` runs the `__init__` function of the **Client** class, this is equivalent to `discord.Client(...)`. Then, `self.tree = discord.app_commands.CommandTree(self)` creates a CommandTree which acts as the container for slash commaands. +- Next up `super().__init__(...)` runs the `__init__` function of the **Client** class, this is equivalent to `discord.Client(...)`. Then, `self.tree = discord.app_commands.CommandTree(self)` creates a CommandTree which acts as the container for slash commands, and binds it to the `discord.Client` subclass instance, so wherever you have access to it, you will also have access to the command tree. - Then in the `setup_hook`, `self.tree.copy_global_to(...)` adds the slash command to the guild of which the ID is provided as a `discord.Object` object. Further up, `self.tree.sync()` updates the API with any changes to the slash commands. - Finishing up with the **Client** subclass, we create an instance of the subclassed Client class which here has been named as `SlashClient` with `client = SlashClient()`. - Then using the `command` method of the `CommandTree` we decorate a function with it as `client.tree` is an instance of `CommandTree` for the current application. The command function takes a default argument as said, which acts as the interaction that took place. Catching up is `await interaction.response.send_message("pong")` which sends back a message to the slash command invoker. -- cgit v1.2.3 From b237e81890836343295bd84b80ff5b34ef05ab92 Mon Sep 17 00:00:00 2001 From: Ash <92868529+Ash-02014@users.noreply.github.com> Date: Fri, 1 Jul 2022 15:59:26 +0100 Subject: Update pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md Co-authored-by: Robin <74519799+Robin5605@users.noreply.github.com> --- .../resources/guides/python-guides/discord-app-commands.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md b/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md index 431ab095..1a6e2453 100644 --- a/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md +++ b/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md @@ -6,7 +6,15 @@ description: A simple guide to creating slash commands within discord.py! --- -Upon resumption of the most popular discord API wrapper library for python, `discord.py`, while catching on to the latest features of the discord API, there have been numerous changes with addition of features to the library. Some additions to the library are -> Buttons support, Select Menus Support, Forms (AKA Modals), Slash Commands (AKA Application Commands) and a bunch of more handy features! All the changes can be found [here](https://discordpy.readthedocs.io/en/latest/migrating.html). Original discord.py gist regarding resumption can be found [here](https://gist.github.com/Rapptz/c4324f17a80c94776832430007ad40e6). +Upon resumption of the most popular discord API wrapper library for python, `discord.py`, while catching on to the latest features of the discord API, there have been numerous changes with addition of features to the library. Some additions to the library include: + +- Buttons support +- Select Menus support +- Forms (AKA Modals) +- Slash commands (AKA Application Commands) +...and a bunch more handy features! + +All the changes can be found [here](https://discordpy.readthedocs.io/en/latest/migrating.html). Original discord.py gist regarding resumption can be found [here](https://gist.github.com/Rapptz/c4324f17a80c94776832430007ad40e6). # Why this gist? -- cgit v1.2.3 From 823a6d2e3ebe7667b6bb2e2cc49635b4684cefcc Mon Sep 17 00:00:00 2001 From: Ash <92868529+Ash-02014@users.noreply.github.com> Date: Fri, 1 Jul 2022 16:00:51 +0100 Subject: Update pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md A good point indeed Co-authored-by: Robin <74519799+Robin5605@users.noreply.github.com> --- .../apps/content/resources/guides/python-guides/discord-app-commands.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md b/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md index 1a6e2453..d5f204b0 100644 --- a/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md +++ b/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md @@ -111,7 +111,7 @@ __**EXPLANATION**__ - `import discord` imports the **discord.py** package. - `class SlashClient(discord.Client)` is a class subclassing **Client**. Though there is no particular reason except readability to subclass the **Client** class, using the `Client.setup_hook = my_func` is equally valid. - Next up `super().__init__(...)` runs the `__init__` function of the **Client** class, this is equivalent to `discord.Client(...)`. Then, `self.tree = discord.app_commands.CommandTree(self)` creates a CommandTree which acts as the container for slash commands, and binds it to the `discord.Client` subclass instance, so wherever you have access to it, you will also have access to the command tree. -- Then in the `setup_hook`, `self.tree.copy_global_to(...)` adds the slash command to the guild of which the ID is provided as a `discord.Object` object. Further up, `self.tree.sync()` updates the API with any changes to the slash commands. +- Then in the `setup_hook`, `self.tree.copy_global_to(...)` adds the slash command to the guild of which the ID is provided as a `discord.Object` object. Further up, `self.tree.sync()` updates the API with any changes to the slash commands. **Without calling this method, your changes will only be saved locally and will NOT show up on Discord!** - Finishing up with the **Client** subclass, we create an instance of the subclassed Client class which here has been named as `SlashClient` with `client = SlashClient()`. - Then using the `command` method of the `CommandTree` we decorate a function with it as `client.tree` is an instance of `CommandTree` for the current application. The command function takes a default argument as said, which acts as the interaction that took place. Catching up is `await interaction.response.send_message("pong")` which sends back a message to the slash command invoker. - And the classic old `client.run("token")` is used to connect the client to the discord gateway. -- cgit v1.2.3 From 2a8b2f4b764c83584972224ad2cf8c625e6712e1 Mon Sep 17 00:00:00 2001 From: Ash <92868529+Ash-02014@users.noreply.github.com> Date: Fri, 1 Jul 2022 16:01:33 +0100 Subject: Update pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md Co-authored-by: Robin <74519799+Robin5605@users.noreply.github.com> --- .../apps/content/resources/guides/python-guides/discord-app-commands.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md b/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md index d5f204b0..7d7239e2 100644 --- a/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md +++ b/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md @@ -133,7 +133,7 @@ class SlashBot(commands.Bot): bot = SlashBot() @bot.tree.command(name="ping", description="...") -async def _ping(interaction: discord.Interaction) -> None: +async def ping(interaction: discord.Interaction) -> None: await interaction.response.send_message("pong") bot.run("token") -- cgit v1.2.3 From 7e5fe0bab6e2b97d2fc4e855d05573ca02801caf Mon Sep 17 00:00:00 2001 From: Ash <92868529+Ash-02014@users.noreply.github.com> Date: Fri, 1 Jul 2022 16:03:16 +0100 Subject: Update pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md Co-authored-by: Robin <74519799+Robin5605@users.noreply.github.com> --- .../apps/content/resources/guides/python-guides/discord-app-commands.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md b/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md index 7d7239e2..1f5a0ff3 100644 --- a/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md +++ b/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md @@ -166,7 +166,7 @@ class MySlashCog(commands.Cog): self.bot = bot @app_commands.command(name="ping", description="...") - async def _ping(self, interaction: discord.Interaction): + async def ping(self, interaction: discord.Interaction): await interaction.response.send_message("pong!") class MySlashBot(commands.Bot): -- cgit v1.2.3 From fff37e8bfd0f3ace81235a00b0d343cd8ba21383 Mon Sep 17 00:00:00 2001 From: Ash <92868529+Ash-02014@users.noreply.github.com> Date: Fri, 1 Jul 2022 16:03:36 +0100 Subject: Update pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md quite alright Co-authored-by: Robin <74519799+Robin5605@users.noreply.github.com> --- .../apps/content/resources/guides/python-guides/discord-app-commands.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md b/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md index 1f5a0ff3..65c0b025 100644 --- a/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md +++ b/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md @@ -410,7 +410,7 @@ async def on_tree_error(interaction: discord.Interaction, error: app_commands.Ap if isinstance(error, app_commands.CommandOnCooldown): return await interaction.response.send_message(f"Command is currently on cooldown! Try again in **{error.retry_after:.2f}** seconds!") - elif isinstance(..., ...): + elif isinstance(error, ...): ... else: -- cgit v1.2.3 From ca3cb63131839258cdec5d28317a90193a15f082 Mon Sep 17 00:00:00 2001 From: Ash <92868529+Ash-02014@users.noreply.github.com> Date: Fri, 1 Jul 2022 16:03:57 +0100 Subject: Update pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md Co-authored-by: Robin <74519799+Robin5605@users.noreply.github.com> --- .../apps/content/resources/guides/python-guides/discord-app-commands.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md b/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md index 65c0b025..02a56ba6 100644 --- a/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md +++ b/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md @@ -436,7 +436,7 @@ bot = commands.Bot(command_prefix="!", intents=discord.Intents.default()) #sync commands @bot.tree.command(name="ping") -app_commands.checks.cooldown(1, 30) +@app_commands.checks.cooldown(1, 30) async def ping(interaction: discord.Interaction): await interaction.response.send_message("pong!") -- cgit v1.2.3 From 9344c927c4e4deba148202f23b7803c775d698a6 Mon Sep 17 00:00:00 2001 From: Ash <92868529+Ash-02014@users.noreply.github.com> Date: Fri, 1 Jul 2022 16:05:07 +0100 Subject: Update pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md Co-authored-by: Robin <74519799+Robin5605@users.noreply.github.com> --- .../apps/content/resources/guides/python-guides/discord-app-commands.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md b/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md index 02a56ba6..917c63a1 100644 --- a/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md +++ b/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md @@ -445,7 +445,7 @@ async def ping_error(interaction: discord.Interaction, error: app_commands.AppCo if isinstance(error, app_commands.CommandOnCooldown): return await interaction.response.send_message(f"Command is currently on cooldown! Try again in **{error.retry_after:.2f}** seconds!") - elif isinstance(..., ...): + elif isinstance(error, ...): ... else: -- cgit v1.2.3 From 7475676fddb5b815e8ff24de3b5d094cda66439e Mon Sep 17 00:00:00 2001 From: Blue-Puddle Date: Fri, 1 Jul 2022 16:15:16 +0100 Subject: add app_commands.md --- .../resources/guides/python-guides/app_commands.md | 448 ++++++++++++++++++++ .../guides/python-guides/discord-app-commands.md | 459 --------------------- 2 files changed, 448 insertions(+), 459 deletions(-) create mode 100644 pydis_site/apps/content/resources/guides/python-guides/app_commands.md delete mode 100644 pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/python-guides/app_commands.md b/pydis_site/apps/content/resources/guides/python-guides/app_commands.md new file mode 100644 index 00000000..d97b849a --- /dev/null +++ b/pydis_site/apps/content/resources/guides/python-guides/app_commands.md @@ -0,0 +1,448 @@ +# DISCORD.PY RESUMATION CHANGES + +--- + +Upon resumation of the most popular discord API wrapper library for python, `discord.py`, while catching on to the latest features of the discord API, there have been numerous changes with addition of features to the library. Some additions to the library are -> Buttons support, Select Menus Support, Forms (AKA Modals), Slash Commands (AKA Application Commands) and a bunch of more handy features! All the changes can be found [here](https://discordpy.readthedocs.io/en/latest/migrating.html). Original discord.py gist regarding resumation can be found [here](https://gist.github.com/Rapptz/c4324f17a80c94776832430007ad40e6). + +# Why this gist? + +--- + +This Gist is being created as an update to slash commands (app commands) with explanation and examples. This Gist mainly focuses on **SLASH COMMANDS** for discord.py 2.0 (and above)! + +# What Are Slash Commands? + +--- + +Slash Commands are the exciting way to build and interact with bots on Discord. With Slash Commands, all you have to do is type / and you're ready to use your favourite bot. You can easily see all the commands a bot has, and validation and error handling help you get the command right the first time. + +# Install the latest version for discord.py + +--- +To use slash commands with discord.py. The latest up-to-date version has to be installed. Make sure that the version is 2.0 or above! +And make sure to uninstall any third party libraries that support slash commands for discord.py (if any) as a few of them [monkey patch](https://en.wikipedia.org/wiki/Monkey_patch#:~:text=A%20monkey%20patch%20is%20a,running%20instance%20of%20the%20program) discord.py! + +The latest and up-to-date usable discord.py version can be installed using `pip install -U git+https://github.com/Rapptz/discord.py`. + +If you get an error such as: `'git' is not recognized...`. [Install git](https://git-scm.com/downloads) for your platform. Go through the required steps for installing git and make sure to enable the `add to path` option while in the installation wizard. After installing git you can run `pip install -U git+https://github.com/Rapptz/discord.py` again to install discord.py 2.0. +**BEFORE MIGRATING TO DISCORD.PY 2.0, PLEASE READ THE CONSEQUENCES OF THE UPDATE [HERE](https://discordpy.readthedocs.io/en/latest/migrating.html)**. + +# Basic Structure for Discord.py Slash Commands! + +--- + +### Note that Slash Commands in discord.py are also referred to as **Application Commmands** and **App Commands** and every *interaction* is a *webhook*. +Slash commands in discord.py are held by a container, [CommandTree](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=commandtree#discord.app_commands.CommandTree). A command tree is required to create slash commands in discord.py. This command tree provides a `command` method which decorates an asynchronous function indicating to discord.py that the decorated function is intended to be a slash command. This asynchronous function expects a default argument which acts as the interaction which took place that invoked the slash command. This default argument is an instance of the **Interaction** class from discord.py. Further up, the command logic takes over the behaviour of the slash command. + +# Fundamentals for this gist! + +--- + + +The fundamental for this gist will remain to be the `setup_hook`. The `setup_hook` is a special asynchronous method of the Client and Bot class which can be overwritten to perform numerous tasks. This method is safe to use as it is always triggered before any events are dispatched, i.e. this method is triggered before the *IDENTIFY* payload is sent to the discord gateway. +Note that methods of the Bot class such as `change_presence` will not work in setup_hook as the current application does not have an active connection to the gateway at this point. + +__**FOLLOWING IS THE EXAMPLE OF HOW A `SETUP_HOOK` FUNCTION CAN BE DEFINED**__ + +```python +import discord + +'''This is one way of creating a "setup_hook" method''' + +class SlashClient(discord.Client): + def __init__(self) -> None: + super().__init__(intents=discord.Intents.default()) + + async def setup_hook(self) -> None: + #perform tasks + +'''Another way of creating a "setup_hook" is as follows''' + +client = discord.Client(intents=discord.Intents.default()) +async def my_setup_hook() -> None: + #perform tasks + +client.setup_hook = my_setup_hook +``` + +# Basic Slash Command application using discord.py. + +#### The `CommandTree` class resides within the `app_commands` of discord.py package. +--- + +## Slash Command Application with a Client + +```python +import discord + +class SlashClient(discord.Client): + def __init__(self) -> None: + super().__init__(intents=discord.Intents.default()) + self.tree = discord.app_commands.CommandTree(self) + + async def setup_hook(self) -> None: + self.tree.copy_global_to(guild=discord.Object(id=12345678900987654)) + await self.tree.sync() + +client = SlashClient() + +@client.tree.command(name="ping", description="...") +async def _ping(interaction: discord.Interaction) -> None: + await interaction.response.send_message("pong") + +client.run("token") +``` + + +__**EXPLANATION**__ + +- `import discord` imports the **discord.py** package. +- `class SlashClient(discord.Client)` is a class subclassing **Client**. Though there is no particular reason except readability to subclass the **Client** class, using the `Client.setup_hook = my_func` is equally valid. +- Next up `super().__init__(...)` runs the `__init__` function of the **Client** class, this is equivalent to `discord.Client(...)`. Then, `self.tree = discord.app_commands.CommandTree(self)` creates a CommandTree which acts as the container for slash commaands. +- Then in the `setup_hook`, `self.tree.copy_global_to(...)` adds the slash command to the guild of which the ID is provided as a `discord.Object` object. Further up, `self.tree.sync()` updates the API with any changes to the slash commands. +- Finishing up with the **Client** subclass, we create an instance of the subclassed Client class which here has been named as `SlashClient` with `client = SlashClient()`. +- Then using the `command` method of the `CommandTree` we decorate a function with it as `client.tree` is an instance of `CommandTree` for the current application. The command function takes a default argument as said, which acts as the interaction that took place. Catching up is `await interaction.response.send_message("pong")` which sends back a message to the slash command invoker. +- And the classic old `client.run("token")` is used to connect the client to the discord gateway. +- Note that the `send_message` is a method of the `InteractionResponse` class and `interaction.response` in this case is an instance of the `InteractionResponse` object. The `send_message` method will not function if the response is not sent within 3 seconds of command invocation. I will discuss about how to handle this issue later following the gist. + +## Slash Command Application with a Bot + +```python +import discord + +class SlashBot(commands.Bot): + def __init__(self) -> None: + super().__init__(command_prefix=".", intents=discord.Intents.default()) + + async def setup_hook(self) -> None: + self.tree.copy_global_to(guild=discord.Object(id=12345678900987654)) + await self.tree.sync() + +bot = SlashBot() + +@bot.tree.command(name="ping", description="...") +async def _ping(interaction: discord.Interaction) -> None: + await interaction.response.send_message("pong") + +bot.run("token") +``` + +The above example shows a basic slash commands within discord.py using the Bot class. + +__**EXPLANATION**__ + +Most of the explanation is the same as the prior example which featured `SlashClient` which was a subclass of **discord.Client**. Though some minor changes are discussed below. + +- The `SlashBot` class now subclasses `discord.ext.commands.Bot` following the passing in of the required arguments to its `__init__` method. +- `discord.ext.commands.Bot` already consists of an instance of the `CommandTree` class which can be accessed using the `tree` property. + +# Slash Commands within a Cog! + +--- + +A cog is a collection of commands, listeners, and optional state to help group commands together. More information on them can be found on the [Cogs](https://discordpy.readthedocs.io/en/latest/ext/commands/cogs.html#ext-commands-cogs) page. + +## An Example to using cogs with discord.py for slash commands! + +```python +import discord +from discord.ext import commands +from discord import app_commands + +class MySlashCog(commands.Cog): + def __init__(self, bot: commands.Bot) -> None: + self.bot = bot + + @app_commands.command(name="ping", description="...") + async def _ping(self, interaction: discord.Interaction): + await interaction.response.send_message("pong!") + +class MySlashBot(commands.Bot): + def __init__(self) -> None: + super().__init__(command_prefix="!", intents=discord.Intents.default()) + + async def setup_hook(self) -> None: + await self.add_cog(MySlashCog(self)) + await self.tree.copy_global_to(discord.Object(id=123456789098765432)) + await self.tree.sync() + +bot = MySlashBot() + +bot.run("token") +``` + +__**EXPLANATION**__ + +- Firstly, `import discord` imports the **discord.py** package. `from discord import app_commands` imports the `app_commands` directory from the **discord.py** root directory. `from discord.ext import commands` imports the commands extension. +- Further up, `class MySlashCog(commands.Cog)` is a class subclassing the `Cog` class. You can read more about this [here](https://discordpy.readthedocs.io/en/latest/ext/commands/cogs.html#ext-commands-cogs). +- `def __init__(self, bot: commands.Bot): self.bot = bot` is the constructor method of the class that is always run when the class is instantiated and that is why we pass in a **Bot** object whenever we create an instance of the cog class. +- Following up is the `@app_commands.command(name="ping", description="...")` decorator. This decorator basically functions the same as a `bot.tree.command` but since the cog currently does not have a **bot**, the `app_commands.command` decorator is used instead. The next two lines follow the same structure for slash commands with **self** added as the first parameter to the function as it is a method of a class. +- The next up lines are mostly the same. +- Talking about the first line inside the `setup_hook` is the `add_cog` method of the **Bot** class. And since **self** acts as the **instance** of the current class, we use **self** to use the `add_cog` method of the **Bot** class as we are inside a subclassed class of the **Bot** class. Then we pass in **self** to the `add_cog` method as the `__init__` function of the **MySlashCog** cog accepts a `Bot` object. +- After that we instantiate the `MySlashBot` class and run the bot using the **run** method which executes our setup_hook function and our commands get loaded and synced. The bot is now ready to use! + +# An Example to using groups with discord.py for slash commands! + +--- + +## An example with optional group! + +```python +import discord +from discord.ext import commands +from discord import app_commands + +class MySlashGroupCog(commands.Cog): + def __init__(self, bot: commands.Bot) -> None: + self.bot = bot + + #-------------------------------------------------------- + group = app_commands.Group(name="uwu", description="...") + #-------------------------------------------------------- + + @app_commands.command(name="ping", description="...") + async def _ping(self, interaction: discord.) -> None: + await interaction.response.send_message("pong!") + + @group.command(name="command", description="...") + async def _cmd(self, interaction: discord.Interaction) -> None: + await interaction.response.send_message("uwu") + +class MySlashBot(commands.Bot): + def __init__(self) -> None: + super().__init__(command_prefix="!", intents=discord.Intents.default()) + + async def setup_hook(self) -> None: + await self.add_cog(MySlashGroupCog(self)) + await self.tree.copy_global_to(discord.Object(id=123456789098765432)) + await self.tree.sync() + +bot = MySlashBot() + +bot.run("token") +``` + +__**EXPLANATION**__ +- The only difference used here is `group = app_commands.Group(name="uwu", description="...")` and `group.command`. `app_commands.Group` is used to initiate a group while `group.command` registers a command under a group. For example, the ping command can be run using **/ping** but this is not the case for group commands. They are registered with the format of `group_name command_name`. So here, the **command** command of the **uwu** group would be run using **/uwu command**. Note that only group commands can have a single space between them. + +--- + +## An example with a **Group** subclass! + +```python +import discord +from discord.ext import commands +from discord import app_commands + +class MySlashGroup(app_commands.Group, name="uwu"): + def __init__(self, bot: commands.Bot) -> None: + self.bot = bot + super().__init__() + + @app_commands.command(name="ping", description="...") + async def _ping(self, interaction: discord.) -> None: + await interaction.response.send_message("pong!") + + @app_commands.command(name="command", description="...") + async def _cmd(self, interaction: discord.Interaction) -> None: + await interaction.response.send_message("uwu") + +class MySlashBot(commands.Bot): + def __init__(self) -> None: + super().__init__(command_prefix="!", intents=discord.Intents.default()) + + async def setup_hook(self) -> None: + await self.add_cog(MySlashGroup(self)) + await self.tree.copy_global_to(discord.Object(id=123456789098765432)) + await self.tree.sync() + +bot = MySlashBot() + +bot.run("token") +``` + +__**EXPLANATION**__ +- The only difference here too is that the `MySlashGroup` class directly subclasses the **Group** class from discord.app_commands which automatically registers all the methods within the group class to be commands of that specific group. So now, the commands such as `ping` can be run using **/uwu ping** and `command` using **/uwu command**. + + +# Some common methods and features used for slash commands. + +--- + +### A common function used for slash commands is the `describe` function. This is used to add descriptions to the arguments of a slash command. The command function can decorated with this function. It goes by the following syntax as shown below. + +```python +from discord.ext import commands +from discord import app_commands +import discord + +bot = commands.Bot(command_prefix=".", intents=discord.Intents.default()) +#sync the commands + +@bot.tree.command(name="echo", description="...") +@app_commands.describe(text="The text to send!", channel="The channel to send the message in!") +async def _echo(interaction: discord.Interaction, text: str, channel: discord.TextChannel=None): + channel = interaction.channel or channel + await channel.send(text) +``` + +### Another common issue that most people come across is the time duraction of sending a message with `send_message`. This issue can be tackled by deferring the interaction response using the `defer` method of the `InteractionResponse` class. An example for fixing this issue is shown below. + +```python +import discord +from discord.ext import commands +import asyncio + +bot = commands.Bot(command_prefix="!", intents=discord.Intents.default()) +#sync the commands + +@bot.tree.command(name="time", description="...") +async def _time(interaction: discord.Interaction, time_to_wait: int): + # ------------------------------------------------------------- + await interaction.response.defer(ephemeral=True, thinking=True) + # ------------------------------------------------------------- + await interaction.edit_original_message(content=f"I will notify you after {time_to_wait} seconds have passed!") + await asyncio.sleep(time_to_wait) + await interaction.edit_original_message(content=f"{interaction.user.mention}, {time_to_wait} seconds have already passed!") +``` + +# Checking for Permissions and Roles! + +--- + +To add a permissions check to a command, the methods are imported through `discord.app_commands.checks`. To check for a member's permissions, the function can be decorated with the [discord.app_commands.checks.has_permissions](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=has_permissions#discord.app_commands.checks.has_permissions) method. An example to this as follows. + +```py +from discord import app_commands +from discord.ext import commands +import discord + +bot = commands.Bot(command_prefix="!", intents=discord.Intents.default()) +#sync commands + +@bot.tree.command(name="ping") +@app_commands.checks.has_permissions(manage_messages=True, manage_channels=True) #example permissions +async def _ping(interaction: discord.Interaction): + await interaction.response.send_message("pong!") + +``` + +If the check fails, it will raise a `MissingPermissions` error which can be handled within an app commands error handler! I will discuss about making an error handler later in the gist. All the permissions can be found [here](https://discordpy.readthedocs.io/en/latest/api.html?highlight=discord%20permissions#discord.Permissions). + +Other methods that you can decorate the commands with are - +- `bot_has_permissions` | This checks if the bot has the required permissions for executing the slash command. This raises a [BotMissingPermissions](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.BotMissingPermissions) exception. +- `has_role` | This checks if the slash command user has the required role or not. Only **ONE** role name or role ID can be passed to this. If the name is being passed, make sure to have the exact same name as the role name. This raises a [MissingRole](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.MissingRole) exception. +- To pass in several role names or role IDs, `has_any_role` can be used to decorate a command. This raises two exceptions -> [MissingAnyRole](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.MissingAnyRole) and [NoPrivateMessage](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.NoPrivateMessage) + + +# Adding cooldowns to slash commands! + +--- + +Slash Commands within discord.py can be applied cooldowns to in order to prevent spamming of the commands. This can be done through the `discord.app_commands.checks.cooldown` method which can be used to decorate a slash command function and register a cooldown to the function. This raises a [CommandOnCooldown](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=checks%20cooldown#discord.app_commands.CommandOnCooldown) exception if the command is currently on cooldown. +An example is as follows. + +```python +from discord.ext import commands +import discord + +class Bot(commands.Bot): + def __init__(self): + super().__init__(command_prefix="uwu", intents=discord.Intents.all()) + + + async def setup_hook(self): + self.tree.copy_global_to(guild=discord.Object(id=12345678909876543)) + await self.tree.sync() + + +bot = Bot() + +@bot.tree.command(name="ping") +# ----------------------------------------- +@discord.app_commands.checks.cooldown(1, 30) +# ----------------------------------------- +async def ping(interaction: discord.Interaction): + await interaction.response.send_message("pong!") + +bot.run("token") +``` + +__**EXPLANATION**__ +- The first argument the `cooldown` method takes is the amount of times the command can be run in a specific period of time. +- The second argument it takes is the period of time in which the command can be run the specified number of times. +- The `CommandOnCooldown` exception can be handled using an error handler. I will discuss about making an error handler for slash commands later in the gist. + + +# Handling errors for slash commands! + +--- + +The Slash Commands exceptions can be handled by overwriting the `on_error` method of the `CommandTree`. The error handler takes two arguments. The first argument is the `Interaction` that took place when the error occurred and the second argument is the error that occurred when the slash commands was invoked. The error is an instance of [discord.app_commands.AppCommandError](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=appcommanderror#discord.app_commands.AppCommandError) which is a subclass of [DiscordException](https://discordpy.readthedocs.io/en/latest/api.html?highlight=discordexception#discord.DiscordException). +An example to creating an error handler for slash commands is as follows. + +```python +from discord.ext import commands +from discord import app_commands +import discord + +bot = commands.Bot(command_prefix="!", intents=discord.Intents.default()) +#sync commands + +@bot.tree.command(name="ping") +@app_commands.checks.cooldown(1, 30) +async def ping(interaction: discord.Interaction): + await interaction.response.send_message("pong!") + +async def on_tree_error(interaction: discord.Interaction, error: app_commands.AppCommandError): + if isinstance(error, app_commands.CommandOnCooldown): + return await interaction.response.send_message(f"Command is currently on cooldown! Try again in **{error.retry_after:.2f}** seconds!") + + elif isinstance(..., ...): + ... + + else: + raise error + +bot.tree.on_error = on_tree_error + +bot.run("token") +``` + +__**EXPLANATION**__ + +First we create a simple function named as `on_tree_error` here. To which the first two required arguments are passed, `Interaction` which is named as `interaction` here and `AppCommandError` which is named as `error` here. Then using simple functions and keywords, we make an error handler like above. Here I have used the `isinstance` function which takes in an object and a base class as the second argument, this function returns a bool value. The `raise error` is just for displayed unhandled errors, i.e. the ones which have not been specificed manually. If this is **removed**, you will not be able to see any exceptions raised due to slash commands and makes debugging the code harder. +After creating the error handler function, we set the function as the error handler for the slash commands. Here, `bot.tree.on_error = on_tree_error` overwrites the default `on_error` method of the **CommandTree** class with our custom error handler which has been named as `on_tree_error` here. + +### Creating an error handler for a specific error! + +```python +from discord.ext import commands +from discord import app_commands +import discord + +bot = commands.Bot(command_prefix="!", intents=discord.Intents.default()) +#sync commands + +@bot.tree.command(name="ping") +app_commands.checks.cooldown(1, 30) +async def ping(interaction: discord.Interaction): + await interaction.response.send_message("pong!") + +@ping.error +async def ping_error(interaction: discord.Interaction, error: app_commands.AppCommandError): + if isinstance(error, app_commands.CommandOnCooldown): + return await interaction.response.send_message(f"Command is currently on cooldown! Try again in **{error.retry_after:.2f}** seconds!") + + elif isinstance(error, ...): + ... + + else: + raise error + +bot.run("token") +``` + +__**EXPLANATION**__ + +Here the command name is simply used to access the `error` method to decorate a function which acts as the `on_error` but for a specific command. Please do not call the `error` method. \ No newline at end of file diff --git a/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md b/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md deleted file mode 100644 index 917c63a1..00000000 --- a/pydis_site/apps/content/resources/guides/python-guides/discord-app-commands.md +++ /dev/null @@ -1,459 +0,0 @@ ---- -title: Slash Commands with discord.py! -description: A simple guide to creating slash commands within discord.py! ---- -# DISCORD.PY RESUMPTION CHANGES - ---- - -Upon resumption of the most popular discord API wrapper library for python, `discord.py`, while catching on to the latest features of the discord API, there have been numerous changes with addition of features to the library. Some additions to the library include: - -- Buttons support -- Select Menus support -- Forms (AKA Modals) -- Slash commands (AKA Application Commands) -...and a bunch more handy features! - -All the changes can be found [here](https://discordpy.readthedocs.io/en/latest/migrating.html). Original discord.py gist regarding resumption can be found [here](https://gist.github.com/Rapptz/c4324f17a80c94776832430007ad40e6). - -# Why this gist? - ---- - -This Gist is being created as an update to slash commands (app commands) with explanation and examples. This Gist mainly focuses on **SLASH COMMANDS** for discord.py 2.0 (and above)! - -# What Are Slash Commands? - ---- - -Slash Commands are the exciting way to build and interact with bots on Discord. With Slash Commands, all you have to do is type / and you're ready to use your favourite bot. You can easily see all the commands a bot has, and validation and error handling help you get the command right the first time. - -# Install the latest version for discord.py - ---- -To use slash commands with discord.py. The latest up-to-date version has to be installed. Make sure that the version is 2.0 or above! -And make sure to uninstall any third party libraries that support slash commands for discord.py (if any) as a few of them [monkey patch](https://en.wikipedia.org/wiki/Monkey_patch#:~:text=A%20monkey%20patch%20is%20a,running%20instance%20of%20the%20program) discord.py! - -The latest and up-to-date usable discord.py version can be installed using `pip install -U git+https://github.com/Rapptz/discord.py`. - -If you get an error such as: `'git' is not recognized...`. [Install git](https://git-scm.com/downloads) for your platform. Go through the required steps for installing git and make sure to enable the `add to path` option while in the installation wizard. After installing git you can run `pip install -U git+https://github.com/Rapptz/discord.py` again to install discord.py 2.0. -**BEFORE MIGRATING TO DISCORD.PY 2.0, PLEASE READ THE CONSEQUENCES OF THE UPDATE [HERE](https://discordpy.readthedocs.io/en/latest/migrating.html)**. - -# Basic Structure for Discord.py Slash Commands! - ---- - -### Note that Slash Commands in discord.py are also referred to as **Application Commmands** and **App Commands** and every *interaction* is a *webhook*. -Slash commands in discord.py are held by a container, [CommandTree](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=commandtree#discord.app_commands.CommandTree). A command tree is required to create slash commands in discord.py. This command tree provides a `command` method which decorates an asynchronous function indicating to discord.py that the decorated function is intended to be a slash command. This asynchronous function expects a default argument which acts as the interaction which took place that invoked the slash command. This default argument is an instance of the **Interaction** class from discord.py. Further up, the command logic takes over the behaviour of the slash command. - -# Fundamentals for this gist! - ---- - - -The fundamental for this gist will remain to be the `setup_hook`. The `setup_hook` is a special asynchronous method of the Client and Bot class which can be overwritten to perform numerous tasks. This method is safe to use as it is always triggered before any events are dispatched, i.e. this method is triggered before the *IDENTIFY* payload is sent to the discord gateway. -Note that methods of the Bot class such as `change_presence` will not work in setup_hook as the current application does not have an active connection to the gateway at this point. - -__**FOLLOWING IS THE EXAMPLE OF HOW A `SETUP_HOOK` FUNCTION CAN BE DEFINED**__ - -```python -import discord - -'''This is one way of creating a "setup_hook" method''' - -class SlashClient(discord.Client): - def __init__(self) -> None: - super().__init__(intents=discord.Intents.default()) - - async def setup_hook(self) -> None: - #perform tasks - -'''Another way of creating a "setup_hook" is as follows''' - -client = discord.Client(intents=discord.Intents.default()) -async def my_setup_hook() -> None: - #perform tasks - -client.setup_hook = my_setup_hook -``` - -# Basic Slash Command application using discord.py. - -#### The `CommandTree` class resides within the `app_commands` of discord.py package. ---- - -## Slash Command Application with a Client - -```python -import discord - -class SlashClient(discord.Client): - def __init__(self) -> None: - super().__init__(intents=discord.Intents.default()) - self.tree = discord.app_commands.CommandTree(self) - - async def setup_hook(self) -> None: - self.tree.copy_global_to(guild=discord.Object(id=12345678900987654)) - await self.tree.sync() - -client = SlashClient() - -@client.tree.command(name="ping", description="...") -async def _ping(interaction: discord.Interaction) -> None: - await interaction.response.send_message("pong") - -client.run("token") -``` - - -__**EXPLANATION**__ - -- `import discord` imports the **discord.py** package. -- `class SlashClient(discord.Client)` is a class subclassing **Client**. Though there is no particular reason except readability to subclass the **Client** class, using the `Client.setup_hook = my_func` is equally valid. -- Next up `super().__init__(...)` runs the `__init__` function of the **Client** class, this is equivalent to `discord.Client(...)`. Then, `self.tree = discord.app_commands.CommandTree(self)` creates a CommandTree which acts as the container for slash commands, and binds it to the `discord.Client` subclass instance, so wherever you have access to it, you will also have access to the command tree. -- Then in the `setup_hook`, `self.tree.copy_global_to(...)` adds the slash command to the guild of which the ID is provided as a `discord.Object` object. Further up, `self.tree.sync()` updates the API with any changes to the slash commands. **Without calling this method, your changes will only be saved locally and will NOT show up on Discord!** -- Finishing up with the **Client** subclass, we create an instance of the subclassed Client class which here has been named as `SlashClient` with `client = SlashClient()`. -- Then using the `command` method of the `CommandTree` we decorate a function with it as `client.tree` is an instance of `CommandTree` for the current application. The command function takes a default argument as said, which acts as the interaction that took place. Catching up is `await interaction.response.send_message("pong")` which sends back a message to the slash command invoker. -- And the classic old `client.run("token")` is used to connect the client to the discord gateway. -- Note that the `send_message` is a method of the `InteractionResponse` class and `interaction.response` in this case is an instance of the `InteractionResponse` object. The `send_message` method will not function if the response is not sent within 3 seconds of command invocation. I will discuss about how to handle this issue later following the gist. - -## Slash Command Application with a Bot - -```python -import discord - -class SlashBot(commands.Bot): - def __init__(self) -> None: - super().__init__(command_prefix=".", intents=discord.Intents.default()) - - async def setup_hook(self) -> None: - self.tree.copy_global_to(guild=discord.Object(id=12345678900987654)) - await self.tree.sync() - -bot = SlashBot() - -@bot.tree.command(name="ping", description="...") -async def ping(interaction: discord.Interaction) -> None: - await interaction.response.send_message("pong") - -bot.run("token") -``` - -The above example shows a basic slash commands within discord.py using the Bot class. - -__**EXPLANATION**__ - -Most of the explanation is the same as the prior example which featured `SlashClient` which was a subclass of **discord.Client**. Though some minor changes are discussed below. - -- The `SlashBot` class now subclasses `discord.ext.commands.Bot` following the passing in of the required arguments to its `__init__` method. -- `discord.ext.commands.Bot` already consists of an instance of the `CommandTree` class which can be accessed using the `tree` property. - -# Slash Commands within a Cog! - ---- - -A cog is a collection of commands, listeners, and optional state to help group commands together. More information on them can be found on the [Cogs](https://discordpy.readthedocs.io/en/latest/ext/commands/cogs.html#ext-commands-cogs) page. - -## An Example to using cogs with discord.py for slash commands! - -```python -import discord -from discord.ext import commands -from discord import app_commands - -class MySlashCog(commands.Cog): - def __init__(self, bot: commands.Bot) -> None: - self.bot = bot - - @app_commands.command(name="ping", description="...") - async def ping(self, interaction: discord.Interaction): - await interaction.response.send_message("pong!") - -class MySlashBot(commands.Bot): - def __init__(self) -> None: - super().__init__(command_prefix="!", intents=discord.Intents.default()) - - async def setup_hook(self) -> None: - await self.add_cog(MySlashCog(self)) - await self.tree.copy_global_to(discord.Object(id=123456789098765432)) - await self.tree.sync() - -bot = MySlashBot() - -bot.run("token") -``` - -__**EXPLANATION**__ - -- Firstly, `import discord` imports the **discord.py** package. `from discord import app_commands` imports the `app_commands` directory from the **discord.py** root directory. `from discord.ext import commands` imports the commands extension. -- Further up, `class MySlashCog(commands.Cog)` is a class subclassing the `Cog` class. You can read more about this [here](https://discordpy.readthedocs.io/en/latest/ext/commands/cogs.html#ext-commands-cogs). -- `def __init__(self, bot: commands.Bot): self.bot = bot` is the constructor method of the class that is always run when the class is instantiated and that is why we pass in a **Bot** object whenever we create an instance of the cog class. -- Following up is the `@app_commands.command(name="ping", description="...")` decorator. This decorator basically functions the same as a `bot.tree.command` but since the cog currently does not have a **bot**, the `app_commands.command` decorator is used instead. The next two lines follow the same structure for slash commands with **self** added as the first parameter to the function as it is a method of a class. -- The next up lines are mostly the same. -- Talking about the first line inside the `setup_hook` is the `add_cog` method of the **Bot** class. And since **self** acts as the **instance** of the current class, we use **self** to use the `add_cog` method of the **Bot** class as we are inside a subclassed class of the **Bot** class. Then we pass in **self** to the `add_cog` method as the `__init__` function of the **MySlashCog** cog accepts a `Bot` object. -- After that we instantiate the `MySlashBot` class and run the bot using the **run** method which executes our setup_hook function and our commands get loaded and synced. The bot is now ready to use! - -# An Example to using groups with discord.py for slash commands! - ---- - -## An example with optional group! - -```python -import discord -from discord.ext import commands -from discord import app_commands - -class MySlashGroupCog(commands.Cog): - def __init__(self, bot: commands.Bot) -> None: - self.bot = bot - - #-------------------------------------------------------- - group = app_commands.Group(name="uwu", description="...") - #-------------------------------------------------------- - - @app_commands.command(name="ping", description="...") - async def _ping(self, interaction: discord.) -> None: - await interaction.response.send_message("pong!") - - @group.command(name="command", description="...") - async def _cmd(self, interaction: discord.Interaction) -> None: - await interaction.response.send_message("uwu") - -class MySlashBot(commands.Bot): - def __init__(self) -> None: - super().__init__(command_prefix="!", intents=discord.Intents.default()) - - async def setup_hook(self) -> None: - await self.add_cog(MySlashGroupCog(self)) - await self.tree.copy_global_to(discord.Object(id=123456789098765432)) - await self.tree.sync() - -bot = MySlashBot() - -bot.run("token") -``` - -__**EXPLANATION**__ -- The only difference used here is `group = app_commands.Group(name="uwu", description="...")` and `group.command`. `app_commands.Group` is used to initiate a group while `group.command` registers a command under a group. For example, the ping command can be run using **/ping** but this is not the case for group commands. They are registered with the format of `group_name command_name`. So here, the **command** command of the **uwu** group would be run using **/uwu command**. Note that only group commands can have a single space between them. - ---- - -## An example with a **Group** subclass! - -```python -import discord -from discord.ext import commands -from discord import app_commands - -class MySlashGroup(commands.GroupCog, name="uwu"): - def __init__(self, bot: commands.Bot) -> None: - self.bot = bot - super().__init__() - - @app_commands.command(name="ping", description="...") - async def _ping(self, interaction: discord.) -> None: - await interaction.response.send_message("pong!") - - @app_commands.command(name="command", description="...") - async def _cmd(self, interaction: discord.Interaction) -> None: - await interaction.response.send_message("uwu") - -class MySlashBot(commands.Bot): - def __init__(self) -> None: - super().__init__(command_prefix="!", intents=discord.Intents.default()) - - async def setup_hook(self) -> None: - await self.add_cog(MySlashGroup(self)) - await self.tree.copy_global_to(discord.Object(id=123456789098765432)) - await self.tree.sync() - -bot = MySlashBot() - -bot.run("token") -``` - -__**EXPLANATION**__ -- The only difference here too is that the `MySlashGroup` class directly subclasses the **GroupCog** class from discord.ext.commands which automatically registers all the methods within the group class to be commands of that specific group. So now, the commands such as `ping` can be run using **/uwu ping** and `command` using **/uwu command**. - - -# Some common methods and features used for slash commands. - ---- - -### A common function used for slash commands is the `describe` function. This is used to add descriptions to the arguments of a slash command. The command function can decorated with this function. It goes by the following syntax as shown below. - -```python -from discord.ext import commands -from discord import app_commands -import discord - -bot = commands.Bot(command_prefix=".", intents=discord.Intents.default()) -#sync the commands - -@bot.tree.command(name="echo", description="...") -@app_commands.describe(text="The text to send!", channel="The channel to send the message in!") -async def _echo(interaction: discord.Interaction, text: str, channel: discord.TextChannel=None): - channel = interaction.channel or channel - await channel.send(text) -``` - -### Another common issue that most people come across is the time duraction of sending a message with `send_message`. This issue can be tackled by deferring the interaction response using the `defer` method of the `InteractionResponse` class. An example for fixing this issue is shown below. - -```python -import discord -from discord.ext import commands -import asyncio - -bot = commands.Bot(command_prefix="!", intents=discord.Intents.default()) -#sync the commands - -@bot.tree.command(name="time", description="...") -async def _time(interaction: discord.Interaction, time_to_wait: int): - # ------------------------------------------------------------- - await interaction.response.defer(ephemeral=True, thinking=True) - # ------------------------------------------------------------- - await interaction.edit_original_message(content=f"I will notify you after {time_to_wait} seconds have passed!") - await asyncio.sleep(time_to_wait) - await interaction.edit_original_message(content=f"{interaction.user.mention}, {time_to_wait} seconds have already passed!") -``` - -# Checking for Permissions and Roles! - ---- - -To add a permissions check to a command, the methods are imported through `discord.app_commands.checks`. To check for a member's permissions, the function can be decorated with the [discord.app_commands.checks.has_permissions](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=has_permissions#discord.app_commands.checks.has_permissions) method. An example to this as follows. - -```py -from discord import app_commands -from discord.ext import commands -import discord - -bot = commands.Bot(command_prefix="!", intents=discord.Intents.default()) -#sync commands - -@bot.tree.command(name="ping") -@app_commands.checks.has_permissions(manage_messages=True, manage_channels=True) #example permissions -async def _ping(interaction: discord.Interaction): - await interaction.response.send_message("pong!") - -``` - -If the check fails, it will raise a `MissingPermissions` error which can be handled within an app commands error handler! I will discuss about making an error handler later in the gist. All the permissions can be found [here](https://discordpy.readthedocs.io/en/latest/api.html?highlight=discord%20permissions#discord.Permissions). - -Other methods that you can decorate the commands with are - -- `bot_has_permissions` | This checks if the bot has the required permissions for executing the slash command. This raises a [BotMissingPermissions](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.BotMissingPermissions) exception. -- `has_role` | This checks if the slash command user has the required role or not. Only **ONE** role name or role ID can be passed to this. If the name is being passed, make sure to have the exact same name as the role name. This raises a [MissingRole](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.MissingRole) exception. -- To pass in several role names or role IDs, `has_any_role` can be used to decorate a command. This raises two exceptions -> [MissingAnyRole](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.MissingAnyRole) and [NoPrivateMessage](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.NoPrivateMessage) - - -# Adding cooldowns to slash commands! - ---- - -Slash Commands within discord.py can be applied cooldowns to in order to prevent spamming of the commands. This can be done through the `discord.app_commands.checks.cooldown` method which can be used to decorate a slash command function and register a cooldown to the function. This raises a [CommandOnCooldown](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=checks%20cooldown#discord.app_commands.CommandOnCooldown) exception if the command is currently on cooldown. -An example is as follows. - -```python -from discord.ext import commands -import discord - -class Bot(commands.Bot): - def __init__(self): - super().__init__(command_prefix="uwu", intents=discord.Intents.all()) - - - async def setup_hook(self): - self.tree.copy_global_to(guild=discord.Object(id=12345678909876543)) - await self.tree.sync() - - -bot = Bot() - -@bot.tree.command(name="ping") -# ----------------------------------------- -@discord.app_commands.checks.cooldown(1, 30) -# ----------------------------------------- -async def ping(interaction: discord.Interaction): - await interaction.response.send_message("pong!") - -bot.run("token") -``` - -__**EXPLANATION**__ -- The first argument the `cooldown` method takes is the amount of times the command can be run in a specific period of time. -- The second argument it takes is the period of time in which the command can be run the specified number of times. -- The `CommandOnCooldown` exception can be handled using an error handler. I will discuss about making an error handler for slash commands later in the gist. - - -# Handling errors for slash commands! - ---- - -The Slash Commands exceptions can be handled by overwriting the `on_error` method of the `CommandTree`. The error handler takes two arguments. The first argument is the `Interaction` that took place when the error occurred and the second argument is the error that occurred when the slash commands was invoked. The error is an instance of [discord.app_commands.AppCommandError](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=appcommanderror#discord.app_commands.AppCommandError) which is a subclass of [DiscordException](https://discordpy.readthedocs.io/en/latest/api.html?highlight=discordexception#discord.DiscordException). -An example to creating an error handler for slash commands is as follows. - -```python -from discord.ext import commands -from discord import app_commands -import discord - -bot = commands.Bot(command_prefix="!", intents=discord.Intents.default()) -#sync commands - -@bot.tree.command(name="ping") -app_commands.checks.cooldown(1, 30) -async def ping(interaction: discord.Interaction): - await interaction.response.send_message("pong!") - -async def on_tree_error(interaction: discord.Interaction, error: app_commands.AppCommandError): - if isinstance(error, app_commands.CommandOnCooldown): - return await interaction.response.send_message(f"Command is currently on cooldown! Try again in **{error.retry_after:.2f}** seconds!") - - elif isinstance(error, ...): - ... - - else: - raise error - -bot.tree.on_error = on_tree_error - -bot.run("token") -``` - -__**EXPLANATION**__ - -First we create a simple function named as `on_tree_error` here. To which the first two required arguments are passed, `Interaction` which is named as `interaction` here and `AppCommandError` which is named as `error` here. Then using simple functions and keywords, we make an error handler like above. Here I have used the `isinstance` function which takes in an object and a base class as the second argument, this function returns a bool value. After creating the error handler function, we set the function as the error handler for the slash commands. Here, `bot.tree.on_error = on_tree_error` overwrites the default `on_error` method of the **CommandTree** class with our custom error handler which has been named as `on_tree_error` here. - -### Creating an error handler for a specific error! - -```python -from discord.ext import commands -from discord import app_commands -import discord - -bot = commands.Bot(command_prefix="!", intents=discord.Intents.default()) -#sync commands - -@bot.tree.command(name="ping") -@app_commands.checks.cooldown(1, 30) -async def ping(interaction: discord.Interaction): - await interaction.response.send_message("pong!") - -@ping.error -async def ping_error(interaction: discord.Interaction, error: app_commands.AppCommandError): - if isinstance(error, app_commands.CommandOnCooldown): - return await interaction.response.send_message(f"Command is currently on cooldown! Try again in **{error.retry_after:.2f}** seconds!") - - elif isinstance(error, ...): - ... - - else: - raise error - -bot.run("token") -``` - -__**EXPLANATION**__ - -Here the command name is simply used to access the `error` method to decorate a function which acts as the `on_error` but for a specific command. Please do not call the `error` method. -- cgit v1.2.3 From 589ffad6935a155df4751401f884c523c777b6c6 Mon Sep 17 00:00:00 2001 From: Blue-Puddle Date: Fri, 1 Jul 2022 16:20:33 +0100 Subject: fix app_commands.md --- pydis_site/apps/content/resources/guides/python-guides/app_commands.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/python-guides/app_commands.md b/pydis_site/apps/content/resources/guides/python-guides/app_commands.md index d97b849a..821ac577 100644 --- a/pydis_site/apps/content/resources/guides/python-guides/app_commands.md +++ b/pydis_site/apps/content/resources/guides/python-guides/app_commands.md @@ -411,7 +411,7 @@ bot.run("token") __**EXPLANATION**__ -First we create a simple function named as `on_tree_error` here. To which the first two required arguments are passed, `Interaction` which is named as `interaction` here and `AppCommandError` which is named as `error` here. Then using simple functions and keywords, we make an error handler like above. Here I have used the `isinstance` function which takes in an object and a base class as the second argument, this function returns a bool value. The `raise error` is just for displayed unhandled errors, i.e. the ones which have not been specificed manually. If this is **removed**, you will not be able to see any exceptions raised due to slash commands and makes debugging the code harder. +First we create a simple function named as `on_tree_error` here. To which the first two required arguments are passed, `Interaction` which is named as `interaction` here and `AppCommandError` which is named as `error` here. Then using simple functions and keywords, we make an error handler like above. Here I have used the `isinstance` function which takes in an object and a base class as the second argument, this function returns a bool value. The `raise error` is just for displaying unhandled errors, i.e. the ones which have not been handled manually. If this is **removed**, you will not be able to see any exceptions raised by slash commands and makes debugging the code harder. After creating the error handler function, we set the function as the error handler for the slash commands. Here, `bot.tree.on_error = on_tree_error` overwrites the default `on_error` method of the **CommandTree** class with our custom error handler which has been named as `on_tree_error` here. ### Creating an error handler for a specific error! -- cgit v1.2.3 From 5144f83c5cbd0571d1ca46f19da61860745d5f7e Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Sun, 6 Nov 2022 02:20:11 +0100 Subject: add thread_id column to the nomination table --- .../api/migrations/0085_add_thread_id_to_nomination.py | 18 ++++++++++++++++++ pydis_site/apps/api/models/bot/nomination.py | 5 +++++ 2 files changed, 23 insertions(+) create mode 100644 pydis_site/apps/api/migrations/0085_add_thread_id_to_nomination.py (limited to 'pydis_site') diff --git a/pydis_site/apps/api/migrations/0085_add_thread_id_to_nomination.py b/pydis_site/apps/api/migrations/0085_add_thread_id_to_nomination.py new file mode 100644 index 00000000..cb216a62 --- /dev/null +++ b/pydis_site/apps/api/migrations/0085_add_thread_id_to_nomination.py @@ -0,0 +1,18 @@ +# Generated by Django 4.1.2 on 2022-11-05 23:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0084_infraction_last_applied'), + ] + + operations = [ + migrations.AddField( + model_name='nomination', + name='thread_id', + field=models.BigIntegerField(blank=True, help_text="The nomination vote thread's id", null=True), + ), + ] diff --git a/pydis_site/apps/api/models/bot/nomination.py b/pydis_site/apps/api/models/bot/nomination.py index 221d8534..e96177d3 100644 --- a/pydis_site/apps/api/models/bot/nomination.py +++ b/pydis_site/apps/api/models/bot/nomination.py @@ -35,6 +35,11 @@ class Nomination(ModelReprMixin, models.Model): default=False, help_text="Whether a review was made." ) + thread_id = models.BigIntegerField( + help_text="The nomination vote thread's id.", + null=True, + blank=True + ) def __str__(self): """Representation that makes the target and state of the nomination immediately evident.""" -- cgit v1.2.3 From bb160ae0700ef3c739b9c622c3b90a26e576ac96 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Sun, 6 Nov 2022 02:29:39 +0100 Subject: add thread_id to serializer's fields --- pydis_site/apps/api/serializers.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/api/serializers.py b/pydis_site/apps/api/serializers.py index 9228c1f4..4303e7d0 100644 --- a/pydis_site/apps/api/serializers.py +++ b/pydis_site/apps/api/serializers.py @@ -432,7 +432,15 @@ class NominationSerializer(ModelSerializer): model = Nomination fields = ( - 'id', 'active', 'user', 'inserted_at', 'end_reason', 'ended_at', 'reviewed', 'entries' + 'id', + 'active', + 'user', + 'inserted_at', + 'end_reason', + 'ended_at', + 'reviewed', + 'entries', + 'thread_id' ) -- cgit v1.2.3 From bf348379b8c825a8c2025894caa43d9611442fb7 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Mon, 7 Nov 2022 18:15:49 +0100 Subject: delete buggy migration --- .../api/migrations/0085_add_thread_id_to_nomination.py | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 pydis_site/apps/api/migrations/0085_add_thread_id_to_nomination.py (limited to 'pydis_site') diff --git a/pydis_site/apps/api/migrations/0085_add_thread_id_to_nomination.py b/pydis_site/apps/api/migrations/0085_add_thread_id_to_nomination.py deleted file mode 100644 index cb216a62..00000000 --- a/pydis_site/apps/api/migrations/0085_add_thread_id_to_nomination.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 4.1.2 on 2022-11-05 23:53 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0084_infraction_last_applied'), - ] - - operations = [ - migrations.AddField( - model_name='nomination', - name='thread_id', - field=models.BigIntegerField(blank=True, help_text="The nomination vote thread's id", null=True), - ), - ] -- cgit v1.2.3 From 5c2b8c2ccb234dad6f31e83144d2e7879889e8aa Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Mon, 7 Nov 2022 18:18:03 +0100 Subject: fix grammar in thread_id's help text --- .../api/migrations/0085_add_thread_id_to_nomination.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 pydis_site/apps/api/migrations/0085_add_thread_id_to_nomination.py (limited to 'pydis_site') diff --git a/pydis_site/apps/api/migrations/0085_add_thread_id_to_nomination.py b/pydis_site/apps/api/migrations/0085_add_thread_id_to_nomination.py new file mode 100644 index 00000000..ee9707f0 --- /dev/null +++ b/pydis_site/apps/api/migrations/0085_add_thread_id_to_nomination.py @@ -0,0 +1,18 @@ +# Generated by Django 4.1.2 on 2022-11-07 17:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0084_infraction_last_applied'), + ] + + operations = [ + migrations.AddField( + model_name='nomination', + name='thread_id', + field=models.BigIntegerField(blank=True, help_text="The nomination vote's thread id.", null=True), + ), + ] -- cgit v1.2.3 From 8bc05d1c7def6b5558f554b0d5305bd346783583 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Mon, 7 Nov 2022 18:20:10 +0100 Subject: push forgotten model update --- pydis_site/apps/api/models/bot/nomination.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/api/models/bot/nomination.py b/pydis_site/apps/api/models/bot/nomination.py index e96177d3..9146c1d0 100644 --- a/pydis_site/apps/api/models/bot/nomination.py +++ b/pydis_site/apps/api/models/bot/nomination.py @@ -36,7 +36,7 @@ class Nomination(ModelReprMixin, models.Model): help_text="Whether a review was made." ) thread_id = models.BigIntegerField( - help_text="The nomination vote thread's id.", + help_text="The nomination vote's thread id.", null=True, blank=True ) -- cgit v1.2.3 From 1f22c1410afa7965b1f1f2e4dffd37f540bc0d5a Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Mon, 7 Nov 2022 18:26:51 +0100 Subject: add validation criteria for thread_id modification --- pydis_site/apps/api/viewsets/bot/nomination.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'pydis_site') diff --git a/pydis_site/apps/api/viewsets/bot/nomination.py b/pydis_site/apps/api/viewsets/bot/nomination.py index 6af42bcb..ecbf217e 100644 --- a/pydis_site/apps/api/viewsets/bot/nomination.py +++ b/pydis_site/apps/api/viewsets/bot/nomination.py @@ -273,6 +273,11 @@ class NominationViewSet(CreateModelMixin, RetrieveModelMixin, ListModelMixin, Ge {'reviewed': ['This field cannot be set while you are ending a nomination.']} ) + if 'thread_id' in request.data: + raise ValidationError( + {'thread_id': ['This field cannot be set when ending a nomination.']} + ) + instance.ended_at = timezone.now() elif 'active' in data: @@ -289,6 +294,13 @@ class NominationViewSet(CreateModelMixin, RetrieveModelMixin, ListModelMixin, Ge {'reviewed': ['This field cannot be set if the nomination is inactive.']} ) + elif 'thread_id' in request.data: + # 5. We are altering the thread_id of the nomination. + if not instance.active: + raise ValidationError( + {'thread_id': ['This field cannot be set if the nomination is inactive.']} + ) + if 'reason' in request.data: if 'actor' not in request.data: raise ValidationError( -- cgit v1.2.3 From af1a11d05f716c7891ae3b661c470d2bbac44ee6 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Mon, 7 Nov 2022 18:28:24 +0100 Subject: add tests for new thread_id validation flow --- pydis_site/apps/api/tests/test_nominations.py | 32 +++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'pydis_site') diff --git a/pydis_site/apps/api/tests/test_nominations.py b/pydis_site/apps/api/tests/test_nominations.py index 62b2314c..b3742cdd 100644 --- a/pydis_site/apps/api/tests/test_nominations.py +++ b/pydis_site/apps/api/tests/test_nominations.py @@ -524,3 +524,35 @@ class NominationTests(AuthenticatedAPITestCase): self.assertEqual(response.json(), { 'actor': ["The actor doesn't exist or has not nominated the user."] }) + + def test_patch_nomination_set_thread_id_of_active_nomination(self): + url = reverse('api:bot:nomination-detail', args=(self.active_nomination.id,)) + data = {'thread_id': 9876543210} + response = self.client.patch(url, data=data) + self.assertEqual(response.status_code, 200) + + def test_patch_nomination_set_thread_id_and_reviewed_of_active_nomination(self): + url = reverse('api:bot:nomination-detail', args=(self.active_nomination.id,)) + data = {'thread_id': 9876543210, "reviewed": True} + response = self.client.patch(url, data=data) + self.assertEqual(response.status_code, 200) + + def test_modifying_thread_id_when_ending_nomination(self): + url = reverse('api:bot:nomination-detail', args=(self.active_nomination.id,)) + data = {'thread_id': 9876543210, 'active': False, 'end_reason': "What?"} + + response = self.client.patch(url, data=data) + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json(), { + 'thread_id': ['This field cannot be set when ending a nomination.'] + }) + + def test_patch_thread_id_for_inactive_nomination(self): + url = reverse('api:bot:nomination-detail', args=(self.inactive_nomination.id,)) + data = {'thread_id': 9876543210} + + response = self.client.patch(url, data=data) + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json(), { + 'thread_id': ['This field cannot be set if the nomination is inactive.'] + }) -- cgit v1.2.3 From d33310037146a7e8988a27238ff475b659ef625c Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Mon, 7 Nov 2022 21:24:46 +0100 Subject: refactor nomination validation flow --- pydis_site/apps/api/viewsets/bot/nomination.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/api/viewsets/bot/nomination.py b/pydis_site/apps/api/viewsets/bot/nomination.py index ecbf217e..49c40e7c 100644 --- a/pydis_site/apps/api/viewsets/bot/nomination.py +++ b/pydis_site/apps/api/viewsets/bot/nomination.py @@ -287,19 +287,15 @@ class NominationViewSet(CreateModelMixin, RetrieveModelMixin, ListModelMixin, Ge ) # This is actually covered, but for some reason coverage don't think so. - elif 'reviewed' in request.data: # pragma: no cover - # 4. We are altering the reviewed state of the nomination. - if not instance.active: - raise ValidationError( - {'reviewed': ['This field cannot be set if the nomination is inactive.']} - ) + elif not instance.active and 'reviewed' in request.data: + raise ValidationError( + {'reviewed': ['This field cannot be set if the nomination is inactive.']} + ) - elif 'thread_id' in request.data: - # 5. We are altering the thread_id of the nomination. - if not instance.active: - raise ValidationError( - {'thread_id': ['This field cannot be set if the nomination is inactive.']} - ) + elif not instance.active and 'thread_id' in request.data: + raise ValidationError( + {'thread_id': ['This field cannot be set if the nomination is inactive.']} + ) if 'reason' in request.data: if 'actor' not in request.data: -- cgit v1.2.3 From ae180bb6a3028f9d0ec05ebfa940265004eb76cd Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Mon, 7 Nov 2022 22:07:32 +0100 Subject: remove useless coverage related commented --- pydis_site/apps/api/viewsets/bot/nomination.py | 1 - 1 file changed, 1 deletion(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/api/viewsets/bot/nomination.py b/pydis_site/apps/api/viewsets/bot/nomination.py index 49c40e7c..78687e0e 100644 --- a/pydis_site/apps/api/viewsets/bot/nomination.py +++ b/pydis_site/apps/api/viewsets/bot/nomination.py @@ -286,7 +286,6 @@ class NominationViewSet(CreateModelMixin, RetrieveModelMixin, ListModelMixin, Ge {'active': ['This field can only be used to end a nomination']} ) - # This is actually covered, but for some reason coverage don't think so. elif not instance.active and 'reviewed' in request.data: raise ValidationError( {'reviewed': ['This field cannot be set if the nomination is inactive.']} -- cgit v1.2.3 From 285f48f4e0fd903f296921640f710fe6459a7154 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Sat, 12 Nov 2022 15:53:25 +0100 Subject: remove useless blank constraint This updates the migration as well --- .../api/migrations/0085_add_thread_id_to_nomination.py | 18 ------------------ .../migrations/0085_add_thread_id_to_nominations.py | 18 ++++++++++++++++++ pydis_site/apps/api/models/bot/nomination.py | 1 - 3 files changed, 18 insertions(+), 19 deletions(-) delete mode 100644 pydis_site/apps/api/migrations/0085_add_thread_id_to_nomination.py create mode 100644 pydis_site/apps/api/migrations/0085_add_thread_id_to_nominations.py (limited to 'pydis_site') diff --git a/pydis_site/apps/api/migrations/0085_add_thread_id_to_nomination.py b/pydis_site/apps/api/migrations/0085_add_thread_id_to_nomination.py deleted file mode 100644 index ee9707f0..00000000 --- a/pydis_site/apps/api/migrations/0085_add_thread_id_to_nomination.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 4.1.2 on 2022-11-07 17:16 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0084_infraction_last_applied'), - ] - - operations = [ - migrations.AddField( - model_name='nomination', - name='thread_id', - field=models.BigIntegerField(blank=True, help_text="The nomination vote's thread id.", null=True), - ), - ] diff --git a/pydis_site/apps/api/migrations/0085_add_thread_id_to_nominations.py b/pydis_site/apps/api/migrations/0085_add_thread_id_to_nominations.py new file mode 100644 index 00000000..56a24cc3 --- /dev/null +++ b/pydis_site/apps/api/migrations/0085_add_thread_id_to_nominations.py @@ -0,0 +1,18 @@ +# Generated by Django 4.1.2 on 2022-11-12 14:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0084_infraction_last_applied'), + ] + + operations = [ + migrations.AddField( + model_name='nomination', + name='thread_id', + field=models.BigIntegerField(help_text="The nomination vote's thread id.", null=True), + ), + ] diff --git a/pydis_site/apps/api/models/bot/nomination.py b/pydis_site/apps/api/models/bot/nomination.py index 9146c1d0..58e70a83 100644 --- a/pydis_site/apps/api/models/bot/nomination.py +++ b/pydis_site/apps/api/models/bot/nomination.py @@ -38,7 +38,6 @@ class Nomination(ModelReprMixin, models.Model): thread_id = models.BigIntegerField( help_text="The nomination vote's thread id.", null=True, - blank=True ) def __str__(self): -- cgit v1.2.3 From 98ea7a8650bc7195700993bf3e94cf0a48a43cd5 Mon Sep 17 00:00:00 2001 From: Xithrius Date: Sat, 12 Nov 2022 19:55:18 -0800 Subject: Resolved a bunch of requests --- .../resources/guides/python-guides/app_commands.md | 58 +++++++++++----------- 1 file changed, 29 insertions(+), 29 deletions(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/python-guides/app_commands.md b/pydis_site/apps/content/resources/guides/python-guides/app_commands.md index 821ac577..d81a037c 100644 --- a/pydis_site/apps/content/resources/guides/python-guides/app_commands.md +++ b/pydis_site/apps/content/resources/guides/python-guides/app_commands.md @@ -2,25 +2,25 @@ --- -Upon resumation of the most popular discord API wrapper library for python, `discord.py`, while catching on to the latest features of the discord API, there have been numerous changes with addition of features to the library. Some additions to the library are -> Buttons support, Select Menus Support, Forms (AKA Modals), Slash Commands (AKA Application Commands) and a bunch of more handy features! All the changes can be found [here](https://discordpy.readthedocs.io/en/latest/migrating.html). Original discord.py gist regarding resumation can be found [here](https://gist.github.com/Rapptz/c4324f17a80c94776832430007ad40e6). +Upon resumption of the most popular discord API wrapper library for python, `discord.py`, while catching on to the latest features of the discord API, there have been numerous changes with addition of features to the library. Some additions to the library are -> Buttons support, Select Menus Support, Forms (AKA Modals), Slash Commands (AKA Application Commands) and a bunch of more handy features! All the changes can be found [here](https://discordpy.readthedocs.io/en/latest/migrating.html). Original discord.py gist regarding resumation can be found [here](https://gist.github.com/Rapptz/c4324f17a80c94776832430007ad40e6). # Why this gist? --- -This Gist is being created as an update to slash commands (app commands) with explanation and examples. This Gist mainly focuses on **SLASH COMMANDS** for discord.py 2.0 (and above)! +This Gist is being created as an update to Slash Commands (app commands) with explanation and examples. This Gist mainly focuses on **SLASH COMMANDS** for discord.py 2.0 (and above)! # What Are Slash Commands? --- -Slash Commands are the exciting way to build and interact with bots on Discord. With Slash Commands, all you have to do is type / and you're ready to use your favourite bot. You can easily see all the commands a bot has, and validation and error handling help you get the command right the first time. +Slash Commands are the exciting new way to build and interact with bots on Discord. With Slash Commands, all you have to do is type / and you're ready to use your favourite bot. You can easily see all the commands a bot has, and validation and error handling help you get the command right the first time. -# Install the latest version for discord.py +# Install the latest version of discord.py --- -To use slash commands with discord.py. The latest up-to-date version has to be installed. Make sure that the version is 2.0 or above! -And make sure to uninstall any third party libraries that support slash commands for discord.py (if any) as a few of them [monkey patch](https://en.wikipedia.org/wiki/Monkey_patch#:~:text=A%20monkey%20patch%20is%20a,running%20instance%20of%20the%20program) discord.py! +To use Slash Commands with discord.py. The latest up-to-date version has to be installed. Make sure that the version is 2.0 or above! +And make sure to uninstall any third party libraries that support Slash Commands for discord.py (if any) as a few of them [monkey patch](https://en.wikipedia.org/wiki/Monkey_patch#:~:text=A%20monkey%20patch%20is%20a,running%20instance%20of%20the%20program) discord.py! The latest and up-to-date usable discord.py version can be installed using `pip install -U git+https://github.com/Rapptz/discord.py`. @@ -32,14 +32,14 @@ If you get an error such as: `'git' is not recognized...`. [Install git](https:/ --- ### Note that Slash Commands in discord.py are also referred to as **Application Commmands** and **App Commands** and every *interaction* is a *webhook*. -Slash commands in discord.py are held by a container, [CommandTree](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=commandtree#discord.app_commands.CommandTree). A command tree is required to create slash commands in discord.py. This command tree provides a `command` method which decorates an asynchronous function indicating to discord.py that the decorated function is intended to be a slash command. This asynchronous function expects a default argument which acts as the interaction which took place that invoked the slash command. This default argument is an instance of the **Interaction** class from discord.py. Further up, the command logic takes over the behaviour of the slash command. +Slash commands in discord.py are held by a container, [CommandTree](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=commandtree#discord.app_commands.CommandTree). A command tree is required to create Slash Commands in discord.py. This command tree provides a `command` method which decorates an asynchronous function indicating to discord.py that the decorated function is intended to be a slash command. This asynchronous function expects a default argument which acts as the interaction which took place that invoked the slash command. This default argument is an instance of the **Interaction** class from discord.py. Further up, the command logic takes over the behaviour of the slash command. # Fundamentals for this gist! --- -The fundamental for this gist will remain to be the `setup_hook`. The `setup_hook` is a special asynchronous method of the Client and Bot class which can be overwritten to perform numerous tasks. This method is safe to use as it is always triggered before any events are dispatched, i.e. this method is triggered before the *IDENTIFY* payload is sent to the discord gateway. +The fundamental for this gist will remain to be the `setup_hook`. The `setup_hook` is a special asynchronous method of the Client and Bot classes which can be overwritten to perform numerous tasks. This method is safe to use as it is always triggered before any events are dispatched, i.e. this method is triggered before the *IDENTIFY* payload is sent to the discord gateway. Note that methods of the Bot class such as `change_presence` will not work in setup_hook as the current application does not have an active connection to the gateway at this point. __**FOLLOWING IS THE EXAMPLE OF HOW A `SETUP_HOOK` FUNCTION CAN BE DEFINED**__ @@ -54,13 +54,13 @@ class SlashClient(discord.Client): super().__init__(intents=discord.Intents.default()) async def setup_hook(self) -> None: - #perform tasks + ... '''Another way of creating a "setup_hook" is as follows''' client = discord.Client(intents=discord.Intents.default()) async def my_setup_hook() -> None: - #perform tasks + ... client.setup_hook = my_setup_hook ``` @@ -99,11 +99,11 @@ __**EXPLANATION**__ - `import discord` imports the **discord.py** package. - `class SlashClient(discord.Client)` is a class subclassing **Client**. Though there is no particular reason except readability to subclass the **Client** class, using the `Client.setup_hook = my_func` is equally valid. - Next up `super().__init__(...)` runs the `__init__` function of the **Client** class, this is equivalent to `discord.Client(...)`. Then, `self.tree = discord.app_commands.CommandTree(self)` creates a CommandTree which acts as the container for slash commaands. -- Then in the `setup_hook`, `self.tree.copy_global_to(...)` adds the slash command to the guild of which the ID is provided as a `discord.Object` object. Further up, `self.tree.sync()` updates the API with any changes to the slash commands. +- Then in the `setup_hook`, `self.tree.copy_global_to(...)` adds the slash command to the guild of which the ID is provided as a `discord.Object` object. Further up, `self.tree.sync()` updates the API with any changes to the Slash Commands. - Finishing up with the **Client** subclass, we create an instance of the subclassed Client class which here has been named as `SlashClient` with `client = SlashClient()`. - Then using the `command` method of the `CommandTree` we decorate a function with it as `client.tree` is an instance of `CommandTree` for the current application. The command function takes a default argument as said, which acts as the interaction that took place. Catching up is `await interaction.response.send_message("pong")` which sends back a message to the slash command invoker. - And the classic old `client.run("token")` is used to connect the client to the discord gateway. -- Note that the `send_message` is a method of the `InteractionResponse` class and `interaction.response` in this case is an instance of the `InteractionResponse` object. The `send_message` method will not function if the response is not sent within 3 seconds of command invocation. I will discuss about how to handle this issue later following the gist. +- Note that the `send_message` is a method of the `InteractionResponse` class and `interaction.response` in this case is an instance of the `InteractionResponse` object. The `send_message` method will not function if the response is not sent within 3 seconds of command invocation. We will discuss how to handle this issue later following the gist. ## Slash Command Application with a Bot @@ -127,7 +127,7 @@ async def _ping(interaction: discord.Interaction) -> None: bot.run("token") ``` -The above example shows a basic slash commands within discord.py using the Bot class. +The above example shows a basic Slash Commands within discord.py using the Bot class. __**EXPLANATION**__ @@ -142,7 +142,7 @@ Most of the explanation is the same as the prior example which featured `SlashCl A cog is a collection of commands, listeners, and optional state to help group commands together. More information on them can be found on the [Cogs](https://discordpy.readthedocs.io/en/latest/ext/commands/cogs.html#ext-commands-cogs) page. -## An Example to using cogs with discord.py for slash commands! +## An Example to using cogs with discord.py for Slash Commands! ```python import discord @@ -176,12 +176,12 @@ __**EXPLANATION**__ - Firstly, `import discord` imports the **discord.py** package. `from discord import app_commands` imports the `app_commands` directory from the **discord.py** root directory. `from discord.ext import commands` imports the commands extension. - Further up, `class MySlashCog(commands.Cog)` is a class subclassing the `Cog` class. You can read more about this [here](https://discordpy.readthedocs.io/en/latest/ext/commands/cogs.html#ext-commands-cogs). - `def __init__(self, bot: commands.Bot): self.bot = bot` is the constructor method of the class that is always run when the class is instantiated and that is why we pass in a **Bot** object whenever we create an instance of the cog class. -- Following up is the `@app_commands.command(name="ping", description="...")` decorator. This decorator basically functions the same as a `bot.tree.command` but since the cog currently does not have a **bot**, the `app_commands.command` decorator is used instead. The next two lines follow the same structure for slash commands with **self** added as the first parameter to the function as it is a method of a class. +- Following up is the `@app_commands.command(name="ping", description="...")` decorator. This decorator basically functions the same as a `bot.tree.command` but since the cog currently does not have a **bot**, the `app_commands.command` decorator is used instead. The next two lines follow the same structure for Slash Commands with **self** added as the first parameter to the function as it is a method of a class. - The next up lines are mostly the same. - Talking about the first line inside the `setup_hook` is the `add_cog` method of the **Bot** class. And since **self** acts as the **instance** of the current class, we use **self** to use the `add_cog` method of the **Bot** class as we are inside a subclassed class of the **Bot** class. Then we pass in **self** to the `add_cog` method as the `__init__` function of the **MySlashCog** cog accepts a `Bot` object. - After that we instantiate the `MySlashBot` class and run the bot using the **run** method which executes our setup_hook function and our commands get loaded and synced. The bot is now ready to use! -# An Example to using groups with discord.py for slash commands! +# An Example to using groups with discord.py for Slash Commands! --- @@ -265,11 +265,11 @@ __**EXPLANATION**__ - The only difference here too is that the `MySlashGroup` class directly subclasses the **Group** class from discord.app_commands which automatically registers all the methods within the group class to be commands of that specific group. So now, the commands such as `ping` can be run using **/uwu ping** and `command` using **/uwu command**. -# Some common methods and features used for slash commands. +# Some common methods and features used for Slash Commands. --- -### A common function used for slash commands is the `describe` function. This is used to add descriptions to the arguments of a slash command. The command function can decorated with this function. It goes by the following syntax as shown below. +### A common function used for Slash Commands is the `describe` function. This is used to add descriptions to the arguments of a slash command. The command function can decorated with this function. It goes by the following syntax as shown below. ```python from discord.ext import commands @@ -286,7 +286,7 @@ async def _echo(interaction: discord.Interaction, text: str, channel: discord.Te await channel.send(text) ``` -### Another common issue that most people come across is the time duraction of sending a message with `send_message`. This issue can be tackled by deferring the interaction response using the `defer` method of the `InteractionResponse` class. An example for fixing this issue is shown below. +### Another common issue that most people come across is the time duration of sending a message with `send_message`. This issue can be tackled by deferring the interaction response using the `defer` method of the `InteractionResponse` class. An example for fixing this issue is shown below. ```python import discord @@ -327,7 +327,7 @@ async def _ping(interaction: discord.Interaction): ``` -If the check fails, it will raise a `MissingPermissions` error which can be handled within an app commands error handler! I will discuss about making an error handler later in the gist. All the permissions can be found [here](https://discordpy.readthedocs.io/en/latest/api.html?highlight=discord%20permissions#discord.Permissions). +If the check fails, it will raise a `MissingPermissions` error which can be handled within an app commands error handler! We will discuss making an error handler later in the gist. All the permissions can be found [here](https://discordpy.readthedocs.io/en/latest/api.html?highlight=discord%20permissions#discord.Permissions). Other methods that you can decorate the commands with are - - `bot_has_permissions` | This checks if the bot has the required permissions for executing the slash command. This raises a [BotMissingPermissions](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.BotMissingPermissions) exception. @@ -335,7 +335,7 @@ Other methods that you can decorate the commands with are - - To pass in several role names or role IDs, `has_any_role` can be used to decorate a command. This raises two exceptions -> [MissingAnyRole](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.MissingAnyRole) and [NoPrivateMessage](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.NoPrivateMessage) -# Adding cooldowns to slash commands! +# Adding cooldowns to Slash Commands! --- @@ -369,17 +369,17 @@ bot.run("token") ``` __**EXPLANATION**__ -- The first argument the `cooldown` method takes is the amount of times the command can be run in a specific period of time. +- The first argument the `cooldown` method takes is the number of times this command can be invoked in a particular unit of time (Which will be defined in the following argument). - The second argument it takes is the period of time in which the command can be run the specified number of times. -- The `CommandOnCooldown` exception can be handled using an error handler. I will discuss about making an error handler for slash commands later in the gist. +- The `CommandOnCooldown` exception can be handled using an error handler. We will discuss making an error handler for Slash Commands later in the gist. -# Handling errors for slash commands! +# Handling errors for Slash Commands! --- -The Slash Commands exceptions can be handled by overwriting the `on_error` method of the `CommandTree`. The error handler takes two arguments. The first argument is the `Interaction` that took place when the error occurred and the second argument is the error that occurred when the slash commands was invoked. The error is an instance of [discord.app_commands.AppCommandError](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=appcommanderror#discord.app_commands.AppCommandError) which is a subclass of [DiscordException](https://discordpy.readthedocs.io/en/latest/api.html?highlight=discordexception#discord.DiscordException). -An example to creating an error handler for slash commands is as follows. +The Slash Commands exceptions can be handled by overwriting the `on_error` method of the `CommandTree`. The error handler takes two arguments. The first argument is the `Interaction` that took place when the error occurred and the second argument is the error that occurred when the Slash Commands was invoked. The error is an instance of [discord.app_commands.AppCommandError](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=appcommanderror#discord.app_commands.AppCommandError) which is a subclass of [DiscordException](https://discordpy.readthedocs.io/en/latest/api.html?highlight=discordexception#discord.DiscordException). +An example to creating an error handler for Slash Commands is as follows. ```python from discord.ext import commands @@ -411,8 +411,8 @@ bot.run("token") __**EXPLANATION**__ -First we create a simple function named as `on_tree_error` here. To which the first two required arguments are passed, `Interaction` which is named as `interaction` here and `AppCommandError` which is named as `error` here. Then using simple functions and keywords, we make an error handler like above. Here I have used the `isinstance` function which takes in an object and a base class as the second argument, this function returns a bool value. The `raise error` is just for displaying unhandled errors, i.e. the ones which have not been handled manually. If this is **removed**, you will not be able to see any exceptions raised by slash commands and makes debugging the code harder. -After creating the error handler function, we set the function as the error handler for the slash commands. Here, `bot.tree.on_error = on_tree_error` overwrites the default `on_error` method of the **CommandTree** class with our custom error handler which has been named as `on_tree_error` here. +First we create a simple asynchronous function named `on_tree_error` here. To which the first two required arguments are passed, `Interaction` which is named as `interaction` here and `AppCommandError` which is named as `error` here. Then using simple functions and keywords, we make an error handler like above. Here we have used the `isinstance` function which takes in an object and a base class as the second argument, this function returns a bool value. The `raise error` is just for displaying unhandled errors, i.e. the ones which have not been handled manually. If this is **removed**, you will not be able to see any exceptions raised by Slash Commands and makes debugging the code harder. +After creating the error handler function, we set the function as the error handler for the Slash Commands. Here, `bot.tree.on_error = on_tree_error` overwrites the default `on_error` method of the **CommandTree** class with our custom error handler which has been named as `on_tree_error` here. ### Creating an error handler for a specific error! @@ -445,4 +445,4 @@ bot.run("token") __**EXPLANATION**__ -Here the command name is simply used to access the `error` method to decorate a function which acts as the `on_error` but for a specific command. Please do not call the `error` method. \ No newline at end of file +Here the command name is simply used to access the `error` method to decorate a function which acts as the `on_error` but for a specific command. Please do not call the `error` method. -- cgit v1.2.3 From f5641894f5645bc41c01708bfb8ffe16d372e449 Mon Sep 17 00:00:00 2001 From: Xithrius Date: Sat, 12 Nov 2022 19:57:08 -0800 Subject: Appeased the formatter --- .../resources/guides/python-guides/app_commands.md | 58 +++++++++++----------- 1 file changed, 29 insertions(+), 29 deletions(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/python-guides/app_commands.md b/pydis_site/apps/content/resources/guides/python-guides/app_commands.md index d81a037c..c319ebb1 100644 --- a/pydis_site/apps/content/resources/guides/python-guides/app_commands.md +++ b/pydis_site/apps/content/resources/guides/python-guides/app_commands.md @@ -52,12 +52,12 @@ import discord class SlashClient(discord.Client): def __init__(self) -> None: super().__init__(intents=discord.Intents.default()) - + async def setup_hook(self) -> None: ... '''Another way of creating a "setup_hook" is as follows''' - + client = discord.Client(intents=discord.Intents.default()) async def my_setup_hook() -> None: ... @@ -79,7 +79,7 @@ class SlashClient(discord.Client): def __init__(self) -> None: super().__init__(intents=discord.Intents.default()) self.tree = discord.app_commands.CommandTree(self) - + async def setup_hook(self) -> None: self.tree.copy_global_to(guild=discord.Object(id=12345678900987654)) await self.tree.sync() @@ -96,11 +96,11 @@ client.run("token") __**EXPLANATION**__ -- `import discord` imports the **discord.py** package. -- `class SlashClient(discord.Client)` is a class subclassing **Client**. Though there is no particular reason except readability to subclass the **Client** class, using the `Client.setup_hook = my_func` is equally valid. -- Next up `super().__init__(...)` runs the `__init__` function of the **Client** class, this is equivalent to `discord.Client(...)`. Then, `self.tree = discord.app_commands.CommandTree(self)` creates a CommandTree which acts as the container for slash commaands. -- Then in the `setup_hook`, `self.tree.copy_global_to(...)` adds the slash command to the guild of which the ID is provided as a `discord.Object` object. Further up, `self.tree.sync()` updates the API with any changes to the Slash Commands. -- Finishing up with the **Client** subclass, we create an instance of the subclassed Client class which here has been named as `SlashClient` with `client = SlashClient()`. +- `import discord` imports the **discord.py** package. +- `class SlashClient(discord.Client)` is a class subclassing **Client**. Though there is no particular reason except readability to subclass the **Client** class, using the `Client.setup_hook = my_func` is equally valid. +- Next up `super().__init__(...)` runs the `__init__` function of the **Client** class, this is equivalent to `discord.Client(...)`. Then, `self.tree = discord.app_commands.CommandTree(self)` creates a CommandTree which acts as the container for slash commaands. +- Then in the `setup_hook`, `self.tree.copy_global_to(...)` adds the slash command to the guild of which the ID is provided as a `discord.Object` object. Further up, `self.tree.sync()` updates the API with any changes to the Slash Commands. +- Finishing up with the **Client** subclass, we create an instance of the subclassed Client class which here has been named as `SlashClient` with `client = SlashClient()`. - Then using the `command` method of the `CommandTree` we decorate a function with it as `client.tree` is an instance of `CommandTree` for the current application. The command function takes a default argument as said, which acts as the interaction that took place. Catching up is `await interaction.response.send_message("pong")` which sends back a message to the slash command invoker. - And the classic old `client.run("token")` is used to connect the client to the discord gateway. - Note that the `send_message` is a method of the `InteractionResponse` class and `interaction.response` in this case is an instance of the `InteractionResponse` object. The `send_message` method will not function if the response is not sent within 3 seconds of command invocation. We will discuss how to handle this issue later following the gist. @@ -113,7 +113,7 @@ import discord class SlashBot(commands.Bot): def __init__(self) -> None: super().__init__(command_prefix=".", intents=discord.Intents.default()) - + async def setup_hook(self) -> None: self.tree.copy_global_to(guild=discord.Object(id=12345678900987654)) await self.tree.sync() @@ -152,20 +152,20 @@ from discord import app_commands class MySlashCog(commands.Cog): def __init__(self, bot: commands.Bot) -> None: self.bot = bot - + @app_commands.command(name="ping", description="...") async def _ping(self, interaction: discord.Interaction): await interaction.response.send_message("pong!") - + class MySlashBot(commands.Bot): def __init__(self) -> None: super().__init__(command_prefix="!", intents=discord.Intents.default()) - + async def setup_hook(self) -> None: await self.add_cog(MySlashCog(self)) await self.tree.copy_global_to(discord.Object(id=123456789098765432)) await self.tree.sync() - + bot = MySlashBot() bot.run("token") @@ -203,20 +203,20 @@ class MySlashGroupCog(commands.Cog): @app_commands.command(name="ping", description="...") async def _ping(self, interaction: discord.) -> None: await interaction.response.send_message("pong!") - + @group.command(name="command", description="...") async def _cmd(self, interaction: discord.Interaction) -> None: await interaction.response.send_message("uwu") - + class MySlashBot(commands.Bot): def __init__(self) -> None: super().__init__(command_prefix="!", intents=discord.Intents.default()) - + async def setup_hook(self) -> None: await self.add_cog(MySlashGroupCog(self)) await self.tree.copy_global_to(discord.Object(id=123456789098765432)) await self.tree.sync() - + bot = MySlashBot() bot.run("token") @@ -242,20 +242,20 @@ class MySlashGroup(app_commands.Group, name="uwu"): @app_commands.command(name="ping", description="...") async def _ping(self, interaction: discord.) -> None: await interaction.response.send_message("pong!") - + @app_commands.command(name="command", description="...") async def _cmd(self, interaction: discord.Interaction) -> None: await interaction.response.send_message("uwu") - + class MySlashBot(commands.Bot): def __init__(self) -> None: super().__init__(command_prefix="!", intents=discord.Intents.default()) - + async def setup_hook(self) -> None: await self.add_cog(MySlashGroup(self)) await self.tree.copy_global_to(discord.Object(id=123456789098765432)) await self.tree.sync() - + bot = MySlashBot() bot.run("token") @@ -267,7 +267,7 @@ __**EXPLANATION**__ # Some common methods and features used for Slash Commands. ---- +--- ### A common function used for Slash Commands is the `describe` function. This is used to add descriptions to the arguments of a slash command. The command function can decorated with this function. It goes by the following syntax as shown below. @@ -349,13 +349,13 @@ import discord class Bot(commands.Bot): def __init__(self): super().__init__(command_prefix="uwu", intents=discord.Intents.all()) - - + + async def setup_hook(self): - self.tree.copy_global_to(guild=discord.Object(id=12345678909876543)) + self.tree.copy_global_to(guild=discord.Object(id=12345678909876543)) await self.tree.sync() - - + + bot = Bot() @bot.tree.command(name="ping") @@ -370,7 +370,7 @@ bot.run("token") __**EXPLANATION**__ - The first argument the `cooldown` method takes is the number of times this command can be invoked in a particular unit of time (Which will be defined in the following argument). -- The second argument it takes is the period of time in which the command can be run the specified number of times. +- The second argument it takes is the period of time in which the command can be run the specified number of times. - The `CommandOnCooldown` exception can be handled using an error handler. We will discuss making an error handler for Slash Commands later in the gist. @@ -412,7 +412,7 @@ bot.run("token") __**EXPLANATION**__ First we create a simple asynchronous function named `on_tree_error` here. To which the first two required arguments are passed, `Interaction` which is named as `interaction` here and `AppCommandError` which is named as `error` here. Then using simple functions and keywords, we make an error handler like above. Here we have used the `isinstance` function which takes in an object and a base class as the second argument, this function returns a bool value. The `raise error` is just for displaying unhandled errors, i.e. the ones which have not been handled manually. If this is **removed**, you will not be able to see any exceptions raised by Slash Commands and makes debugging the code harder. -After creating the error handler function, we set the function as the error handler for the Slash Commands. Here, `bot.tree.on_error = on_tree_error` overwrites the default `on_error` method of the **CommandTree** class with our custom error handler which has been named as `on_tree_error` here. +After creating the error handler function, we set the function as the error handler for the Slash Commands. Here, `bot.tree.on_error = on_tree_error` overwrites the default `on_error` method of the **CommandTree** class with our custom error handler which has been named as `on_tree_error` here. ### Creating an error handler for a specific error! -- cgit v1.2.3 From 5a25a5233241bd6ca5b574d6a8bb9bd85790ca35 Mon Sep 17 00:00:00 2001 From: Xithrius Date: Sat, 19 Nov 2022 15:55:57 -0800 Subject: Appeased review requests. --- .../resources/guides/python-guides/app_commands.md | 49 +++++++++++----------- 1 file changed, 25 insertions(+), 24 deletions(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/python-guides/app_commands.md b/pydis_site/apps/content/resources/guides/python-guides/app_commands.md index c319ebb1..ed6b24cd 100644 --- a/pydis_site/apps/content/resources/guides/python-guides/app_commands.md +++ b/pydis_site/apps/content/resources/guides/python-guides/app_commands.md @@ -2,19 +2,19 @@ --- -Upon resumption of the most popular discord API wrapper library for python, `discord.py`, while catching on to the latest features of the discord API, there have been numerous changes with addition of features to the library. Some additions to the library are -> Buttons support, Select Menus Support, Forms (AKA Modals), Slash Commands (AKA Application Commands) and a bunch of more handy features! All the changes can be found [here](https://discordpy.readthedocs.io/en/latest/migrating.html). Original discord.py gist regarding resumation can be found [here](https://gist.github.com/Rapptz/c4324f17a80c94776832430007ad40e6). +Upon resumption of the most popular discord API wrapper library for python, `discord.py`, while catching on to the latest features of the discord API, there have been numerous changes with addition of features to the library. Some additions to the library are -> Buttons support, Select Menus Support, Forms (AKA Modals), Slash Commands (AKA Application Commands) and a bunch of more handy features! All the changes can be found [here](https://discordpy.readthedocs.io/en/latest/migrating.html). Original discord.py Gist regarding resumption can be found [here](https://Gist.github.com/Rapptz/c4324f17a80c94776832430007ad40e6). -# Why this gist? +# Why this Gist? --- This Gist is being created as an update to Slash Commands (app commands) with explanation and examples. This Gist mainly focuses on **SLASH COMMANDS** for discord.py 2.0 (and above)! -# What Are Slash Commands? +# What are Slash Commands? --- -Slash Commands are the exciting new way to build and interact with bots on Discord. With Slash Commands, all you have to do is type / and you're ready to use your favourite bot. You can easily see all the commands a bot has, and validation and error handling help you get the command right the first time. +Slash Commands are the exciting new way to build and interact with bots on Discord. As soon as you type "/", you can easily see all the commands a bot has. It also comes with autocomplete, validation and error handling, which will all help you get the command right the first time. # Install the latest version of discord.py @@ -22,27 +22,28 @@ Slash Commands are the exciting new way to build and interact with bots on Disco To use Slash Commands with discord.py. The latest up-to-date version has to be installed. Make sure that the version is 2.0 or above! And make sure to uninstall any third party libraries that support Slash Commands for discord.py (if any) as a few of them [monkey patch](https://en.wikipedia.org/wiki/Monkey_patch#:~:text=A%20monkey%20patch%20is%20a,running%20instance%20of%20the%20program) discord.py! -The latest and up-to-date usable discord.py version can be installed using `pip install -U git+https://github.com/Rapptz/discord.py`. +The latest and up-to-date usable discord.py version can be installed using `pip install -U discord.py`. If you get an error such as: `'git' is not recognized...`, it means that you don't have it installed on your platform. In that case, you will need to do do so by going through the required steps for [installing git](https://git-scm.com/downloads) and make sure to enable the `add to path` option while in the installation wizard. After installing git you can run `pip install -U discord.py` again to install discord.py 2.0. -If you get an error such as: `'git' is not recognized...`. [Install git](https://git-scm.com/downloads) for your platform. Go through the required steps for installing git and make sure to enable the `add to path` option while in the installation wizard. After installing git you can run `pip install -U git+https://github.com/Rapptz/discord.py` again to install discord.py 2.0. **BEFORE MIGRATING TO DISCORD.PY 2.0, PLEASE READ THE CONSEQUENCES OF THE UPDATE [HERE](https://discordpy.readthedocs.io/en/latest/migrating.html)**. -# Basic Structure for Discord.py Slash Commands! +# Basic structure for discord.py Slash Commands! --- ### Note that Slash Commands in discord.py are also referred to as **Application Commmands** and **App Commands** and every *interaction* is a *webhook*. Slash commands in discord.py are held by a container, [CommandTree](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=commandtree#discord.app_commands.CommandTree). A command tree is required to create Slash Commands in discord.py. This command tree provides a `command` method which decorates an asynchronous function indicating to discord.py that the decorated function is intended to be a slash command. This asynchronous function expects a default argument which acts as the interaction which took place that invoked the slash command. This default argument is an instance of the **Interaction** class from discord.py. Further up, the command logic takes over the behaviour of the slash command. -# Fundamentals for this gist! +# Fundamentals for this Gist! --- -The fundamental for this gist will remain to be the `setup_hook`. The `setup_hook` is a special asynchronous method of the Client and Bot classes which can be overwritten to perform numerous tasks. This method is safe to use as it is always triggered before any events are dispatched, i.e. this method is triggered before the *IDENTIFY* payload is sent to the discord gateway. +The fundamental for this Gist will remain to be the `setup_hook`. The `setup_hook` is a special asynchronous method of the Client and Bot classes which can be overwritten to perform numerous tasks. This method is safe to use as it is always triggered before any events are dispatched, i.e. this method is triggered before the *IDENTIFY* payload is sent to the discord gateway. Note that methods of the Bot class such as `change_presence` will not work in setup_hook as the current application does not have an active connection to the gateway at this point. -__**FOLLOWING IS THE EXAMPLE OF HOW A `SETUP_HOOK` FUNCTION CAN BE DEFINED**__ +__**THE FOLLOWING ARE EXAMPLES OF HOW A `SETUP_HOOK` FUNCTION CAN BE DEFINED**__ + +Note that the default intents are defined [here](https://discordpy.readthedocs.io/en/stable/api.html?highlight=discord%20intents%20default#discord.Intents.default) to have all intents enabled except presences, members, and message_content. ```python import discord @@ -67,7 +68,7 @@ client.setup_hook = my_setup_hook # Basic Slash Command application using discord.py. -#### The `CommandTree` class resides within the `app_commands` of discord.py package. +#### The `CommandTree` class resides within the `app_commands` of the discord.py package. --- ## Slash Command Application with a Client @@ -98,14 +99,14 @@ __**EXPLANATION**__ - `import discord` imports the **discord.py** package. - `class SlashClient(discord.Client)` is a class subclassing **Client**. Though there is no particular reason except readability to subclass the **Client** class, using the `Client.setup_hook = my_func` is equally valid. -- Next up `super().__init__(...)` runs the `__init__` function of the **Client** class, this is equivalent to `discord.Client(...)`. Then, `self.tree = discord.app_commands.CommandTree(self)` creates a CommandTree which acts as the container for slash commaands. -- Then in the `setup_hook`, `self.tree.copy_global_to(...)` adds the slash command to the guild of which the ID is provided as a `discord.Object` object. Further up, `self.tree.sync()` updates the API with any changes to the Slash Commands. +- Next up `super().__init__(...)` runs the `__init__` function of the **Client** class, this is equivalent to `discord.Client(...)`. Then, `self.tree = discord.app_commands.CommandTree(self)` creates a CommandTree which acts as the container for slash commands. +- Then in the `setup_hook`, `self.tree.copy_global_to(...)` adds the slash command to the guild of which the ID is provided as a `discord.Object` object. **Essential to creation of commands** Further up, `self.tree.sync()` updates the API with any changes to the Slash Commands. - Finishing up with the **Client** subclass, we create an instance of the subclassed Client class which here has been named as `SlashClient` with `client = SlashClient()`. - Then using the `command` method of the `CommandTree` we decorate a function with it as `client.tree` is an instance of `CommandTree` for the current application. The command function takes a default argument as said, which acts as the interaction that took place. Catching up is `await interaction.response.send_message("pong")` which sends back a message to the slash command invoker. - And the classic old `client.run("token")` is used to connect the client to the discord gateway. -- Note that the `send_message` is a method of the `InteractionResponse` class and `interaction.response` in this case is an instance of the `InteractionResponse` object. The `send_message` method will not function if the response is not sent within 3 seconds of command invocation. We will discuss how to handle this issue later following the gist. +- Note that the `send_message` is a method of the `InteractionResponse` class and `interaction.response` in this case is an instance of the `InteractionResponse` object. The `send_message` method will not function if the response is not sent within 3 seconds of command invocation. We will discuss how to handle this issue later following the Gist. -## Slash Command Application with a Bot +## Slash Command application with the Bot class ```python import discord @@ -131,7 +132,7 @@ The above example shows a basic Slash Commands within discord.py using the Bot c __**EXPLANATION**__ -Most of the explanation is the same as the prior example which featured `SlashClient` which was a subclass of **discord.Client**. Though some minor changes are discussed below. +Most of the explanation is the same as the prior example that featured `SlashClient` which was a subclass of **discord.Client**. Though some minor changes are discussed below. - The `SlashBot` class now subclasses `discord.ext.commands.Bot` following the passing in of the required arguments to its `__init__` method. - `discord.ext.commands.Bot` already consists of an instance of the `CommandTree` class which can be accessed using the `tree` property. @@ -173,7 +174,7 @@ bot.run("token") __**EXPLANATION**__ -- Firstly, `import discord` imports the **discord.py** package. `from discord import app_commands` imports the `app_commands` directory from the **discord.py** root directory. `from discord.ext import commands` imports the commands extension. +- Firstly, `import discord` imports the **discord.py** package. `from discord import app_commands` imports the `app_commands` module from the **discord.py** root module. `from discord.ext import commands` imports the commands extension. - Further up, `class MySlashCog(commands.Cog)` is a class subclassing the `Cog` class. You can read more about this [here](https://discordpy.readthedocs.io/en/latest/ext/commands/cogs.html#ext-commands-cogs). - `def __init__(self, bot: commands.Bot): self.bot = bot` is the constructor method of the class that is always run when the class is instantiated and that is why we pass in a **Bot** object whenever we create an instance of the cog class. - Following up is the `@app_commands.command(name="ping", description="...")` decorator. This decorator basically functions the same as a `bot.tree.command` but since the cog currently does not have a **bot**, the `app_commands.command` decorator is used instead. The next two lines follow the same structure for Slash Commands with **self** added as the first parameter to the function as it is a method of a class. @@ -223,7 +224,7 @@ bot.run("token") ``` __**EXPLANATION**__ -- The only difference used here is `group = app_commands.Group(name="uwu", description="...")` and `group.command`. `app_commands.Group` is used to initiate a group while `group.command` registers a command under a group. For example, the ping command can be run using **/ping** but this is not the case for group commands. They are registered with the format of `group_name command_name`. So here, the **command** command of the **uwu** group would be run using **/uwu command**. Note that only group commands can have a single space between them. +- The only difference used here is `group = app_commands.Group(name="uwu", description="...")` and `group.command`. `app_commands.Group` is used to initiate a group while `group.command` reGisters a command under a group. For example, the ping command can be run using **/ping** but this is not the case for group commands. They are reGistered with the format of `group_name command_name`. So here, the **command** command of the **uwu** group would be run using **/uwu command**. Note that only group commands can have a single space between them. --- @@ -262,7 +263,7 @@ bot.run("token") ``` __**EXPLANATION**__ -- The only difference here too is that the `MySlashGroup` class directly subclasses the **Group** class from discord.app_commands which automatically registers all the methods within the group class to be commands of that specific group. So now, the commands such as `ping` can be run using **/uwu ping** and `command` using **/uwu command**. +- The only difference here too is that the `MySlashGroup` class directly subclasses the **Group** class from discord.app_commands which automatically reGisters all the methods within the group class to be commands of that specific group. So now, the commands such as `ping` can be run using **/uwu ping** and `command` using **/uwu command**. # Some common methods and features used for Slash Commands. @@ -282,7 +283,7 @@ bot = commands.Bot(command_prefix=".", intents=discord.Intents.default()) @bot.tree.command(name="echo", description="...") @app_commands.describe(text="The text to send!", channel="The channel to send the message in!") async def _echo(interaction: discord.Interaction, text: str, channel: discord.TextChannel=None): - channel = interaction.channel or channel + channel = channel or interaction.channel await channel.send(text) ``` @@ -327,7 +328,7 @@ async def _ping(interaction: discord.Interaction): ``` -If the check fails, it will raise a `MissingPermissions` error which can be handled within an app commands error handler! We will discuss making an error handler later in the gist. All the permissions can be found [here](https://discordpy.readthedocs.io/en/latest/api.html?highlight=discord%20permissions#discord.Permissions). +If the check fails, it will raise a `MissingPermissions` error which can be handled within an app commands error handler! We will discuss making an error handler later in the Gist. All the permissions can be found [here](https://discordpy.readthedocs.io/en/latest/api.html?highlight=discord%20permissions#discord.Permissions). Other methods that you can decorate the commands with are - - `bot_has_permissions` | This checks if the bot has the required permissions for executing the slash command. This raises a [BotMissingPermissions](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.BotMissingPermissions) exception. @@ -339,7 +340,7 @@ Other methods that you can decorate the commands with are - --- -Slash Commands within discord.py can be applied cooldowns to in order to prevent spamming of the commands. This can be done through the `discord.app_commands.checks.cooldown` method which can be used to decorate a slash command function and register a cooldown to the function. This raises a [CommandOnCooldown](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=checks%20cooldown#discord.app_commands.CommandOnCooldown) exception if the command is currently on cooldown. +Slash Commands within discord.py can be applied cooldowns to in order to prevent spamming of the commands. This can be done through the `discord.app_commands.checks.cooldown` method which can be used to decorate a slash command function and reGister a cooldown to the function. This raises a [CommandOnCooldown](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=checks%20cooldown#discord.app_commands.CommandOnCooldown) exception if the command is currently on cooldown. An example is as follows. ```python @@ -369,9 +370,9 @@ bot.run("token") ``` __**EXPLANATION**__ -- The first argument the `cooldown` method takes is the number of times this command can be invoked in a particular unit of time (Which will be defined in the following argument). +- The first argument is the number of times this command can be invoked before the cooldown is triggered. - The second argument it takes is the period of time in which the command can be run the specified number of times. -- The `CommandOnCooldown` exception can be handled using an error handler. We will discuss making an error handler for Slash Commands later in the gist. +- The `CommandOnCooldown` exception can be handled using an error handler. We will discuss making an error handler for Slash Commands later in the Gist. # Handling errors for Slash Commands! -- cgit v1.2.3 From 2c6ce25b38e67e5c7755c0cea8a14d8dcd2a3ae0 Mon Sep 17 00:00:00 2001 From: Xithrius Date: Sun, 20 Nov 2022 05:27:29 -0800 Subject: I can't spell --- .../content/resources/guides/python-guides/app_commands.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/python-guides/app_commands.md b/pydis_site/apps/content/resources/guides/python-guides/app_commands.md index ed6b24cd..9880beb5 100644 --- a/pydis_site/apps/content/resources/guides/python-guides/app_commands.md +++ b/pydis_site/apps/content/resources/guides/python-guides/app_commands.md @@ -2,7 +2,7 @@ --- -Upon resumption of the most popular discord API wrapper library for python, `discord.py`, while catching on to the latest features of the discord API, there have been numerous changes with addition of features to the library. Some additions to the library are -> Buttons support, Select Menus Support, Forms (AKA Modals), Slash Commands (AKA Application Commands) and a bunch of more handy features! All the changes can be found [here](https://discordpy.readthedocs.io/en/latest/migrating.html). Original discord.py Gist regarding resumption can be found [here](https://Gist.github.com/Rapptz/c4324f17a80c94776832430007ad40e6). +Upon resumption of the most popular discord API wrapper library for python, `discord.py`, while catching on to the latest features of the discord API, there have been numerous changes with addition of features to the library. Some additions to the library are -> Buttons support, Select Menus Support, Forms (AKA Modals), Slash Commands (AKA Application Commands) and a bunch of more handy features! All the changes can be found [here](https://discordpy.readthedocs.io/en/latest/migrating.html). Original discord.py Gist regarding resumption can be found [here](https://gist.github.com/Rapptz/c4324f17a80c94776832430007ad40e6). # Why this Gist? @@ -22,7 +22,7 @@ Slash Commands are the exciting new way to build and interact with bots on Disco To use Slash Commands with discord.py. The latest up-to-date version has to be installed. Make sure that the version is 2.0 or above! And make sure to uninstall any third party libraries that support Slash Commands for discord.py (if any) as a few of them [monkey patch](https://en.wikipedia.org/wiki/Monkey_patch#:~:text=A%20monkey%20patch%20is%20a,running%20instance%20of%20the%20program) discord.py! -The latest and up-to-date usable discord.py version can be installed using `pip install -U discord.py`. If you get an error such as: `'git' is not recognized...`, it means that you don't have it installed on your platform. In that case, you will need to do do so by going through the required steps for [installing git](https://git-scm.com/downloads) and make sure to enable the `add to path` option while in the installation wizard. After installing git you can run `pip install -U discord.py` again to install discord.py 2.0. +The latest and up-to-date usable discord.py version can be installed using `pip install -U discord.py`. If you get an error such as: `'git' is not recognized...`, it means that you don't have git installed on your platform. In that case, you will need to do so by going through the required steps for [installing git](https://git-scm.com/downloads) and make sure to enable the `add to path` option while in the installation wizard. After installing git you can run `pip install -U discord.py` again to install discord.py 2.0 **BEFORE MIGRATING TO DISCORD.PY 2.0, PLEASE READ THE CONSEQUENCES OF THE UPDATE [HERE](https://discordpy.readthedocs.io/en/latest/migrating.html)**. @@ -224,7 +224,7 @@ bot.run("token") ``` __**EXPLANATION**__ -- The only difference used here is `group = app_commands.Group(name="uwu", description="...")` and `group.command`. `app_commands.Group` is used to initiate a group while `group.command` reGisters a command under a group. For example, the ping command can be run using **/ping** but this is not the case for group commands. They are reGistered with the format of `group_name command_name`. So here, the **command** command of the **uwu** group would be run using **/uwu command**. Note that only group commands can have a single space between them. +- The only difference used here is `group = app_commands.Group(name="uwu", description="...")` and `group.command`. `app_commands.Group` is used to initiate a group while `group.command` registers a command under a group. For example, the ping command can be run using **/ping** but this is not the case for group commands. They are registered with the format of `group_name command_name`. So here, the **command** command of the **uwu** group would be run using **/uwu command**. Note that only group commands can have a single space between them. --- @@ -263,8 +263,7 @@ bot.run("token") ``` __**EXPLANATION**__ -- The only difference here too is that the `MySlashGroup` class directly subclasses the **Group** class from discord.app_commands which automatically reGisters all the methods within the group class to be commands of that specific group. So now, the commands such as `ping` can be run using **/uwu ping** and `command` using **/uwu command**. - +- The only difference here too is that the `MySlashGroup` class directly subclasses the **Group** class from discord.app_commands which automatically registers all the methods within the group class to be commands of that specific group. So now, the commands such as `ping` can be run using **/uwu ping** and `command` using **/uwu command**. # Some common methods and features used for Slash Commands. @@ -340,7 +339,7 @@ Other methods that you can decorate the commands with are - --- -Slash Commands within discord.py can be applied cooldowns to in order to prevent spamming of the commands. This can be done through the `discord.app_commands.checks.cooldown` method which can be used to decorate a slash command function and reGister a cooldown to the function. This raises a [CommandOnCooldown](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=checks%20cooldown#discord.app_commands.CommandOnCooldown) exception if the command is currently on cooldown. +Slash Commands within discord.py can be applied cooldowns to in order to prevent spamming of the commands. This can be done through the `discord.app_commands.checks.cooldown` method which can be used to decorate a slash command function and register a cooldown to the function. This raises a [CommandOnCooldown](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=checks%20cooldown#discord.app_commands.CommandOnCooldown) exception if the command is currently on cooldown. An example is as follows. ```python -- cgit v1.2.3 From 0179ae50cbb49c83eeca4493e14a67e813ef3772 Mon Sep 17 00:00:00 2001 From: Xithrius Date: Sun, 20 Nov 2022 06:11:52 -0800 Subject: Appeased the requests from reviews. --- .../setting-different-statuses-on-your-bot.md | 39 +++++++++++----------- 1 file changed, 19 insertions(+), 20 deletions(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/python-guides/setting-different-statuses-on-your-bot.md b/pydis_site/apps/content/resources/guides/python-guides/setting-different-statuses-on-your-bot.md index 53390416..b44c55e1 100644 --- a/pydis_site/apps/content/resources/guides/python-guides/setting-different-statuses-on-your-bot.md +++ b/pydis_site/apps/content/resources/guides/python-guides/setting-different-statuses-on-your-bot.md @@ -1,13 +1,22 @@ --- -title: Setting Different Statuses to Set Your Bot +title: Setting Different Statuses on Your Bot description: How to personalize your Discord bot status --- + +You've probably seen a bot or two have a status message under their username in the member bar set to something such as `Playing Commands: .help`. + +This guide shows how to set such a presence, so your bot can have one as well. + **Please note:** -If you want to change the bot status, it is suggested to not do it during the on_ready event, since it would be called -many times and making an API call on that event has a chance to disconnect the bot. -Instead, set the desired status using the activity / status kwarg of commands.Bot, for example -`bot = commands.Bot(command_prefix="!", activity=..., status=...)` +If you want to change the bot status, it is suggested to not do so during the on_ready event, since it would be called many times and making an API call on that event has a chance to disconnect the bot. + +Instead, set the desired status using the activity / status kwarg of commands.Bot, for example: +```python +bot = commands.Bot(command_prefix="!", activity=..., status=...) +``` + +The following are examples of what you can put into the `activity` keyword argument. #### Setting 'Playing' Status ```python @@ -29,19 +38,9 @@ await client.change_presence(activity=discord.Activity(type=discord.ActivityType await client.change_presence(activity=discord.Activity(type=discord.ActivityType.watching, name="a movie")) ``` -#### Add Optional Status as Well: - -* status=discord.Status.\ - -####Available Statuses: - -* do_not_disturb(red icon) - - -* idle(yellow icon) - - -* online(default, green icon) - +### Add Optional Status as Well: -* offline(gray icon) +* `discord.Status.online` (default, green icon) +* `discord.Status.idle` (yellow icon) +* `discord.Status.do_not_disturb` (red icon) +* `discord.Status.offline` (gray icon) -- cgit v1.2.3 From 7e12ad290b0a5c1d9f5ca0bb4ba0f20983552270 Mon Sep 17 00:00:00 2001 From: wookie184 Date: Sun, 20 Nov 2022 14:17:42 +0000 Subject: Changes from proofread --- .../resources/guides/python-guides/app_commands.md | 63 ++++++---------------- 1 file changed, 16 insertions(+), 47 deletions(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/python-guides/app_commands.md b/pydis_site/apps/content/resources/guides/python-guides/app_commands.md index 9880beb5..1354a136 100644 --- a/pydis_site/apps/content/resources/guides/python-guides/app_commands.md +++ b/pydis_site/apps/content/resources/guides/python-guides/app_commands.md @@ -1,44 +1,33 @@ -# DISCORD.PY RESUMATION CHANGES - --- - -Upon resumption of the most popular discord API wrapper library for python, `discord.py`, while catching on to the latest features of the discord API, there have been numerous changes with addition of features to the library. Some additions to the library are -> Buttons support, Select Menus Support, Forms (AKA Modals), Slash Commands (AKA Application Commands) and a bunch of more handy features! All the changes can be found [here](https://discordpy.readthedocs.io/en/latest/migrating.html). Original discord.py Gist regarding resumption can be found [here](https://gist.github.com/Rapptz/c4324f17a80c94776832430007ad40e6). - -# Why this Gist? - +title: Discord.py 2.0 changes +description: Changes and new features in version 2.0 of discord.py --- -This Gist is being created as an update to Slash Commands (app commands) with explanation and examples. This Gist mainly focuses on **SLASH COMMANDS** for discord.py 2.0 (and above)! +Upon the return of the most popular discord API wrapper library for Python, `discord.py`, while catching on to the latest features of the discord API, there have been numerous changes with additions of features to the library. Additions to the library include support for Buttons, Select Menus, Forms (AKA Modals), Slash Commands (AKA Application Commands) and a bunch more handy features! All the changes can be found [here](https://discordpy.readthedocs.io/en/latest/migrating.html). Original discord.py Gist regarding resumption can be found [here](https://gist.github.com/Rapptz/c4324f17a80c94776832430007ad40e6). -# What are Slash Commands? ---- +# Install the latest version of discord.py -Slash Commands are the exciting new way to build and interact with bots on Discord. As soon as you type "/", you can easily see all the commands a bot has. It also comes with autocomplete, validation and error handling, which will all help you get the command right the first time. +Before you can make use of any of the new 2.0 features, you need to install the latest version of discord.py. Make sure that the version is 2.0 or above! +Also, make sure to uninstall any third party libraries intended to add slash-command support to pre-2.0 discord.py, as they are no longer necessary and will likely cause issues. -# Install the latest version of discord.py +The latest and most up-to-date stable discord.py version can be installed using `pip install -U discord.py`. ---- -To use Slash Commands with discord.py. The latest up-to-date version has to be installed. Make sure that the version is 2.0 or above! -And make sure to uninstall any third party libraries that support Slash Commands for discord.py (if any) as a few of them [monkey patch](https://en.wikipedia.org/wiki/Monkey_patch#:~:text=A%20monkey%20patch%20is%20a,running%20instance%20of%20the%20program) discord.py! +**Before migrating to discord.py 2.0, make sure you read the migration guide [here](https://discordpy.readthedocs.io/en/latest/migrating.html) as there are lots of breaking changes.**. +{: .notification .is-warning } -The latest and up-to-date usable discord.py version can be installed using `pip install -U discord.py`. If you get an error such as: `'git' is not recognized...`, it means that you don't have git installed on your platform. In that case, you will need to do so by going through the required steps for [installing git](https://git-scm.com/downloads) and make sure to enable the `add to path` option while in the installation wizard. After installing git you can run `pip install -U discord.py` again to install discord.py 2.0 +# What are Slash Commands? -**BEFORE MIGRATING TO DISCORD.PY 2.0, PLEASE READ THE CONSEQUENCES OF THE UPDATE [HERE](https://discordpy.readthedocs.io/en/latest/migrating.html)**. +Slash Commands are an exciting new way to build and interact with bots on Discord. As soon as you type "/", you can easily see all the commands a bot has. It also comes with autocomplete, validation and error handling, which will all help users of your bot get the command right the first time. # Basic structure for discord.py Slash Commands! ---- - ### Note that Slash Commands in discord.py are also referred to as **Application Commmands** and **App Commands** and every *interaction* is a *webhook*. Slash commands in discord.py are held by a container, [CommandTree](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=commandtree#discord.app_commands.CommandTree). A command tree is required to create Slash Commands in discord.py. This command tree provides a `command` method which decorates an asynchronous function indicating to discord.py that the decorated function is intended to be a slash command. This asynchronous function expects a default argument which acts as the interaction which took place that invoked the slash command. This default argument is an instance of the **Interaction** class from discord.py. Further up, the command logic takes over the behaviour of the slash command. # Fundamentals for this Gist! ---- - - -The fundamental for this Gist will remain to be the `setup_hook`. The `setup_hook` is a special asynchronous method of the Client and Bot classes which can be overwritten to perform numerous tasks. This method is safe to use as it is always triggered before any events are dispatched, i.e. this method is triggered before the *IDENTIFY* payload is sent to the discord gateway. +One new feature added in discord.py v2 is `setup_hook`. `setup_hook` is a special asynchronous method of the Client and Bot classes which can be overwritten to perform numerous tasks. This method is safe to use as it is always triggered before any events are dispatched, i.e. this method is triggered before the *IDENTIFY* payload is sent to the discord gateway. Note that methods of the Bot class such as `change_presence` will not work in setup_hook as the current application does not have an active connection to the gateway at this point. __**THE FOLLOWING ARE EXAMPLES OF HOW A `SETUP_HOOK` FUNCTION CAN BE DEFINED**__ @@ -48,7 +37,7 @@ Note that the default intents are defined [here](https://discordpy.readthedocs.i ```python import discord -'''This is one way of creating a "setup_hook" method''' +# You can create the setup_hook directly in the class definition class SlashClient(discord.Client): def __init__(self) -> None: @@ -57,7 +46,7 @@ class SlashClient(discord.Client): async def setup_hook(self) -> None: ... -'''Another way of creating a "setup_hook" is as follows''' +# Or add it to the client after creating it client = discord.Client(intents=discord.Intents.default()) async def my_setup_hook() -> None: @@ -69,7 +58,6 @@ client.setup_hook = my_setup_hook # Basic Slash Command application using discord.py. #### The `CommandTree` class resides within the `app_commands` of the discord.py package. ---- ## Slash Command Application with a Client @@ -139,8 +127,6 @@ Most of the explanation is the same as the prior example that featured `SlashCli # Slash Commands within a Cog! ---- - A cog is a collection of commands, listeners, and optional state to help group commands together. More information on them can be found on the [Cogs](https://discordpy.readthedocs.io/en/latest/ext/commands/cogs.html#ext-commands-cogs) page. ## An Example to using cogs with discord.py for Slash Commands! @@ -184,8 +170,6 @@ __**EXPLANATION**__ # An Example to using groups with discord.py for Slash Commands! ---- - ## An example with optional group! ```python @@ -226,8 +210,6 @@ bot.run("token") __**EXPLANATION**__ - The only difference used here is `group = app_commands.Group(name="uwu", description="...")` and `group.command`. `app_commands.Group` is used to initiate a group while `group.command` registers a command under a group. For example, the ping command can be run using **/ping** but this is not the case for group commands. They are registered with the format of `group_name command_name`. So here, the **command** command of the **uwu** group would be run using **/uwu command**. Note that only group commands can have a single space between them. ---- - ## An example with a **Group** subclass! ```python @@ -267,8 +249,6 @@ __**EXPLANATION**__ # Some common methods and features used for Slash Commands. ---- - ### A common function used for Slash Commands is the `describe` function. This is used to add descriptions to the arguments of a slash command. The command function can decorated with this function. It goes by the following syntax as shown below. ```python @@ -308,8 +288,6 @@ async def _time(interaction: discord.Interaction, time_to_wait: int): # Checking for Permissions and Roles! ---- - To add a permissions check to a command, the methods are imported through `discord.app_commands.checks`. To check for a member's permissions, the function can be decorated with the [discord.app_commands.checks.has_permissions](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=has_permissions#discord.app_commands.checks.has_permissions) method. An example to this as follows. ```py @@ -337,8 +315,6 @@ Other methods that you can decorate the commands with are - # Adding cooldowns to Slash Commands! ---- - Slash Commands within discord.py can be applied cooldowns to in order to prevent spamming of the commands. This can be done through the `discord.app_commands.checks.cooldown` method which can be used to decorate a slash command function and register a cooldown to the function. This raises a [CommandOnCooldown](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=checks%20cooldown#discord.app_commands.CommandOnCooldown) exception if the command is currently on cooldown. An example is as follows. @@ -350,7 +326,6 @@ class Bot(commands.Bot): def __init__(self): super().__init__(command_prefix="uwu", intents=discord.Intents.all()) - async def setup_hook(self): self.tree.copy_global_to(guild=discord.Object(id=12345678909876543)) await self.tree.sync() @@ -376,8 +351,6 @@ __**EXPLANATION**__ # Handling errors for Slash Commands! ---- - The Slash Commands exceptions can be handled by overwriting the `on_error` method of the `CommandTree`. The error handler takes two arguments. The first argument is the `Interaction` that took place when the error occurred and the second argument is the error that occurred when the Slash Commands was invoked. The error is an instance of [discord.app_commands.AppCommandError](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=appcommanderror#discord.app_commands.AppCommandError) which is a subclass of [DiscordException](https://discordpy.readthedocs.io/en/latest/api.html?highlight=discordexception#discord.DiscordException). An example to creating an error handler for Slash Commands is as follows. @@ -397,10 +370,8 @@ async def ping(interaction: discord.Interaction): async def on_tree_error(interaction: discord.Interaction, error: app_commands.AppCommandError): if isinstance(error, app_commands.CommandOnCooldown): return await interaction.response.send_message(f"Command is currently on cooldown! Try again in **{error.retry_after:.2f}** seconds!") - - elif isinstance(..., ...): + elif isinstance(error, ...): ... - else: raise error @@ -433,10 +404,8 @@ async def ping(interaction: discord.Interaction): async def ping_error(interaction: discord.Interaction, error: app_commands.AppCommandError): if isinstance(error, app_commands.CommandOnCooldown): return await interaction.response.send_message(f"Command is currently on cooldown! Try again in **{error.retry_after:.2f}** seconds!") - elif isinstance(error, ...): ... - else: raise error @@ -445,4 +414,4 @@ bot.run("token") __**EXPLANATION**__ -Here the command name is simply used to access the `error` method to decorate a function which acts as the `on_error` but for a specific command. Please do not call the `error` method. +Here the command name is simply used to access the `error` method to decorate a function which acts as the `on_error` but for a specific command. You should not need to call the `error` method manually. -- cgit v1.2.3 From 58845b3653d6a4864c9d32a2950e3e3f8e74c2c5 Mon Sep 17 00:00:00 2001 From: wookie184 Date: Sun, 20 Nov 2022 14:19:08 +0000 Subject: Rename file for consistency --- .../resources/guides/python-guides/app-commands.md | 417 +++++++++++++++++++++ .../resources/guides/python-guides/app_commands.md | 417 --------------------- 2 files changed, 417 insertions(+), 417 deletions(-) create mode 100644 pydis_site/apps/content/resources/guides/python-guides/app-commands.md delete mode 100644 pydis_site/apps/content/resources/guides/python-guides/app_commands.md (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/python-guides/app-commands.md b/pydis_site/apps/content/resources/guides/python-guides/app-commands.md new file mode 100644 index 00000000..1354a136 --- /dev/null +++ b/pydis_site/apps/content/resources/guides/python-guides/app-commands.md @@ -0,0 +1,417 @@ +--- +title: Discord.py 2.0 changes +description: Changes and new features in version 2.0 of discord.py +--- + +Upon the return of the most popular discord API wrapper library for Python, `discord.py`, while catching on to the latest features of the discord API, there have been numerous changes with additions of features to the library. Additions to the library include support for Buttons, Select Menus, Forms (AKA Modals), Slash Commands (AKA Application Commands) and a bunch more handy features! All the changes can be found [here](https://discordpy.readthedocs.io/en/latest/migrating.html). Original discord.py Gist regarding resumption can be found [here](https://gist.github.com/Rapptz/c4324f17a80c94776832430007ad40e6). + + +# Install the latest version of discord.py + +Before you can make use of any of the new 2.0 features, you need to install the latest version of discord.py. Make sure that the version is 2.0 or above! +Also, make sure to uninstall any third party libraries intended to add slash-command support to pre-2.0 discord.py, as they are no longer necessary and will likely cause issues. + +The latest and most up-to-date stable discord.py version can be installed using `pip install -U discord.py`. + +**Before migrating to discord.py 2.0, make sure you read the migration guide [here](https://discordpy.readthedocs.io/en/latest/migrating.html) as there are lots of breaking changes.**. +{: .notification .is-warning } + +# What are Slash Commands? + +Slash Commands are an exciting new way to build and interact with bots on Discord. As soon as you type "/", you can easily see all the commands a bot has. It also comes with autocomplete, validation and error handling, which will all help users of your bot get the command right the first time. + +# Basic structure for discord.py Slash Commands! + +### Note that Slash Commands in discord.py are also referred to as **Application Commmands** and **App Commands** and every *interaction* is a *webhook*. +Slash commands in discord.py are held by a container, [CommandTree](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=commandtree#discord.app_commands.CommandTree). A command tree is required to create Slash Commands in discord.py. This command tree provides a `command` method which decorates an asynchronous function indicating to discord.py that the decorated function is intended to be a slash command. This asynchronous function expects a default argument which acts as the interaction which took place that invoked the slash command. This default argument is an instance of the **Interaction** class from discord.py. Further up, the command logic takes over the behaviour of the slash command. + +# Fundamentals for this Gist! + +One new feature added in discord.py v2 is `setup_hook`. `setup_hook` is a special asynchronous method of the Client and Bot classes which can be overwritten to perform numerous tasks. This method is safe to use as it is always triggered before any events are dispatched, i.e. this method is triggered before the *IDENTIFY* payload is sent to the discord gateway. +Note that methods of the Bot class such as `change_presence` will not work in setup_hook as the current application does not have an active connection to the gateway at this point. + +__**THE FOLLOWING ARE EXAMPLES OF HOW A `SETUP_HOOK` FUNCTION CAN BE DEFINED**__ + +Note that the default intents are defined [here](https://discordpy.readthedocs.io/en/stable/api.html?highlight=discord%20intents%20default#discord.Intents.default) to have all intents enabled except presences, members, and message_content. + +```python +import discord + +# You can create the setup_hook directly in the class definition + +class SlashClient(discord.Client): + def __init__(self) -> None: + super().__init__(intents=discord.Intents.default()) + + async def setup_hook(self) -> None: + ... + +# Or add it to the client after creating it + +client = discord.Client(intents=discord.Intents.default()) +async def my_setup_hook() -> None: + ... + +client.setup_hook = my_setup_hook +``` + +# Basic Slash Command application using discord.py. + +#### The `CommandTree` class resides within the `app_commands` of the discord.py package. + +## Slash Command Application with a Client + +```python +import discord + +class SlashClient(discord.Client): + def __init__(self) -> None: + super().__init__(intents=discord.Intents.default()) + self.tree = discord.app_commands.CommandTree(self) + + async def setup_hook(self) -> None: + self.tree.copy_global_to(guild=discord.Object(id=12345678900987654)) + await self.tree.sync() + +client = SlashClient() + +@client.tree.command(name="ping", description="...") +async def _ping(interaction: discord.Interaction) -> None: + await interaction.response.send_message("pong") + +client.run("token") +``` + + +__**EXPLANATION**__ + +- `import discord` imports the **discord.py** package. +- `class SlashClient(discord.Client)` is a class subclassing **Client**. Though there is no particular reason except readability to subclass the **Client** class, using the `Client.setup_hook = my_func` is equally valid. +- Next up `super().__init__(...)` runs the `__init__` function of the **Client** class, this is equivalent to `discord.Client(...)`. Then, `self.tree = discord.app_commands.CommandTree(self)` creates a CommandTree which acts as the container for slash commands. +- Then in the `setup_hook`, `self.tree.copy_global_to(...)` adds the slash command to the guild of which the ID is provided as a `discord.Object` object. **Essential to creation of commands** Further up, `self.tree.sync()` updates the API with any changes to the Slash Commands. +- Finishing up with the **Client** subclass, we create an instance of the subclassed Client class which here has been named as `SlashClient` with `client = SlashClient()`. +- Then using the `command` method of the `CommandTree` we decorate a function with it as `client.tree` is an instance of `CommandTree` for the current application. The command function takes a default argument as said, which acts as the interaction that took place. Catching up is `await interaction.response.send_message("pong")` which sends back a message to the slash command invoker. +- And the classic old `client.run("token")` is used to connect the client to the discord gateway. +- Note that the `send_message` is a method of the `InteractionResponse` class and `interaction.response` in this case is an instance of the `InteractionResponse` object. The `send_message` method will not function if the response is not sent within 3 seconds of command invocation. We will discuss how to handle this issue later following the Gist. + +## Slash Command application with the Bot class + +```python +import discord + +class SlashBot(commands.Bot): + def __init__(self) -> None: + super().__init__(command_prefix=".", intents=discord.Intents.default()) + + async def setup_hook(self) -> None: + self.tree.copy_global_to(guild=discord.Object(id=12345678900987654)) + await self.tree.sync() + +bot = SlashBot() + +@bot.tree.command(name="ping", description="...") +async def _ping(interaction: discord.Interaction) -> None: + await interaction.response.send_message("pong") + +bot.run("token") +``` + +The above example shows a basic Slash Commands within discord.py using the Bot class. + +__**EXPLANATION**__ + +Most of the explanation is the same as the prior example that featured `SlashClient` which was a subclass of **discord.Client**. Though some minor changes are discussed below. + +- The `SlashBot` class now subclasses `discord.ext.commands.Bot` following the passing in of the required arguments to its `__init__` method. +- `discord.ext.commands.Bot` already consists of an instance of the `CommandTree` class which can be accessed using the `tree` property. + +# Slash Commands within a Cog! + +A cog is a collection of commands, listeners, and optional state to help group commands together. More information on them can be found on the [Cogs](https://discordpy.readthedocs.io/en/latest/ext/commands/cogs.html#ext-commands-cogs) page. + +## An Example to using cogs with discord.py for Slash Commands! + +```python +import discord +from discord.ext import commands +from discord import app_commands + +class MySlashCog(commands.Cog): + def __init__(self, bot: commands.Bot) -> None: + self.bot = bot + + @app_commands.command(name="ping", description="...") + async def _ping(self, interaction: discord.Interaction): + await interaction.response.send_message("pong!") + +class MySlashBot(commands.Bot): + def __init__(self) -> None: + super().__init__(command_prefix="!", intents=discord.Intents.default()) + + async def setup_hook(self) -> None: + await self.add_cog(MySlashCog(self)) + await self.tree.copy_global_to(discord.Object(id=123456789098765432)) + await self.tree.sync() + +bot = MySlashBot() + +bot.run("token") +``` + +__**EXPLANATION**__ + +- Firstly, `import discord` imports the **discord.py** package. `from discord import app_commands` imports the `app_commands` module from the **discord.py** root module. `from discord.ext import commands` imports the commands extension. +- Further up, `class MySlashCog(commands.Cog)` is a class subclassing the `Cog` class. You can read more about this [here](https://discordpy.readthedocs.io/en/latest/ext/commands/cogs.html#ext-commands-cogs). +- `def __init__(self, bot: commands.Bot): self.bot = bot` is the constructor method of the class that is always run when the class is instantiated and that is why we pass in a **Bot** object whenever we create an instance of the cog class. +- Following up is the `@app_commands.command(name="ping", description="...")` decorator. This decorator basically functions the same as a `bot.tree.command` but since the cog currently does not have a **bot**, the `app_commands.command` decorator is used instead. The next two lines follow the same structure for Slash Commands with **self** added as the first parameter to the function as it is a method of a class. +- The next up lines are mostly the same. +- Talking about the first line inside the `setup_hook` is the `add_cog` method of the **Bot** class. And since **self** acts as the **instance** of the current class, we use **self** to use the `add_cog` method of the **Bot** class as we are inside a subclassed class of the **Bot** class. Then we pass in **self** to the `add_cog` method as the `__init__` function of the **MySlashCog** cog accepts a `Bot` object. +- After that we instantiate the `MySlashBot` class and run the bot using the **run** method which executes our setup_hook function and our commands get loaded and synced. The bot is now ready to use! + +# An Example to using groups with discord.py for Slash Commands! + +## An example with optional group! + +```python +import discord +from discord.ext import commands +from discord import app_commands + +class MySlashGroupCog(commands.Cog): + def __init__(self, bot: commands.Bot) -> None: + self.bot = bot + + #-------------------------------------------------------- + group = app_commands.Group(name="uwu", description="...") + #-------------------------------------------------------- + + @app_commands.command(name="ping", description="...") + async def _ping(self, interaction: discord.) -> None: + await interaction.response.send_message("pong!") + + @group.command(name="command", description="...") + async def _cmd(self, interaction: discord.Interaction) -> None: + await interaction.response.send_message("uwu") + +class MySlashBot(commands.Bot): + def __init__(self) -> None: + super().__init__(command_prefix="!", intents=discord.Intents.default()) + + async def setup_hook(self) -> None: + await self.add_cog(MySlashGroupCog(self)) + await self.tree.copy_global_to(discord.Object(id=123456789098765432)) + await self.tree.sync() + +bot = MySlashBot() + +bot.run("token") +``` + +__**EXPLANATION**__ +- The only difference used here is `group = app_commands.Group(name="uwu", description="...")` and `group.command`. `app_commands.Group` is used to initiate a group while `group.command` registers a command under a group. For example, the ping command can be run using **/ping** but this is not the case for group commands. They are registered with the format of `group_name command_name`. So here, the **command** command of the **uwu** group would be run using **/uwu command**. Note that only group commands can have a single space between them. + +## An example with a **Group** subclass! + +```python +import discord +from discord.ext import commands +from discord import app_commands + +class MySlashGroup(app_commands.Group, name="uwu"): + def __init__(self, bot: commands.Bot) -> None: + self.bot = bot + super().__init__() + + @app_commands.command(name="ping", description="...") + async def _ping(self, interaction: discord.) -> None: + await interaction.response.send_message("pong!") + + @app_commands.command(name="command", description="...") + async def _cmd(self, interaction: discord.Interaction) -> None: + await interaction.response.send_message("uwu") + +class MySlashBot(commands.Bot): + def __init__(self) -> None: + super().__init__(command_prefix="!", intents=discord.Intents.default()) + + async def setup_hook(self) -> None: + await self.add_cog(MySlashGroup(self)) + await self.tree.copy_global_to(discord.Object(id=123456789098765432)) + await self.tree.sync() + +bot = MySlashBot() + +bot.run("token") +``` + +__**EXPLANATION**__ +- The only difference here too is that the `MySlashGroup` class directly subclasses the **Group** class from discord.app_commands which automatically registers all the methods within the group class to be commands of that specific group. So now, the commands such as `ping` can be run using **/uwu ping** and `command` using **/uwu command**. + +# Some common methods and features used for Slash Commands. + +### A common function used for Slash Commands is the `describe` function. This is used to add descriptions to the arguments of a slash command. The command function can decorated with this function. It goes by the following syntax as shown below. + +```python +from discord.ext import commands +from discord import app_commands +import discord + +bot = commands.Bot(command_prefix=".", intents=discord.Intents.default()) +#sync the commands + +@bot.tree.command(name="echo", description="...") +@app_commands.describe(text="The text to send!", channel="The channel to send the message in!") +async def _echo(interaction: discord.Interaction, text: str, channel: discord.TextChannel=None): + channel = channel or interaction.channel + await channel.send(text) +``` + +### Another common issue that most people come across is the time duration of sending a message with `send_message`. This issue can be tackled by deferring the interaction response using the `defer` method of the `InteractionResponse` class. An example for fixing this issue is shown below. + +```python +import discord +from discord.ext import commands +import asyncio + +bot = commands.Bot(command_prefix="!", intents=discord.Intents.default()) +#sync the commands + +@bot.tree.command(name="time", description="...") +async def _time(interaction: discord.Interaction, time_to_wait: int): + # ------------------------------------------------------------- + await interaction.response.defer(ephemeral=True, thinking=True) + # ------------------------------------------------------------- + await interaction.edit_original_message(content=f"I will notify you after {time_to_wait} seconds have passed!") + await asyncio.sleep(time_to_wait) + await interaction.edit_original_message(content=f"{interaction.user.mention}, {time_to_wait} seconds have already passed!") +``` + +# Checking for Permissions and Roles! + +To add a permissions check to a command, the methods are imported through `discord.app_commands.checks`. To check for a member's permissions, the function can be decorated with the [discord.app_commands.checks.has_permissions](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=has_permissions#discord.app_commands.checks.has_permissions) method. An example to this as follows. + +```py +from discord import app_commands +from discord.ext import commands +import discord + +bot = commands.Bot(command_prefix="!", intents=discord.Intents.default()) +#sync commands + +@bot.tree.command(name="ping") +@app_commands.checks.has_permissions(manage_messages=True, manage_channels=True) #example permissions +async def _ping(interaction: discord.Interaction): + await interaction.response.send_message("pong!") + +``` + +If the check fails, it will raise a `MissingPermissions` error which can be handled within an app commands error handler! We will discuss making an error handler later in the Gist. All the permissions can be found [here](https://discordpy.readthedocs.io/en/latest/api.html?highlight=discord%20permissions#discord.Permissions). + +Other methods that you can decorate the commands with are - +- `bot_has_permissions` | This checks if the bot has the required permissions for executing the slash command. This raises a [BotMissingPermissions](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.BotMissingPermissions) exception. +- `has_role` | This checks if the slash command user has the required role or not. Only **ONE** role name or role ID can be passed to this. If the name is being passed, make sure to have the exact same name as the role name. This raises a [MissingRole](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.MissingRole) exception. +- To pass in several role names or role IDs, `has_any_role` can be used to decorate a command. This raises two exceptions -> [MissingAnyRole](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.MissingAnyRole) and [NoPrivateMessage](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.NoPrivateMessage) + + +# Adding cooldowns to Slash Commands! + +Slash Commands within discord.py can be applied cooldowns to in order to prevent spamming of the commands. This can be done through the `discord.app_commands.checks.cooldown` method which can be used to decorate a slash command function and register a cooldown to the function. This raises a [CommandOnCooldown](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=checks%20cooldown#discord.app_commands.CommandOnCooldown) exception if the command is currently on cooldown. +An example is as follows. + +```python +from discord.ext import commands +import discord + +class Bot(commands.Bot): + def __init__(self): + super().__init__(command_prefix="uwu", intents=discord.Intents.all()) + + async def setup_hook(self): + self.tree.copy_global_to(guild=discord.Object(id=12345678909876543)) + await self.tree.sync() + + +bot = Bot() + +@bot.tree.command(name="ping") +# ----------------------------------------- +@discord.app_commands.checks.cooldown(1, 30) +# ----------------------------------------- +async def ping(interaction: discord.Interaction): + await interaction.response.send_message("pong!") + +bot.run("token") +``` + +__**EXPLANATION**__ +- The first argument is the number of times this command can be invoked before the cooldown is triggered. +- The second argument it takes is the period of time in which the command can be run the specified number of times. +- The `CommandOnCooldown` exception can be handled using an error handler. We will discuss making an error handler for Slash Commands later in the Gist. + + +# Handling errors for Slash Commands! + +The Slash Commands exceptions can be handled by overwriting the `on_error` method of the `CommandTree`. The error handler takes two arguments. The first argument is the `Interaction` that took place when the error occurred and the second argument is the error that occurred when the Slash Commands was invoked. The error is an instance of [discord.app_commands.AppCommandError](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=appcommanderror#discord.app_commands.AppCommandError) which is a subclass of [DiscordException](https://discordpy.readthedocs.io/en/latest/api.html?highlight=discordexception#discord.DiscordException). +An example to creating an error handler for Slash Commands is as follows. + +```python +from discord.ext import commands +from discord import app_commands +import discord + +bot = commands.Bot(command_prefix="!", intents=discord.Intents.default()) +#sync commands + +@bot.tree.command(name="ping") +@app_commands.checks.cooldown(1, 30) +async def ping(interaction: discord.Interaction): + await interaction.response.send_message("pong!") + +async def on_tree_error(interaction: discord.Interaction, error: app_commands.AppCommandError): + if isinstance(error, app_commands.CommandOnCooldown): + return await interaction.response.send_message(f"Command is currently on cooldown! Try again in **{error.retry_after:.2f}** seconds!") + elif isinstance(error, ...): + ... + else: + raise error + +bot.tree.on_error = on_tree_error + +bot.run("token") +``` + +__**EXPLANATION**__ + +First we create a simple asynchronous function named `on_tree_error` here. To which the first two required arguments are passed, `Interaction` which is named as `interaction` here and `AppCommandError` which is named as `error` here. Then using simple functions and keywords, we make an error handler like above. Here we have used the `isinstance` function which takes in an object and a base class as the second argument, this function returns a bool value. The `raise error` is just for displaying unhandled errors, i.e. the ones which have not been handled manually. If this is **removed**, you will not be able to see any exceptions raised by Slash Commands and makes debugging the code harder. +After creating the error handler function, we set the function as the error handler for the Slash Commands. Here, `bot.tree.on_error = on_tree_error` overwrites the default `on_error` method of the **CommandTree** class with our custom error handler which has been named as `on_tree_error` here. + +### Creating an error handler for a specific error! + +```python +from discord.ext import commands +from discord import app_commands +import discord + +bot = commands.Bot(command_prefix="!", intents=discord.Intents.default()) +#sync commands + +@bot.tree.command(name="ping") +app_commands.checks.cooldown(1, 30) +async def ping(interaction: discord.Interaction): + await interaction.response.send_message("pong!") + +@ping.error +async def ping_error(interaction: discord.Interaction, error: app_commands.AppCommandError): + if isinstance(error, app_commands.CommandOnCooldown): + return await interaction.response.send_message(f"Command is currently on cooldown! Try again in **{error.retry_after:.2f}** seconds!") + elif isinstance(error, ...): + ... + else: + raise error + +bot.run("token") +``` + +__**EXPLANATION**__ + +Here the command name is simply used to access the `error` method to decorate a function which acts as the `on_error` but for a specific command. You should not need to call the `error` method manually. diff --git a/pydis_site/apps/content/resources/guides/python-guides/app_commands.md b/pydis_site/apps/content/resources/guides/python-guides/app_commands.md deleted file mode 100644 index 1354a136..00000000 --- a/pydis_site/apps/content/resources/guides/python-guides/app_commands.md +++ /dev/null @@ -1,417 +0,0 @@ ---- -title: Discord.py 2.0 changes -description: Changes and new features in version 2.0 of discord.py ---- - -Upon the return of the most popular discord API wrapper library for Python, `discord.py`, while catching on to the latest features of the discord API, there have been numerous changes with additions of features to the library. Additions to the library include support for Buttons, Select Menus, Forms (AKA Modals), Slash Commands (AKA Application Commands) and a bunch more handy features! All the changes can be found [here](https://discordpy.readthedocs.io/en/latest/migrating.html). Original discord.py Gist regarding resumption can be found [here](https://gist.github.com/Rapptz/c4324f17a80c94776832430007ad40e6). - - -# Install the latest version of discord.py - -Before you can make use of any of the new 2.0 features, you need to install the latest version of discord.py. Make sure that the version is 2.0 or above! -Also, make sure to uninstall any third party libraries intended to add slash-command support to pre-2.0 discord.py, as they are no longer necessary and will likely cause issues. - -The latest and most up-to-date stable discord.py version can be installed using `pip install -U discord.py`. - -**Before migrating to discord.py 2.0, make sure you read the migration guide [here](https://discordpy.readthedocs.io/en/latest/migrating.html) as there are lots of breaking changes.**. -{: .notification .is-warning } - -# What are Slash Commands? - -Slash Commands are an exciting new way to build and interact with bots on Discord. As soon as you type "/", you can easily see all the commands a bot has. It also comes with autocomplete, validation and error handling, which will all help users of your bot get the command right the first time. - -# Basic structure for discord.py Slash Commands! - -### Note that Slash Commands in discord.py are also referred to as **Application Commmands** and **App Commands** and every *interaction* is a *webhook*. -Slash commands in discord.py are held by a container, [CommandTree](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=commandtree#discord.app_commands.CommandTree). A command tree is required to create Slash Commands in discord.py. This command tree provides a `command` method which decorates an asynchronous function indicating to discord.py that the decorated function is intended to be a slash command. This asynchronous function expects a default argument which acts as the interaction which took place that invoked the slash command. This default argument is an instance of the **Interaction** class from discord.py. Further up, the command logic takes over the behaviour of the slash command. - -# Fundamentals for this Gist! - -One new feature added in discord.py v2 is `setup_hook`. `setup_hook` is a special asynchronous method of the Client and Bot classes which can be overwritten to perform numerous tasks. This method is safe to use as it is always triggered before any events are dispatched, i.e. this method is triggered before the *IDENTIFY* payload is sent to the discord gateway. -Note that methods of the Bot class such as `change_presence` will not work in setup_hook as the current application does not have an active connection to the gateway at this point. - -__**THE FOLLOWING ARE EXAMPLES OF HOW A `SETUP_HOOK` FUNCTION CAN BE DEFINED**__ - -Note that the default intents are defined [here](https://discordpy.readthedocs.io/en/stable/api.html?highlight=discord%20intents%20default#discord.Intents.default) to have all intents enabled except presences, members, and message_content. - -```python -import discord - -# You can create the setup_hook directly in the class definition - -class SlashClient(discord.Client): - def __init__(self) -> None: - super().__init__(intents=discord.Intents.default()) - - async def setup_hook(self) -> None: - ... - -# Or add it to the client after creating it - -client = discord.Client(intents=discord.Intents.default()) -async def my_setup_hook() -> None: - ... - -client.setup_hook = my_setup_hook -``` - -# Basic Slash Command application using discord.py. - -#### The `CommandTree` class resides within the `app_commands` of the discord.py package. - -## Slash Command Application with a Client - -```python -import discord - -class SlashClient(discord.Client): - def __init__(self) -> None: - super().__init__(intents=discord.Intents.default()) - self.tree = discord.app_commands.CommandTree(self) - - async def setup_hook(self) -> None: - self.tree.copy_global_to(guild=discord.Object(id=12345678900987654)) - await self.tree.sync() - -client = SlashClient() - -@client.tree.command(name="ping", description="...") -async def _ping(interaction: discord.Interaction) -> None: - await interaction.response.send_message("pong") - -client.run("token") -``` - - -__**EXPLANATION**__ - -- `import discord` imports the **discord.py** package. -- `class SlashClient(discord.Client)` is a class subclassing **Client**. Though there is no particular reason except readability to subclass the **Client** class, using the `Client.setup_hook = my_func` is equally valid. -- Next up `super().__init__(...)` runs the `__init__` function of the **Client** class, this is equivalent to `discord.Client(...)`. Then, `self.tree = discord.app_commands.CommandTree(self)` creates a CommandTree which acts as the container for slash commands. -- Then in the `setup_hook`, `self.tree.copy_global_to(...)` adds the slash command to the guild of which the ID is provided as a `discord.Object` object. **Essential to creation of commands** Further up, `self.tree.sync()` updates the API with any changes to the Slash Commands. -- Finishing up with the **Client** subclass, we create an instance of the subclassed Client class which here has been named as `SlashClient` with `client = SlashClient()`. -- Then using the `command` method of the `CommandTree` we decorate a function with it as `client.tree` is an instance of `CommandTree` for the current application. The command function takes a default argument as said, which acts as the interaction that took place. Catching up is `await interaction.response.send_message("pong")` which sends back a message to the slash command invoker. -- And the classic old `client.run("token")` is used to connect the client to the discord gateway. -- Note that the `send_message` is a method of the `InteractionResponse` class and `interaction.response` in this case is an instance of the `InteractionResponse` object. The `send_message` method will not function if the response is not sent within 3 seconds of command invocation. We will discuss how to handle this issue later following the Gist. - -## Slash Command application with the Bot class - -```python -import discord - -class SlashBot(commands.Bot): - def __init__(self) -> None: - super().__init__(command_prefix=".", intents=discord.Intents.default()) - - async def setup_hook(self) -> None: - self.tree.copy_global_to(guild=discord.Object(id=12345678900987654)) - await self.tree.sync() - -bot = SlashBot() - -@bot.tree.command(name="ping", description="...") -async def _ping(interaction: discord.Interaction) -> None: - await interaction.response.send_message("pong") - -bot.run("token") -``` - -The above example shows a basic Slash Commands within discord.py using the Bot class. - -__**EXPLANATION**__ - -Most of the explanation is the same as the prior example that featured `SlashClient` which was a subclass of **discord.Client**. Though some minor changes are discussed below. - -- The `SlashBot` class now subclasses `discord.ext.commands.Bot` following the passing in of the required arguments to its `__init__` method. -- `discord.ext.commands.Bot` already consists of an instance of the `CommandTree` class which can be accessed using the `tree` property. - -# Slash Commands within a Cog! - -A cog is a collection of commands, listeners, and optional state to help group commands together. More information on them can be found on the [Cogs](https://discordpy.readthedocs.io/en/latest/ext/commands/cogs.html#ext-commands-cogs) page. - -## An Example to using cogs with discord.py for Slash Commands! - -```python -import discord -from discord.ext import commands -from discord import app_commands - -class MySlashCog(commands.Cog): - def __init__(self, bot: commands.Bot) -> None: - self.bot = bot - - @app_commands.command(name="ping", description="...") - async def _ping(self, interaction: discord.Interaction): - await interaction.response.send_message("pong!") - -class MySlashBot(commands.Bot): - def __init__(self) -> None: - super().__init__(command_prefix="!", intents=discord.Intents.default()) - - async def setup_hook(self) -> None: - await self.add_cog(MySlashCog(self)) - await self.tree.copy_global_to(discord.Object(id=123456789098765432)) - await self.tree.sync() - -bot = MySlashBot() - -bot.run("token") -``` - -__**EXPLANATION**__ - -- Firstly, `import discord` imports the **discord.py** package. `from discord import app_commands` imports the `app_commands` module from the **discord.py** root module. `from discord.ext import commands` imports the commands extension. -- Further up, `class MySlashCog(commands.Cog)` is a class subclassing the `Cog` class. You can read more about this [here](https://discordpy.readthedocs.io/en/latest/ext/commands/cogs.html#ext-commands-cogs). -- `def __init__(self, bot: commands.Bot): self.bot = bot` is the constructor method of the class that is always run when the class is instantiated and that is why we pass in a **Bot** object whenever we create an instance of the cog class. -- Following up is the `@app_commands.command(name="ping", description="...")` decorator. This decorator basically functions the same as a `bot.tree.command` but since the cog currently does not have a **bot**, the `app_commands.command` decorator is used instead. The next two lines follow the same structure for Slash Commands with **self** added as the first parameter to the function as it is a method of a class. -- The next up lines are mostly the same. -- Talking about the first line inside the `setup_hook` is the `add_cog` method of the **Bot** class. And since **self** acts as the **instance** of the current class, we use **self** to use the `add_cog` method of the **Bot** class as we are inside a subclassed class of the **Bot** class. Then we pass in **self** to the `add_cog` method as the `__init__` function of the **MySlashCog** cog accepts a `Bot` object. -- After that we instantiate the `MySlashBot` class and run the bot using the **run** method which executes our setup_hook function and our commands get loaded and synced. The bot is now ready to use! - -# An Example to using groups with discord.py for Slash Commands! - -## An example with optional group! - -```python -import discord -from discord.ext import commands -from discord import app_commands - -class MySlashGroupCog(commands.Cog): - def __init__(self, bot: commands.Bot) -> None: - self.bot = bot - - #-------------------------------------------------------- - group = app_commands.Group(name="uwu", description="...") - #-------------------------------------------------------- - - @app_commands.command(name="ping", description="...") - async def _ping(self, interaction: discord.) -> None: - await interaction.response.send_message("pong!") - - @group.command(name="command", description="...") - async def _cmd(self, interaction: discord.Interaction) -> None: - await interaction.response.send_message("uwu") - -class MySlashBot(commands.Bot): - def __init__(self) -> None: - super().__init__(command_prefix="!", intents=discord.Intents.default()) - - async def setup_hook(self) -> None: - await self.add_cog(MySlashGroupCog(self)) - await self.tree.copy_global_to(discord.Object(id=123456789098765432)) - await self.tree.sync() - -bot = MySlashBot() - -bot.run("token") -``` - -__**EXPLANATION**__ -- The only difference used here is `group = app_commands.Group(name="uwu", description="...")` and `group.command`. `app_commands.Group` is used to initiate a group while `group.command` registers a command under a group. For example, the ping command can be run using **/ping** but this is not the case for group commands. They are registered with the format of `group_name command_name`. So here, the **command** command of the **uwu** group would be run using **/uwu command**. Note that only group commands can have a single space between them. - -## An example with a **Group** subclass! - -```python -import discord -from discord.ext import commands -from discord import app_commands - -class MySlashGroup(app_commands.Group, name="uwu"): - def __init__(self, bot: commands.Bot) -> None: - self.bot = bot - super().__init__() - - @app_commands.command(name="ping", description="...") - async def _ping(self, interaction: discord.) -> None: - await interaction.response.send_message("pong!") - - @app_commands.command(name="command", description="...") - async def _cmd(self, interaction: discord.Interaction) -> None: - await interaction.response.send_message("uwu") - -class MySlashBot(commands.Bot): - def __init__(self) -> None: - super().__init__(command_prefix="!", intents=discord.Intents.default()) - - async def setup_hook(self) -> None: - await self.add_cog(MySlashGroup(self)) - await self.tree.copy_global_to(discord.Object(id=123456789098765432)) - await self.tree.sync() - -bot = MySlashBot() - -bot.run("token") -``` - -__**EXPLANATION**__ -- The only difference here too is that the `MySlashGroup` class directly subclasses the **Group** class from discord.app_commands which automatically registers all the methods within the group class to be commands of that specific group. So now, the commands such as `ping` can be run using **/uwu ping** and `command` using **/uwu command**. - -# Some common methods and features used for Slash Commands. - -### A common function used for Slash Commands is the `describe` function. This is used to add descriptions to the arguments of a slash command. The command function can decorated with this function. It goes by the following syntax as shown below. - -```python -from discord.ext import commands -from discord import app_commands -import discord - -bot = commands.Bot(command_prefix=".", intents=discord.Intents.default()) -#sync the commands - -@bot.tree.command(name="echo", description="...") -@app_commands.describe(text="The text to send!", channel="The channel to send the message in!") -async def _echo(interaction: discord.Interaction, text: str, channel: discord.TextChannel=None): - channel = channel or interaction.channel - await channel.send(text) -``` - -### Another common issue that most people come across is the time duration of sending a message with `send_message`. This issue can be tackled by deferring the interaction response using the `defer` method of the `InteractionResponse` class. An example for fixing this issue is shown below. - -```python -import discord -from discord.ext import commands -import asyncio - -bot = commands.Bot(command_prefix="!", intents=discord.Intents.default()) -#sync the commands - -@bot.tree.command(name="time", description="...") -async def _time(interaction: discord.Interaction, time_to_wait: int): - # ------------------------------------------------------------- - await interaction.response.defer(ephemeral=True, thinking=True) - # ------------------------------------------------------------- - await interaction.edit_original_message(content=f"I will notify you after {time_to_wait} seconds have passed!") - await asyncio.sleep(time_to_wait) - await interaction.edit_original_message(content=f"{interaction.user.mention}, {time_to_wait} seconds have already passed!") -``` - -# Checking for Permissions and Roles! - -To add a permissions check to a command, the methods are imported through `discord.app_commands.checks`. To check for a member's permissions, the function can be decorated with the [discord.app_commands.checks.has_permissions](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=has_permissions#discord.app_commands.checks.has_permissions) method. An example to this as follows. - -```py -from discord import app_commands -from discord.ext import commands -import discord - -bot = commands.Bot(command_prefix="!", intents=discord.Intents.default()) -#sync commands - -@bot.tree.command(name="ping") -@app_commands.checks.has_permissions(manage_messages=True, manage_channels=True) #example permissions -async def _ping(interaction: discord.Interaction): - await interaction.response.send_message("pong!") - -``` - -If the check fails, it will raise a `MissingPermissions` error which can be handled within an app commands error handler! We will discuss making an error handler later in the Gist. All the permissions can be found [here](https://discordpy.readthedocs.io/en/latest/api.html?highlight=discord%20permissions#discord.Permissions). - -Other methods that you can decorate the commands with are - -- `bot_has_permissions` | This checks if the bot has the required permissions for executing the slash command. This raises a [BotMissingPermissions](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.BotMissingPermissions) exception. -- `has_role` | This checks if the slash command user has the required role or not. Only **ONE** role name or role ID can be passed to this. If the name is being passed, make sure to have the exact same name as the role name. This raises a [MissingRole](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.MissingRole) exception. -- To pass in several role names or role IDs, `has_any_role` can be used to decorate a command. This raises two exceptions -> [MissingAnyRole](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.MissingAnyRole) and [NoPrivateMessage](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=app_commands%20checks%20has_role#discord.app_commands.NoPrivateMessage) - - -# Adding cooldowns to Slash Commands! - -Slash Commands within discord.py can be applied cooldowns to in order to prevent spamming of the commands. This can be done through the `discord.app_commands.checks.cooldown` method which can be used to decorate a slash command function and register a cooldown to the function. This raises a [CommandOnCooldown](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=checks%20cooldown#discord.app_commands.CommandOnCooldown) exception if the command is currently on cooldown. -An example is as follows. - -```python -from discord.ext import commands -import discord - -class Bot(commands.Bot): - def __init__(self): - super().__init__(command_prefix="uwu", intents=discord.Intents.all()) - - async def setup_hook(self): - self.tree.copy_global_to(guild=discord.Object(id=12345678909876543)) - await self.tree.sync() - - -bot = Bot() - -@bot.tree.command(name="ping") -# ----------------------------------------- -@discord.app_commands.checks.cooldown(1, 30) -# ----------------------------------------- -async def ping(interaction: discord.Interaction): - await interaction.response.send_message("pong!") - -bot.run("token") -``` - -__**EXPLANATION**__ -- The first argument is the number of times this command can be invoked before the cooldown is triggered. -- The second argument it takes is the period of time in which the command can be run the specified number of times. -- The `CommandOnCooldown` exception can be handled using an error handler. We will discuss making an error handler for Slash Commands later in the Gist. - - -# Handling errors for Slash Commands! - -The Slash Commands exceptions can be handled by overwriting the `on_error` method of the `CommandTree`. The error handler takes two arguments. The first argument is the `Interaction` that took place when the error occurred and the second argument is the error that occurred when the Slash Commands was invoked. The error is an instance of [discord.app_commands.AppCommandError](https://discordpy.readthedocs.io/en/latest/interactions/api.html?highlight=appcommanderror#discord.app_commands.AppCommandError) which is a subclass of [DiscordException](https://discordpy.readthedocs.io/en/latest/api.html?highlight=discordexception#discord.DiscordException). -An example to creating an error handler for Slash Commands is as follows. - -```python -from discord.ext import commands -from discord import app_commands -import discord - -bot = commands.Bot(command_prefix="!", intents=discord.Intents.default()) -#sync commands - -@bot.tree.command(name="ping") -@app_commands.checks.cooldown(1, 30) -async def ping(interaction: discord.Interaction): - await interaction.response.send_message("pong!") - -async def on_tree_error(interaction: discord.Interaction, error: app_commands.AppCommandError): - if isinstance(error, app_commands.CommandOnCooldown): - return await interaction.response.send_message(f"Command is currently on cooldown! Try again in **{error.retry_after:.2f}** seconds!") - elif isinstance(error, ...): - ... - else: - raise error - -bot.tree.on_error = on_tree_error - -bot.run("token") -``` - -__**EXPLANATION**__ - -First we create a simple asynchronous function named `on_tree_error` here. To which the first two required arguments are passed, `Interaction` which is named as `interaction` here and `AppCommandError` which is named as `error` here. Then using simple functions and keywords, we make an error handler like above. Here we have used the `isinstance` function which takes in an object and a base class as the second argument, this function returns a bool value. The `raise error` is just for displaying unhandled errors, i.e. the ones which have not been handled manually. If this is **removed**, you will not be able to see any exceptions raised by Slash Commands and makes debugging the code harder. -After creating the error handler function, we set the function as the error handler for the Slash Commands. Here, `bot.tree.on_error = on_tree_error` overwrites the default `on_error` method of the **CommandTree** class with our custom error handler which has been named as `on_tree_error` here. - -### Creating an error handler for a specific error! - -```python -from discord.ext import commands -from discord import app_commands -import discord - -bot = commands.Bot(command_prefix="!", intents=discord.Intents.default()) -#sync commands - -@bot.tree.command(name="ping") -app_commands.checks.cooldown(1, 30) -async def ping(interaction: discord.Interaction): - await interaction.response.send_message("pong!") - -@ping.error -async def ping_error(interaction: discord.Interaction, error: app_commands.AppCommandError): - if isinstance(error, app_commands.CommandOnCooldown): - return await interaction.response.send_message(f"Command is currently on cooldown! Try again in **{error.retry_after:.2f}** seconds!") - elif isinstance(error, ...): - ... - else: - raise error - -bot.run("token") -``` - -__**EXPLANATION**__ - -Here the command name is simply used to access the `error` method to decorate a function which acts as the `on_error` but for a specific command. You should not need to call the `error` method manually. -- cgit v1.2.3 From 4844ab4b138d75cc830ccfd0d06464351a030a12 Mon Sep 17 00:00:00 2001 From: Xithrius Date: Sun, 20 Nov 2022 14:02:14 -0800 Subject: Partial requested reviews resolved --- .../apps/content/resources/guides/python-guides/app-commands.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/python-guides/app-commands.md b/pydis_site/apps/content/resources/guides/python-guides/app-commands.md index 1354a136..3afe342a 100644 --- a/pydis_site/apps/content/resources/guides/python-guides/app-commands.md +++ b/pydis_site/apps/content/resources/guides/python-guides/app-commands.md @@ -279,9 +279,9 @@ bot = commands.Bot(command_prefix="!", intents=discord.Intents.default()) @bot.tree.command(name="time", description="...") async def _time(interaction: discord.Interaction, time_to_wait: int): # ------------------------------------------------------------- - await interaction.response.defer(ephemeral=True, thinking=True) + await interaction.response.defer(ephemeral=True) # ------------------------------------------------------------- - await interaction.edit_original_message(content=f"I will notify you after {time_to_wait} seconds have passed!") + await interaction.edit_original_response(content=f"I will notify you after {time_to_wait} seconds have passed!") await asyncio.sleep(time_to_wait) await interaction.edit_original_message(content=f"{interaction.user.mention}, {time_to_wait} seconds have already passed!") ``` @@ -396,7 +396,7 @@ bot = commands.Bot(command_prefix="!", intents=discord.Intents.default()) #sync commands @bot.tree.command(name="ping") -app_commands.checks.cooldown(1, 30) +@app_commands.checks.cooldown(1, 30) async def ping(interaction: discord.Interaction): await interaction.response.send_message("pong!") -- cgit v1.2.3 From ad7410b02bac80d4d3f4171988be0ba38820a7e2 Mon Sep 17 00:00:00 2001 From: Xithrius Date: Sun, 11 Dec 2022 02:39:35 -0800 Subject: Reviews fully resolved, but unsure about defer --- pydis_site/apps/content/resources/guides/python-guides/app-commands.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/python-guides/app-commands.md b/pydis_site/apps/content/resources/guides/python-guides/app-commands.md index 3afe342a..713cd650 100644 --- a/pydis_site/apps/content/resources/guides/python-guides/app-commands.md +++ b/pydis_site/apps/content/resources/guides/python-guides/app-commands.md @@ -29,6 +29,7 @@ Slash commands in discord.py are held by a container, [CommandTree](https://disc One new feature added in discord.py v2 is `setup_hook`. `setup_hook` is a special asynchronous method of the Client and Bot classes which can be overwritten to perform numerous tasks. This method is safe to use as it is always triggered before any events are dispatched, i.e. this method is triggered before the *IDENTIFY* payload is sent to the discord gateway. Note that methods of the Bot class such as `change_presence` will not work in setup_hook as the current application does not have an active connection to the gateway at this point. +A full list of commands you can't use in setup_hook can be found [here](https://discord.com/developers/docs/topics/gateway-events#send-events). __**THE FOLLOWING ARE EXAMPLES OF HOW A `SETUP_HOOK` FUNCTION CAN BE DEFINED**__ @@ -283,7 +284,7 @@ async def _time(interaction: discord.Interaction, time_to_wait: int): # ------------------------------------------------------------- await interaction.edit_original_response(content=f"I will notify you after {time_to_wait} seconds have passed!") await asyncio.sleep(time_to_wait) - await interaction.edit_original_message(content=f"{interaction.user.mention}, {time_to_wait} seconds have already passed!") + await interaction.edit_original_response(content=f"{interaction.user.mention}, {time_to_wait} seconds have already passed!") ``` # Checking for Permissions and Roles! -- cgit v1.2.3 From 2889b53d33bd5f4b38e5e28746ee81c252d64be8 Mon Sep 17 00:00:00 2001 From: Xithrius Date: Mon, 12 Dec 2022 02:54:10 -0800 Subject: Confirmation with the reader on when to set status --- .../guides/python-guides/setting-different-statuses-on-your-bot.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/python-guides/setting-different-statuses-on-your-bot.md b/pydis_site/apps/content/resources/guides/python-guides/setting-different-statuses-on-your-bot.md index b44c55e1..45c7b37c 100644 --- a/pydis_site/apps/content/resources/guides/python-guides/setting-different-statuses-on-your-bot.md +++ b/pydis_site/apps/content/resources/guides/python-guides/setting-different-statuses-on-your-bot.md @@ -5,11 +5,13 @@ description: How to personalize your Discord bot status You've probably seen a bot or two have a status message under their username in the member bar set to something such as `Playing Commands: .help`. -This guide shows how to set such a presence, so your bot can have one as well. +This guide shows how to set such a status, so your bot can have one as well. **Please note:** -If you want to change the bot status, it is suggested to not do so during the on_ready event, since it would be called many times and making an API call on that event has a chance to disconnect the bot. +If you want to change the bot status, it is suggested to not do so during the `on_ready` event, since it would be called many times and making an API call on that event has a chance to disconnect the bot. + +The status should not have a problem being set during runtime with `change_presence`, in the examples shown below. Instead, set the desired status using the activity / status kwarg of commands.Bot, for example: ```python -- cgit v1.2.3 From 318a2980035e60362035693fadb8bfa278ae9c29 Mon Sep 17 00:00:00 2001 From: Amrou Bellalouna Date: Thu, 29 Dec 2022 00:51:08 +0100 Subject: Update help channels guide (#814) Co-authored-by: Xithrius <15021300+Xithrius@users.noreply.github.com> --- .../guides/pydis-guides/help-channel-guide.md | 79 ++++++++++++--------- .../guides/pydis-guides/helping-others.md | 4 +- .../content/help_channels/available_channels.png | Bin 6556 -> 0 bytes .../content/help_channels/available_message.png | Bin 89386 -> 0 bytes .../content/help_channels/claimed_channel.png | Bin 26100 -> 0 bytes .../content/help_channels/dormant_channels.png | Bin 22386 -> 0 bytes .../content/help_channels/help-system-category.png | Bin 0 -> 7425 bytes .../content/help_channels/new-post-button.png | Bin 0 -> 15804 bytes .../images/content/help_channels/new-post-form.png | Bin 0 -> 35364 bytes .../help_channels/newly-created-thread-example.png | Bin 0 -> 149793 bytes .../content/help_channels/occupied_channels.png | Bin 10950 -> 0 bytes .../content/help_channels/question-example.png | Bin 0 -> 45436 bytes 12 files changed, 47 insertions(+), 36 deletions(-) delete mode 100644 pydis_site/static/images/content/help_channels/available_channels.png delete mode 100644 pydis_site/static/images/content/help_channels/available_message.png delete mode 100644 pydis_site/static/images/content/help_channels/claimed_channel.png delete mode 100644 pydis_site/static/images/content/help_channels/dormant_channels.png create mode 100644 pydis_site/static/images/content/help_channels/help-system-category.png create mode 100644 pydis_site/static/images/content/help_channels/new-post-button.png create mode 100644 pydis_site/static/images/content/help_channels/new-post-form.png create mode 100644 pydis_site/static/images/content/help_channels/newly-created-thread-example.png delete mode 100644 pydis_site/static/images/content/help_channels/occupied_channels.png create mode 100644 pydis_site/static/images/content/help_channels/question-example.png (limited to 'pydis_site') diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/help-channel-guide.md b/pydis_site/apps/content/resources/guides/pydis-guides/help-channel-guide.md index 2be845d3..bef2df9b 100644 --- a/pydis_site/apps/content/resources/guides/pydis-guides/help-channel-guide.md +++ b/pydis_site/apps/content/resources/guides/pydis-guides/help-channel-guide.md @@ -9,7 +9,7 @@ relevant_links: toc: 3 --- -At Python Discord we have two different kinds of help channels: **topical help channels** and **general help channels**. +At Python Discord we have two different kinds of help channels: **topical help channels** and **help forum posts**. # Topical Help Channels @@ -24,71 +24,82 @@ For example, `#data-science-and-ai` covers scientific Python, statistics, and ma Each channel on the server has a channel description which briefly describes the topics covered by that channel. If you're not sure where to post, feel free to ask us which channel is appropriate in `#community-meta`. -# General Help Channels +# Help Forum Posts -General help channels can be used for all Python-related help, and have the advantage of attracting a more diverse spectrum of helpers. There is also the added benefit of receiving individual focus and attention on your question. These channels are a great choice for generic Python help, but can be used for domain-specific Python help as well. +Help forum posts can be used for all Python-related help, and have the advantage of attracting a more diverse spectrum of helpers. There is also the added benefit of receiving individual focus and attention on your question. These posts are a great choice for generic Python help, but can be used for domain-specific Python help as well. -## How to Claim a Channel +## How to Create A New Post -There are always three help channels waiting to be claimed in the **Available Help Channels** category. +There are 4 easy needed steps to make this happen -![Available help channels](/static/images/content/help_channels/available_channels.png) -*The Available Help Channels category is always at the top of the server's channel list.* +1. Navigate to the **Python Help System** category.
+![Python help system category](/static/images/content/help_channels/help-system-category.png) +2. Open the **python-help** forum channel. +3. Click on the **New Post** button in the top-right corner.
+![New post button](/static/images/content/help_channels/new-post-button.png) +4. Choose a brief title that best describes your issue, along with a message explaining it more in details, and **post** it. +Note that you can also choose one or more tags which can help attract experts of that tag easily.
+![New post form](/static/images/content/help_channels/new-post-form.png) -![Available message](/static/images/content/help_channels/available_message.png) -*This message indicates that a channel is available.* +Be sure to [ask questions with enough information](../asking-good-questions) in order to give yourself the best chances of getting help! -In order to claim one, simply ask your question in one of the available channels. Be sure to [ask questions with enough information](../asking-good-questions) in order to give yourself the best chances of getting help! +At this point you will have the **Help Cooldown** role which will remain on your profile until you close your newly created post. This ensures that users can only have one post at any given time, giving everyone a chance to have their question seen. -![Channel claimed embed](/static/images/content/help_channels/claimed_channel.png) -*This messages indicates that you've claimed the channel.* +# Frequently Asked Questions -At this point you will have the **Help Cooldown** role which will remain on your profile until you close your help channel. This ensures that users can claim only one help channel at any given time, giving everyone a chance to have their question seen. +### I created a new help post, what happens now? +Once you click on `Post`, these events take place:
+1. A new channel will be created for you, and you'll have an `OP` next to you username, which tells people you're the `Original Poster`, or in other words, the owner of the help topic in that channel.
+2. Your original question/message will always be the first one in that channel.
+3. Our Python bot will send a message reminding you of what you should include in your question/message in case you could have missed anything.
+4. People will be able to jump on that channel, and you can have a discussion with anyone who's volunteering to help you by asking as many followup questions as you want.
-# Frequently Asked Questions +#### Example +Suppose we're trying to find the minimum value in a list of integers. +Once we've chosen our title and message content, we are ready to make a new post.

+![Filled form example](/static/images/content/help_channels/question-example.png)

+Note how we've checked the **Algos & data structs** tag here, whose circumference is highlighted in blue, since this is a question about an algorithm to find the minimum.
+This will greatly help others pinpoint where they can help you best based on a combination of your title and tag from a first glance.

+Once you click on post, a new channel is created, and you can see the original message on top along with the `OP` tag next to the poster's avatar.
+You will also see the message that our Python bot sends instantly right after yours.

+![Newly created thread example](/static/images/content/help_channels/newly-created-thread-example.png) -### How long does my help channel stay active? +### How long does my help post stay active? -The channel remains open for **30 minutes** after your last message, or 10 minutes after the last message sent by another user (whichever time comes later). +The post remains open for **30 minutes** after your last message, or 10 minutes after the last message sent by another user (whichever time comes later). ![Channel dormant message](/static/images/content/help_channels/dormant_message.png) -*You'll see this message in your channel once it goes dormant.* +*You'll see this message in your post once it goes dormant.* + ### No one answered my question. How come? -The server has users active all over the world and all hours of the day, but some time periods are less active than others. It's also possible that the users that read your question didn't have the knowledge required to help you. If no one responded, feel free to claim another help channel a little later, or try an appropriate topical channel. +The server has users active all over the world and all hours of the day, but some time periods are less active than others. It's also possible that the users that read your question didn't have the knowledge required to help you. If no one responded, feel free to open another post a little later, or try an appropriate topical channel. If you feel like your question is continuously being overlooked, read our guide on [asking good questions](../asking-good-questions) to increase your chances of getting a response. ### My question was answered. What do I do? -Go ahead use the `!close` command if you've satisfactorily solved your problem. You will only be able to run this command in your own help channel, and no one (outside of staff) will be able to close your channel for you. +Go ahead and use one of the `!close` or `!solved` commands if you've satisfactorily solved your problem. You will only be able to run this command in your own post, and no one (outside of staff) will be able to close your post for you. -Closing your help channel once you are finished leads to less occupied channels, which means more attention can be given to other users that still need help. +Closing your post once you are finished leads to less occupied ones, which means more attention can be given to other users that still need help. ### Can only Helpers answer help questions? -Definitely not! We encourage all members of the community to participate in giving help. If you'd like to help answer some questions, head over to the **Occupied Help Channels** or **Topical Chat/Help** categories. +Definitely not! We encourage all members of the community to participate in giving help. If you'd like to help answer some questions, you can either browse all posts in the **python-help** forum channel or head over to the **Topical Chat/Help** category. Before jumping in, please read our guide on [helping others](../helping-others) which explains our expectations for the culture and quailty of help that we aim for on the server. -Tip: run the `!helpdm on` command in `#bot-commands` to get notified via DM with jumplinks to help channels you're participating in. - -### What are the available, occupied, and dormant categories? - -The three help channels under **Available Help Channels** are free for anyone to claim. Claimed channels are then moved to **Occupied Help Channels**. Once they close, they are moved to the **Python Help: Dormant** category until they are needed again for **Available Help Channels**. +Tip: run the `!helpdm on` command in the `#bot-commands` channel to get notified via DM with jumplinks to help posts you're participating in. ### Can I save my help session for future reference? -Yes! Because the help channels are continuously cycled in and out without being deleted, this means you can always refer to a previous help session if you found one particularly helpful. +Yes! Because the help posts are only closed without being deleted, this means you can always refer to a previous help session if you found one particularly helpful. Tip: reply to a message and run the `.bm` command to get bookmarks sent to you via DM for future reference. -### I lost my help channel! +### I lost my help post! -No need to panic. Your channel was probably just closed due to inactivity. -All the dormant help channels are still available at the bottom of the channel list, in the **Python Help: Dormant** category, and also through search. -If you're not sure what the name of your help channel was, you can easily find it by using the Discord Search feature. +No need to panic. Your post was probably just closed due to inactivity. +All the dormant help posts are still available at the bottom of the **python-help** forum channel and also through search in the **Python Help System** category. +If you're not sure what the title of your help post was, you can easily find it by using the Discord Search feature. Try searching for `from:` to find the last messages sent by yourself, and from there you will be able to jump directly into the channel by pressing the Jump button on your message. - -![Dormant help channels](/static/images/content/help_channels/dormant_channels.png) -*The dormant help channels can be found at the bottom of the channel list.* diff --git a/pydis_site/apps/content/resources/guides/pydis-guides/helping-others.md b/pydis_site/apps/content/resources/guides/pydis-guides/helping-others.md index a7f1ce1d..9f0d947f 100644 --- a/pydis_site/apps/content/resources/guides/pydis-guides/helping-others.md +++ b/pydis_site/apps/content/resources/guides/pydis-guides/helping-others.md @@ -9,7 +9,7 @@ relevant_links: toc: 2 --- -Python Discord has a lot of people asking questions, be it in the help channels, topical channels, or any other part of the server. +Python Discord has a lot of people asking questions, be it in the help forum, topical channels, or any other part of the server. Therefore, you might sometimes want to give people the answers you have in mind. But you might not be sure how best to approach the issue, or maybe you'd like to see how others handle it. This article aims to present a few of the general principles which guide the staff on a day-to-day basis on the server. @@ -64,7 +64,7 @@ At other times, it might not be as obvious, and it might be a good idea to kindl The path is often more important than the answer. Your goal should primarily be to allow the helpee to apply, at least to a degree, the concepts you introduce in your answer. Otherwise, they might keep struggling with the same problem over and over again. -That means that simply showing your answer might close the help channel for the moment, but won't be very helpful in the long-term. +That means that simply showing your answer might close the help post for the moment, but won't be very helpful in the long-term. A common approach is to walk the helpee through to an answer: diff --git a/pydis_site/static/images/content/help_channels/available_channels.png b/pydis_site/static/images/content/help_channels/available_channels.png deleted file mode 100644 index 0b9cfd03..00000000 Binary files a/pydis_site/static/images/content/help_channels/available_channels.png and /dev/null differ diff --git a/pydis_site/static/images/content/help_channels/available_message.png b/pydis_site/static/images/content/help_channels/available_message.png deleted file mode 100644 index 09668c9b..00000000 Binary files a/pydis_site/static/images/content/help_channels/available_message.png and /dev/null differ diff --git a/pydis_site/static/images/content/help_channels/claimed_channel.png b/pydis_site/static/images/content/help_channels/claimed_channel.png deleted file mode 100644 index 777e31ea..00000000 Binary files a/pydis_site/static/images/content/help_channels/claimed_channel.png and /dev/null differ diff --git a/pydis_site/static/images/content/help_channels/dormant_channels.png b/pydis_site/static/images/content/help_channels/dormant_channels.png deleted file mode 100644 index 7c9ba61e..00000000 Binary files a/pydis_site/static/images/content/help_channels/dormant_channels.png and /dev/null differ diff --git a/pydis_site/static/images/content/help_channels/help-system-category.png b/pydis_site/static/images/content/help_channels/help-system-category.png new file mode 100644 index 00000000..bea5a92c Binary files /dev/null and b/pydis_site/static/images/content/help_channels/help-system-category.png differ diff --git a/pydis_site/static/images/content/help_channels/new-post-button.png b/pydis_site/static/images/content/help_channels/new-post-button.png new file mode 100644 index 00000000..4ceabf0f Binary files /dev/null and b/pydis_site/static/images/content/help_channels/new-post-button.png differ diff --git a/pydis_site/static/images/content/help_channels/new-post-form.png b/pydis_site/static/images/content/help_channels/new-post-form.png new file mode 100644 index 00000000..3e90bf7d Binary files /dev/null and b/pydis_site/static/images/content/help_channels/new-post-form.png differ diff --git a/pydis_site/static/images/content/help_channels/newly-created-thread-example.png b/pydis_site/static/images/content/help_channels/newly-created-thread-example.png new file mode 100644 index 00000000..d7b1eed4 Binary files /dev/null and b/pydis_site/static/images/content/help_channels/newly-created-thread-example.png differ diff --git a/pydis_site/static/images/content/help_channels/occupied_channels.png b/pydis_site/static/images/content/help_channels/occupied_channels.png deleted file mode 100644 index 6ccb4ed6..00000000 Binary files a/pydis_site/static/images/content/help_channels/occupied_channels.png and /dev/null differ diff --git a/pydis_site/static/images/content/help_channels/question-example.png b/pydis_site/static/images/content/help_channels/question-example.png new file mode 100644 index 00000000..da181351 Binary files /dev/null and b/pydis_site/static/images/content/help_channels/question-example.png differ -- cgit v1.2.3