Good practices
Running code when a cog is loaded
Most people are used to running everything in __init__
but that doesn't allow running async code. In this case you can
overwrite the special cog_load
method.
class MyCog(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
async def cog_load(self):
self.data = await bot.fetch_database_data()
@commands.slash_command()
async def command(self, interaction: disnake.ApplicationCommandInteraction, user: disnake.User):
await interaction.response.send_message(self.data[user.id])
Reloading your bot
One of the lesser known disnake features is the reload
kwarg for your bot. It reloads extensions every time they are
edited; allowing you to test your code in real-time. This is especially useful if startup times for your bot are very
slow, since only one extension will be reloaded at a time.
from disnake.ext import commands
bot = commands.Bot(..., reload=True)
This should be used purely for debugging. Please do not use this in production.
Converting arguments in commands
discord.py used to have Converter
classes to convert arguments if they are provided. These were however very hard to
use with type-checkers because the type of the parameter is never actually the converter itself.
Disnake aims to eliminate this issue by only allowing conversion of builtin types like disnake.Member
,
disnake.Emoji
, etc. If you ever want to have your own converter you have to use the converter
argument in Param
.
- disnake
- discord.py
async def convert_data(inter: disnake.ApplicationCommandInteraction, arg: str):
parts = arg.split(",")
return {"a": parts[0], "b": int(parts[1]), "c": parts[2].lower()}
@commands.slash_command()
async def command(
self,
inter: disnake.ApplicationCommandInteraction,
data: Dict[str, Any] = commands.Param(converter=convert_data),
):
...
class DataConverter(commands.Converter):
async def convert(self, ctx: commands.Context, arg: str):
parts = arg.split(",")
return {"a": parts[0], "b": int(parts[1]), "c": parts[2].lower()}
@commands.command()
async def command(self, ctx: commands.Context, data: DataConverter):
...
If you absolutely want to be able to use classes you may pass in a class method. Alternatively, set a method of the
class to be the converter using converter_method
.
- classmethod converter
- converter method
from dataclasses import dataclass
@dataclass
class Data:
a: str
b: int
@classmethod
async def from_option(cls, inter: disnake.CommandInteraction, arg: str):
a, b = arg.split(",")
return cls(a, int(b))
@commands.slash_command()
async def command(
self,
inter: disnake.CommandInteraction,
data: Data = commands.Param(converter=Data.from_option),
):
...
from dataclasses import dataclass
@dataclass
class Data:
a: str
b: int
@commands.converter_method
async def from_option(cls, inter: disnake.CommandInteraction, arg: str):
a, b = arg.split(",")
return cls(a, int(b))
@commands.slash_command()
async def command(
self,
inter: disnake.CommandInteraction,
data: Data,
):
...
Context command targets
Instead of using inter.target
you should be using a parameter of your command.
@commands.user_command()
async def command(self, inter: disnake.ApplicationCommandInteraction, user: disnake.User):
await inter.response.send_message(f"Used on {user.mention}")
Slash command descriptions
You may use docstrings for command and option descriptions. Everything before Parameters
is the command description.
Everything after Parameters
are the option descriptions.
@commands.slash_command()
async def command(
self,
inter: disnake.ApplicationCommandInteraction,
category: str,
item: str,
details: bool,
):
"""Show item info
Parameters
----------
category: The category to search
item: The item to display
details: Whether to get the details of this time
"""
Guild-only commands
While disnake does provide a @commands.guild_only()
decorator, it still makes you handle guild
being optional in
case you're using linters. To solve this you should be using GuildCommandInteraction
.
# before
@commands.slash_command()
@commands.guild_only()
async def command(inter: disnake.ApplicationCommandInteraction):
assert inter.guild is not None
await inter.send(inter.guild.name)
# after
@commands.slash_command()
async def command(inter: disnake.GuildCommandInteraction):
await inter.send(inter.guild.name)