A modern, easy-to-use, feature-rich async-ready API wrapper for Discord written in Python
—
Modern Discord application commands including slash commands, user context menu commands, and message context menu commands. These provide native Discord UI integration with parameter validation, autocomplete, and rich interaction handling.
Interactive commands that appear in Discord's command picker with built-in parameter validation and autocomplete support.
def slash_command(
name: Optional[str] = None,
*,
description: Optional[str] = None,
guild_ids: Optional[Sequence[int]] = None,
dm_permission: Optional[bool] = None,
default_member_permissions: Optional[Union[Permissions, int]] = None,
nsfw: Optional[bool] = None,
auto_sync: Optional[bool] = None,
extras: Optional[Dict[str, Any]] = None
):
"""
Decorator for creating slash commands.
Parameters:
- name: Command name (defaults to function name)
- description: Command description shown in Discord
- guild_ids: Specific guilds to register command in (for testing)
- dm_permission: Whether command can be used in DMs
- default_member_permissions: Required permissions to use command
- nsfw: Whether command is NSFW
- auto_sync: Whether to auto-sync this command
- extras: Additional metadata
Returns:
Decorator function
"""
class SlashCommand:
def __init__(self): ...
name: str
description: str
guild_ids: Optional[List[int]]
dm_permission: Optional[bool]
default_member_permissions: Optional[Permissions]
nsfw: Optional[bool]
options: List[Option]
qualified_name: str
async def __call__(self, interaction: ApplicationCommandInteraction, *args, **kwargs) -> Any:
"""Execute the slash command."""
class Option:
def __init__(
self,
name: str,
description: str,
type: OptionType,
*,
required: bool = True,
choices: Optional[List[OptionChoice]] = None,
autocomplete: Optional[bool] = None,
channel_types: Optional[List[ChannelType]] = None,
min_value: Optional[Union[int, float]] = None,
max_value: Optional[Union[int, float]] = None,
min_length: Optional[int] = None,
max_length: Optional[int] = None
):
"""
Slash command option/parameter definition.
Parameters:
- name: Option name
- description: Option description
- type: Option type (string, integer, etc.)
- required: Whether option is required
- choices: Predefined choices for the option
- autocomplete: Whether option supports autocomplete
- channel_types: Allowed channel types (for channel options)
- min_value: Minimum value (for number options)
- max_value: Maximum value (for number options)
- min_length: Minimum string length
- max_length: Maximum string length
"""
class Param:
def __init__(
self,
default: Any = ...,
*,
name: Optional[str] = None,
description: Optional[str] = None,
choices: Optional[List[Union[str, int, float, OptionChoice]]] = None,
converter: Optional[Union[Converter, Callable[[str], Any]]] = None,
autocomplete: Optional[Union[bool, Callable[..., Awaitable[List[OptionChoice]]]]] = None,
channel_types: Optional[List[ChannelType]] = None,
min_value: Optional[Union[int, float]] = None,
max_value: Optional[Union[int, float]] = None,
min_length: Optional[int] = None,
max_length: Optional[int] = None,
large: Optional[bool] = None
):
"""
Parameter descriptor for advanced slash command parameters.
Parameters:
- default: Default value (use ... for required parameters)
- name: Parameter name override
- description: Parameter description
- choices: Predefined choices
- converter: Custom converter function
- autocomplete: Autocomplete function or boolean
- channel_types: Allowed channel types
- min_value: Minimum numeric value
- max_value: Maximum numeric value
- min_length: Minimum string length
- max_length: Maximum string length
- large: Whether to use large number type
"""Right-click context menu commands that operate on users and members.
def user_command(
name: Optional[str] = None,
*,
guild_ids: Optional[Sequence[int]] = None,
dm_permission: Optional[bool] = None,
default_member_permissions: Optional[Union[Permissions, int]] = None,
nsfw: Optional[bool] = None,
auto_sync: Optional[bool] = None,
extras: Optional[Dict[str, Any]] = None
):
"""
Decorator for creating user context menu commands.
Parameters:
- name: Command name (defaults to function name)
- guild_ids: Specific guilds to register command in
- dm_permission: Whether command can be used in DMs
- default_member_permissions: Required permissions
- nsfw: Whether command is NSFW
- auto_sync: Whether to auto-sync this command
- extras: Additional metadata
Returns:
Decorator function
"""
class UserCommand:
def __init__(self): ...
name: str
guild_ids: Optional[List[int]]
dm_permission: Optional[bool]
default_member_permissions: Optional[Permissions]
nsfw: Optional[bool]
async def __call__(self, interaction: UserCommandInteraction, user: Union[User, Member]) -> Any:
"""Execute the user command."""Right-click context menu commands that operate on messages.
def message_command(
name: Optional[str] = None,
*,
guild_ids: Optional[Sequence[int]] = None,
dm_permission: Optional[bool] = None,
default_member_permissions: Optional[Union[Permissions, int]] = None,
nsfw: Optional[bool] = None,
auto_sync: Optional[bool] = None,
extras: Optional[Dict[str, Any]] = None
):
"""
Decorator for creating message context menu commands.
Parameters:
- name: Command name (defaults to function name)
- guild_ids: Specific guilds to register command in
- dm_permission: Whether command can be used in DMs
- default_member_permissions: Required permissions
- nsfw: Whether command is NSFW
- auto_sync: Whether to auto-sync this command
- extras: Additional metadata
Returns:
Decorator function
"""
class MessageCommand:
def __init__(self): ...
name: str
guild_ids: Optional[List[int]]
dm_permission: Optional[bool]
default_member_permissions: Optional[Permissions]
nsfw: Optional[bool]
async def __call__(self, interaction: MessageCommandInteraction, message: Message) -> Any:
"""Execute the message command."""Interaction objects received when application commands are executed.
class ApplicationCommandInteraction:
def __init__(self): ...
id: int
type: InteractionType
data: ApplicationCommandInteractionData
guild: Optional[Guild]
guild_id: Optional[int]
channel: Optional[Union[GuildChannel, PartialMessageable]]
channel_id: Optional[int]
user: Union[User, Member]
token: str
version: int
message: Optional[Message]
locale: Optional[Locale]
guild_locale: Optional[Locale]
created_at: datetime
expires_at: datetime
@property
def response(self) -> InteractionResponse:
"""Interaction response handler."""
@property
def followup(self) -> Webhook:
"""Webhook for followup messages."""
@property
def author(self) -> Union[User, Member]:
"""Alias for user."""
class ApplicationCommandInteractionData:
def __init__(self): ...
id: int
name: str
type: ApplicationCommandType
resolved: Optional[ApplicationCommandInteractionDataResolved]
options: List[ApplicationCommandInteractionDataOption]
guild_id: Optional[int]
target_id: Optional[int]
class ApplicationCommandInteractionDataOption:
def __init__(self): ...
name: str
type: OptionType
value: Optional[Union[str, int, float, bool]]
options: List[ApplicationCommandInteractionDataOption]
focused: Optional[bool]
class InteractionResponse:
def __init__(self): ...
async def send_message(
self,
content: Optional[str] = None,
*,
embed: Optional[Embed] = None,
embeds: Optional[List[Embed]] = None,
file: Optional[File] = None,
files: Optional[List[File]] = None,
view: Optional[View] = None,
components: Optional[Union[ActionRow, List[ActionRow], List[List[Component]], List[Component]]] = None,
tts: bool = False,
ephemeral: bool = False,
allowed_mentions: Optional[AllowedMentions] = None,
suppress_embeds: bool = False,
flags: Optional[MessageFlags] = None,
delete_after: Optional[float] = None
) -> None:
"""
Send an interaction response message.
Parameters:
- content: Message content
- embed: Single embed
- embeds: List of embeds
- file: Single file
- files: List of files
- view: UI components view
- components: Raw components
- tts: Text-to-speech
- ephemeral: Whether message is ephemeral (only visible to user)
- allowed_mentions: Mention controls
- suppress_embeds: Suppress embeds
- flags: Message flags
- delete_after: Auto-delete timer
"""
async def defer(self, *, ephemeral: bool = False, with_message: bool = False) -> None:
"""
Defer the interaction response.
Parameters:
- ephemeral: Whether deferred response should be ephemeral
- with_message: Whether to show "Bot is thinking..." message
"""
async def edit_message(
self,
content: Optional[str] = None,
*,
embed: Optional[Embed] = None,
embeds: Optional[List[Embed]] = None,
file: Optional[File] = None,
files: Optional[List[File]] = None,
attachments: Optional[List[Union[Attachment, File]]] = None,
view: Optional[View] = None,
components: Optional[Union[ActionRow, List[ActionRow], List[List[Component]], List[Component]]] = None,
allowed_mentions: Optional[AllowedMentions] = None,
delete_after: Optional[float] = None
) -> None:
"""
Edit the original interaction response message.
Parameters:
- content: New message content
- embed: Single embed
- embeds: List of embeds
- file: Single file
- files: List of files
- attachments: List of attachments/files
- view: UI components view
- components: Raw components
- allowed_mentions: Mention controls
- delete_after: Auto-delete timer
"""
async def send_modal(self, modal: Modal) -> None:
"""
Send a modal dialog response.
Parameters:
- modal: Modal to display
"""
@property
def is_done(self) -> bool:
"""Whether the interaction has been responded to."""Dynamic option value suggestions for slash command parameters.
async def autocomplete_function(
interaction: ApplicationCommandInteraction,
current: str
) -> List[OptionChoice]:
"""
Autocomplete function signature.
Parameters:
- interaction: Command interaction
- current: Current user input
Returns:
List of suggested choices (max 25)
"""
class OptionChoice:
def __init__(
self,
name: str,
value: Union[str, int, float],
*,
name_localizations: Optional[Dict[Locale, str]] = None
):
"""
Option choice for slash command parameters.
Parameters:
- name: Choice display name
- value: Choice value
- name_localizations: Localized names
"""
name: str
value: Union[str, int, float]
name_localizations: Optional[Dict[Locale, str]]import disnake
from disnake.ext import commands
bot = commands.InteractionBot()
@bot.slash_command(description="Get bot latency")
async def ping(inter: disnake.ApplicationCommandInteraction):
latency = round(bot.latency * 1000)
await inter.response.send_message(f"Pong! {latency}ms")
@bot.slash_command(description="Say something")
async def say(
inter: disnake.ApplicationCommandInteraction,
message: str = disnake.Param(description="Message to say"),
channel: disnake.TextChannel = disnake.Param(default=None, description="Channel to send to")
):
target = channel or inter.channel
await target.send(message)
await inter.response.send_message(f"Message sent to {target.mention}!", ephemeral=True)@bot.slash_command(description="User information")
async def userinfo(
inter: disnake.ApplicationCommandInteraction,
user: disnake.Member = disnake.Param(description="User to get info about"),
show_avatar: bool = disnake.Param(default=False, description="Show user avatar")
):
embed = disnake.Embed(title=f"Info for {user.display_name}")
embed.add_field(name="Joined", value=user.joined_at.strftime("%Y-%m-%d"))
embed.add_field(name="Roles", value=len(user.roles))
if show_avatar and user.avatar:
embed.set_thumbnail(url=user.avatar.url)
await inter.response.send_message(embed=embed)
@bot.slash_command(description="Set slowmode")
async def slowmode(
inter: disnake.ApplicationCommandInteraction,
seconds: int = disnake.Param(
description="Slowmode delay in seconds",
min_value=0,
max_value=21600
)
):
await inter.channel.edit(slowmode_delay=seconds)
await inter.response.send_message(f"Slowmode set to {seconds} seconds")@bot.slash_command_group(description="Admin commands")
async def admin(inter: disnake.ApplicationCommandInteraction):
pass
@admin.sub_command(description="Ban a user")
async def ban(
inter: disnake.ApplicationCommandInteraction,
user: disnake.Member,
reason: str = disnake.Param(default="No reason provided")
):
await user.ban(reason=reason)
await inter.response.send_message(f"Banned {user}", ephemeral=True)
@admin.sub_command(description="Kick a user")
async def kick(
inter: disnake.ApplicationCommandInteraction,
user: disnake.Member,
reason: str = disnake.Param(default="No reason provided")
):
await user.kick(reason=reason)
await inter.response.send_message(f"Kicked {user}", ephemeral=True)@bot.user_command(name="User Info")
async def user_info_context(inter: disnake.UserCommandInteraction, user: disnake.Member):
embed = disnake.Embed(title=f"Info for {user.display_name}")
embed.add_field(name="ID", value=user.id)
embed.add_field(name="Joined", value=user.joined_at.strftime("%Y-%m-%d"))
embed.set_thumbnail(url=user.display_avatar.url)
await inter.response.send_message(embed=embed, ephemeral=True)
@bot.message_command(name="Pin Message")
async def pin_message_context(inter: disnake.MessageCommandInteraction, message: disnake.Message):
try:
await message.pin()
await inter.response.send_message("Message pinned!", ephemeral=True)
except disnake.HTTPException:
await inter.response.send_message("Failed to pin message.", ephemeral=True)# Static autocomplete choices
@bot.slash_command(description="Get information about a programming language")
async def language(
inter: disnake.ApplicationCommandInteraction,
lang: str = disnake.Param(
description="Programming language",
choices=["Python", "JavaScript", "Java", "C++", "Go", "Rust"]
)
):
await inter.response.send_message(f"You chose {lang}!")
# Dynamic autocomplete
async def tag_autocomplete(inter: disnake.ApplicationCommandInteraction, current: str):
tags = ["bug", "feature", "question", "documentation", "help", "discussion"]
choices = [
disnake.OptionChoice(name=tag, value=tag)
for tag in tags if current.lower() in tag.lower()
]
return choices[:25] # Discord limit
@bot.slash_command(description="Search by tag")
async def search(
inter: disnake.ApplicationCommandInteraction,
tag: str = disnake.Param(description="Tag to search", autocomplete=tag_autocomplete)
):
await inter.response.send_message(f"Searching for tag: {tag}")@bot.slash_command(description="Process large data")
async def process(inter: disnake.ApplicationCommandInteraction):
# Defer response for long-running operations
await inter.response.defer()
# Simulate processing
await asyncio.sleep(5)
# Send followup message
await inter.followup.send("Processing complete!")
@bot.slash_command(description="Get user avatar")
async def avatar(
inter: disnake.ApplicationCommandInteraction,
user: disnake.Member = disnake.Param(description="User to get avatar for")
):
# Defer with ephemeral response
await inter.response.defer(ephemeral=True)
# Fetch high-res avatar
avatar_url = user.display_avatar.with_size(1024).url
embed = disnake.Embed(title=f"{user.display_name}'s Avatar")
embed.set_image(url=avatar_url)
# Edit the deferred response
await inter.edit_original_response(embed=embed)@bot.slash_command(description="Divide two numbers")
async def divide(
inter: disnake.ApplicationCommandInteraction,
a: float = disnake.Param(description="First number"),
b: float = disnake.Param(description="Second number")
):
if b == 0:
await inter.response.send_message("Cannot divide by zero!", ephemeral=True)
return
result = a / b
await inter.response.send_message(f"{a} ÷ {b} = {result}")
@bot.event
async def on_slash_command_error(inter, error):
if isinstance(error, commands.MissingPermissions):
await inter.response.send_message(
"You don't have permission to use this command!",
ephemeral=True
)
else:
await inter.response.send_message(
"An error occurred while processing the command.",
ephemeral=True
)Install with Tessl CLI
npx tessl i tessl/pypi-disnake