0
# Application Commands
1
2
Discord's native slash commands and context menus with built-in parameter validation, autocomplete, localization, and seamless UI integration. Application commands appear in Discord's interface and provide a modern alternative to prefix-based commands.
3
4
## Capabilities
5
6
### Command Tree Management
7
8
The CommandTree manages registration and syncing of application commands with Discord's API.
9
10
```python { .api }
11
class CommandTree:
12
"""
13
Manages application commands for a client.
14
"""
15
def __init__(self, client: Client): ...
16
17
client: Client # Associated Discord client
18
19
async def sync(self, *, guild: Optional[Guild] = None) -> List[AppCommand]:
20
"""
21
Sync commands with Discord.
22
23
Parameters:
24
- guild: Guild to sync to (None for global commands)
25
26
Returns:
27
List of synced AppCommand objects
28
"""
29
30
async def fetch_command(self, command_id: int, *, guild: Optional[Guild] = None) -> AppCommand:
31
"""Fetch a command by ID."""
32
33
async def fetch_commands(self, *, guild: Optional[Guild] = None) -> List[AppCommand]:
34
"""Fetch all commands."""
35
36
def command(
37
self,
38
*,
39
name: str = None,
40
description: str = None,
41
nsfw: bool = False,
42
guild: Optional[Guild] = None,
43
guilds: Optional[List[Guild]] = None,
44
auto_locale_strings: bool = True
45
) -> Callable:
46
"""Decorator to register a slash command."""
47
48
def context_menu(
49
self,
50
*,
51
name: str = None,
52
nsfw: bool = False,
53
guild: Optional[Guild] = None,
54
guilds: Optional[List[Guild]] = None,
55
auto_locale_strings: bool = True
56
) -> Callable:
57
"""Decorator to register a context menu command."""
58
59
def add_command(self, command: Union[Command, ContextMenu], *, guild: Optional[Guild] = None, guilds: Optional[List[Guild]] = None, override: bool = False) -> None:
60
"""Add a command to the tree."""
61
62
def remove_command(self, command: Union[str, Command, ContextMenu], *, guild: Optional[Guild] = None, type: Optional[AppCommandType] = None) -> Optional[Union[Command, ContextMenu]]:
63
"""Remove a command from the tree."""
64
65
def get_command(self, name: str, *, guild: Optional[Guild] = None, type: Optional[AppCommandType] = None) -> Optional[Union[Command, ContextMenu]]:
66
"""Get a command by name."""
67
68
def get_commands(self, *, guild: Optional[Guild] = None, type: Optional[AppCommandType] = None) -> List[Union[Command, ContextMenu]]:
69
"""Get all commands."""
70
71
def walk_commands(self, *, guild: Optional[Guild] = None, type: Optional[AppCommandType] = None) -> Iterator[Union[Command, ContextMenu]]:
72
"""Walk all commands."""
73
74
def clear_commands(self, *, guild: Optional[Guild] = None, type: Optional[AppCommandType] = None) -> None:
75
"""Clear all commands."""
76
77
def copy_global_to(self, guild: Guild) -> None:
78
"""Copy global commands to a guild."""
79
80
async def set_translator(self, translator: Translator) -> None:
81
"""Set command translator for localization."""
82
83
def error(self, coro: Callable) -> Callable:
84
"""Decorator for global error handler."""
85
86
def interaction_check(self, coro: Callable) -> Callable:
87
"""Decorator for global interaction check."""
88
```
89
90
### Slash Commands
91
92
Slash commands provide native Discord UI integration with parameter validation and autocomplete.
93
94
```python { .api }
95
class Command:
96
"""
97
Represents a slash command.
98
"""
99
def __init__(
100
self,
101
*,
102
name: str,
103
description: str,
104
callback: Callable,
105
nsfw: bool = False,
106
parent: Optional[Group] = None,
107
guild_ids: Optional[List[int]] = None,
108
auto_locale_strings: bool = True,
109
extras: Dict[Any, Any] = None
110
): ...
111
112
name: str # Command name
113
description: str # Command description
114
callback: Callable # Command function
115
nsfw: bool # Whether command is NSFW
116
parent: Optional[Group] # Parent group
117
guild_ids: Optional[List[int]] # Restricted guild IDs
118
qualified_name: str # Full command name including parent
119
mention: str # Command mention string
120
parameters: List[Parameter] # Command parameters
121
checks: List[Callable] # Command checks
122
extras: Dict[Any, Any] # Extra metadata
123
124
async def __call__(self, interaction: Interaction, **parameters) -> Any:
125
"""Invoke the command."""
126
127
def error(self, coro: Callable) -> Callable:
128
"""Decorator for command error handler."""
129
130
def autocomplete(self, name: str) -> Callable:
131
"""Decorator for parameter autocomplete."""
132
133
def describe(self, **parameters: str) -> Command:
134
"""Add parameter descriptions."""
135
136
def rename(self, **parameters: str) -> Command:
137
"""Rename parameters."""
138
139
class Group(Command):
140
"""
141
Slash command group containing subcommands.
142
"""
143
def __init__(self, *, name: str, description: str, **kwargs): ...
144
145
commands: Dict[str, Union[Command, Group]] # Child commands
146
147
def command(
148
self,
149
*,
150
name: str = None,
151
description: str = None,
152
nsfw: bool = False,
153
auto_locale_strings: bool = True,
154
extras: Dict[Any, Any] = None
155
) -> Callable:
156
"""Decorator to add a subcommand."""
157
158
def group(
159
self,
160
*,
161
name: str = None,
162
description: str = None,
163
nsfw: bool = False,
164
auto_locale_strings: bool = True,
165
extras: Dict[Any, Any] = None
166
) -> Callable:
167
"""Decorator to add a subcommand group."""
168
169
def add_command(self, command: Command) -> None:
170
"""Add a subcommand."""
171
172
def remove_command(self, name: str) -> Optional[Command]:
173
"""Remove a subcommand."""
174
175
def get_command(self, name: str) -> Optional[Command]:
176
"""Get a subcommand by name."""
177
178
# Command decorators
179
def command(
180
*,
181
name: str = None,
182
description: str = None,
183
nsfw: bool = False,
184
guild: Optional[Guild] = None,
185
guilds: Optional[List[Guild]] = None,
186
auto_locale_strings: bool = True,
187
extras: Dict[Any, Any] = None
188
) -> Callable:
189
"""Decorator to create a slash command."""
190
191
def context_menu(
192
*,
193
name: str = None,
194
nsfw: bool = False,
195
guild: Optional[Guild] = None,
196
guilds: Optional[List[Guild]] = None,
197
auto_locale_strings: bool = True,
198
extras: Dict[Any, Any] = None
199
) -> Callable:
200
"""Decorator to create a context menu command."""
201
202
def describe(**parameters: str) -> Callable:
203
"""Decorator to add parameter descriptions."""
204
205
def rename(**parameters: str) -> Callable:
206
"""Decorator to rename parameters."""
207
208
def choices(**parameters: List[Choice]) -> Callable:
209
"""Decorator to add parameter choices."""
210
211
def autocomplete(**parameters: Callable) -> Callable:
212
"""Decorator to add parameter autocomplete."""
213
214
def guild_only() -> Callable:
215
"""Decorator to restrict command to guilds only."""
216
217
def guilds(*guild_ids: int) -> Callable:
218
"""Decorator to restrict command to specific guilds."""
219
220
def default_permissions(**permissions: bool) -> Callable:
221
"""Decorator to set default permissions."""
222
```
223
224
### Context Menu Commands
225
226
Context menu commands appear in right-click menus for users and messages.
227
228
```python { .api }
229
class ContextMenu:
230
"""
231
Represents a context menu command.
232
"""
233
def __init__(
234
self,
235
*,
236
name: str,
237
callback: Callable,
238
nsfw: bool = False,
239
guild_ids: Optional[List[int]] = None,
240
auto_locale_strings: bool = True,
241
extras: Dict[Any, Any] = None
242
): ...
243
244
name: str # Command name
245
type: AppCommandType # Command type (USER or MESSAGE)
246
callback: Callable # Command function
247
nsfw: bool # Whether command is NSFW
248
guild_ids: Optional[List[int]] # Restricted guild IDs
249
qualified_name: str # Command name
250
mention: str # Command mention string
251
checks: List[Callable] # Command checks
252
extras: Dict[Any, Any] # Extra metadata
253
254
async def __call__(self, interaction: Interaction, target: Union[User, Member, Message]) -> Any:
255
"""Invoke the context menu command."""
256
257
def error(self, coro: Callable) -> Callable:
258
"""Decorator for command error handler."""
259
```
260
261
### Parameters & Validation
262
263
Command parameters with type validation, choices, and autocomplete support.
264
265
```python { .api }
266
class Parameter:
267
"""
268
Represents a command parameter.
269
"""
270
def __init__(
271
self,
272
*,
273
name: str,
274
description: str,
275
type: AppCommandOptionType,
276
required: bool = True,
277
default: Any = None,
278
choices: Optional[List[Choice]] = None,
279
autocomplete: Optional[Callable] = None,
280
channel_types: Optional[List[ChannelType]] = None,
281
min_value: Optional[Union[int, float]] = None,
282
max_value: Optional[Union[int, float]] = None,
283
min_length: Optional[int] = None,
284
max_length: Optional[int] = None
285
): ...
286
287
name: str # Parameter name
288
display_name: str # Display name (localized)
289
description: str # Parameter description
290
type: AppCommandOptionType # Parameter type
291
required: bool # Whether parameter is required
292
default: Any # Default value
293
choices: Optional[List[Choice]] # Fixed choices
294
autocomplete: Optional[Callable] # Autocomplete function
295
channel_types: Optional[List[ChannelType]] # Allowed channel types
296
min_value: Optional[Union[int, float]] # Minimum numeric value
297
max_value: Optional[Union[int, float]] # Maximum numeric value
298
min_length: Optional[int] # Minimum string length
299
max_length: Optional[int] # Maximum string length
300
301
class Choice:
302
"""
303
Represents a parameter choice.
304
"""
305
def __init__(self, *, name: str, value: Union[str, int, float]): ...
306
307
name: str # Choice display name
308
value: Union[str, int, float] # Choice value
309
310
class Range:
311
"""
312
Type annotation for numeric parameter ranges.
313
314
Usage: value: app_commands.Range[int, 1, 100]
315
"""
316
def __init__(self, annotation: Type, min: Any = None, max: Any = None): ...
317
318
# Parameter transformers
319
class Transform:
320
"""
321
Transform parameter values with custom logic.
322
323
Usage: value: app_commands.Transform[CustomType, CustomTransformer]
324
"""
325
def __init__(self, annotation: Type, transformer: Type[Transformer]): ...
326
327
class Transformer:
328
"""
329
Base class for parameter transformers.
330
"""
331
async def transform(self, interaction: Interaction, value: Any) -> Any:
332
"""Transform parameter value."""
333
raise NotImplementedError
334
335
async def autocomplete(self, interaction: Interaction, value: Union[int, float, str]) -> List[Choice]:
336
"""Provide autocomplete choices."""
337
return []
338
```
339
340
### Interaction Handling
341
342
Interaction objects represent slash command invocations with response management.
343
344
```python { .api }
345
class Interaction:
346
"""
347
Represents a Discord interaction (slash command, button click, etc.).
348
"""
349
id: int # Interaction ID
350
application_id: int # Application ID
351
type: InteractionType # Interaction type
352
data: Optional[InteractionData] # Interaction data
353
guild_id: Optional[int] # Guild ID
354
guild: Optional[Guild] # Guild object
355
channel_id: Optional[int] # Channel ID
356
channel: Optional[Union[GuildChannel, PartialMessageable]] # Channel object
357
user: User # User who triggered interaction
358
member: Optional[Member] # Member object if in guild
359
token: str # Interaction token
360
version: int # Interaction version
361
message: Optional[Message] # Message for component interactions
362
followup: Followup # Followup webhook
363
response: InteractionResponse # Response manager
364
command: Optional[Union[Command, ContextMenu]] # Command object
365
namespace: Optional[Namespace] # Parameter namespace
366
locale: str # User locale
367
guild_locale: Optional[str] # Guild locale
368
created_at: datetime # Interaction creation time
369
expires_at: datetime # When interaction expires
370
371
@property
372
def client(self) -> Client:
373
"""Client that received the interaction."""
374
375
def is_expired(self) -> bool:
376
"""Check if interaction has expired."""
377
378
async def original_response(self) -> InteractionMessage:
379
"""Get the original response message."""
380
381
async def edit_original_response(self, **kwargs) -> InteractionMessage:
382
"""Edit the original response."""
383
384
async def delete_original_response(self) -> None:
385
"""Delete the original response."""
386
387
async def translate(self, string: locale_str, locale: str = None, **kwargs) -> str:
388
"""Translate a localizable string."""
389
390
class InteractionResponse:
391
"""
392
Manages interaction responses.
393
"""
394
def __init__(self, parent: Interaction): ...
395
396
is_done(self) -> bool:
397
"""Check if response has been sent."""
398
399
async def send_message(
400
self,
401
content: str = None,
402
*,
403
embed: Embed = None,
404
embeds: List[Embed] = None,
405
file: File = None,
406
files: List[File] = None,
407
view: View = None,
408
ephemeral: bool = False,
409
tts: bool = False,
410
allowed_mentions: AllowedMentions = None,
411
suppress_embeds: bool = False
412
) -> None:
413
"""Send response message."""
414
415
async def defer(self, *, ephemeral: bool = False, thinking: bool = False) -> None:
416
"""Defer response to send later."""
417
418
async def pong(self) -> None:
419
"""Respond to ping interaction."""
420
421
async def edit_message(
422
self,
423
*,
424
content: str = None,
425
embed: Embed = None,
426
embeds: List[Embed] = None,
427
attachments: List[Attachment] = None,
428
view: View = None,
429
allowed_mentions: AllowedMentions = None,
430
suppress_embeds: bool = False
431
) -> None:
432
"""Edit original message."""
433
434
async def autocomplete(self, *, choices: List[Choice]) -> None:
435
"""Respond with autocomplete choices."""
436
437
class Namespace:
438
"""
439
Container for command parameters.
440
"""
441
def __init__(self, interaction: Interaction, data: InteractionData): ...
442
443
def __getattr__(self, name: str) -> Any:
444
"""Get parameter value by name."""
445
446
def __getitem__(self, name: str) -> Any:
447
"""Get parameter value by name."""
448
449
def __contains__(self, name: str) -> bool:
450
"""Check if parameter exists."""
451
452
def __iter__(self) -> Iterator[str]:
453
"""Iterate parameter names."""
454
455
def get(self, name: str, default: Any = None) -> Any:
456
"""Get parameter value with default."""
457
```
458
459
### Command Checks & Permissions
460
461
Permission validation and custom checks for application commands.
462
463
```python { .api }
464
# Check decorators
465
def check(predicate: Callable[[Interaction], Awaitable[bool]]) -> Callable:
466
"""Add a check to a command."""
467
468
# Permission checks
469
def has_role(role: Union[int, str]) -> Callable:
470
"""Check if user has a specific role."""
471
472
def has_any_role(*roles: Union[int, str]) -> Callable:
473
"""Check if user has any of the specified roles."""
474
475
def has_permissions(**permissions: bool) -> Callable:
476
"""Check if user has specific permissions."""
477
478
def bot_has_permissions(**permissions: bool) -> Callable:
479
"""Check if bot has specific permissions."""
480
481
def default_permissions(**permissions: bool) -> Callable:
482
"""Set default permissions for the command."""
483
484
# Cooldown system
485
class Cooldown:
486
"""
487
Application command cooldown.
488
"""
489
def __init__(self, rate: int, per: float): ...
490
491
rate: int # Number of uses allowed
492
per: float # Time period in seconds
493
494
def cooldown(rate: int, per: float) -> Callable:
495
"""Add cooldown to a command."""
496
497
def dynamic_cooldown(cooldown_func: Callable[[Interaction], Optional[Cooldown]]) -> Callable:
498
"""Add dynamic cooldown to a command."""
499
```
500
501
### Error Handling
502
503
Comprehensive error handling for application commands with specific exception types.
504
505
```python { .api }
506
# Base application command errors
507
class AppCommandError(DiscordException):
508
"""Base exception for application command errors."""
509
pass
510
511
class CommandInvokeError(AppCommandError):
512
"""Exception occurred during command execution."""
513
def __init__(self, command: Union[Command, ContextMenu], e: Exception): ...
514
command: Union[Command, ContextMenu]
515
original: Exception
516
517
# Parameter errors
518
class TransformerError(AppCommandError):
519
"""Parameter transformer failed."""
520
def __init__(self, value: Any, type: AppCommandOptionType, transformer: Type[Transformer]): ...
521
value: Any
522
type: AppCommandOptionType
523
transformer: Type[Transformer]
524
525
# Command management errors
526
class CommandAlreadyRegistered(AppCommandError):
527
"""Command is already registered."""
528
def __init__(self, name: str, guild_id: Optional[int]): ...
529
name: str
530
guild_id: Optional[int]
531
532
class CommandSignatureMismatch(AppCommandError):
533
"""Command signature doesn't match Discord's requirements."""
534
pass
535
536
class CommandNotFound(AppCommandError):
537
"""Command was not found."""
538
def __init__(self, name: str, parents: List[str]): ...
539
name: str
540
parents: List[str]
541
542
class CommandLimitReached(AppCommandError):
543
"""Maximum number of commands reached."""
544
def __init__(self, guild_id: Optional[int], limit: int, type: AppCommandType): ...
545
guild_id: Optional[int]
546
limit: int
547
type: AppCommandType
548
549
# Check failures
550
class CheckFailure(AppCommandError):
551
"""Command check failed."""
552
pass
553
554
class NoPrivateMessage(CheckFailure):
555
"""Command cannot be used in private messages."""
556
pass
557
558
class MissingRole(CheckFailure):
559
"""User is missing required role."""
560
def __init__(self, missing_role: Union[str, int]): ...
561
missing_role: Union[str, int]
562
563
class MissingAnyRole(CheckFailure):
564
"""User is missing any of the required roles."""
565
def __init__(self, missing_roles: List[Union[str, int]]): ...
566
missing_roles: List[Union[str, int]]
567
568
class MissingPermissions(CheckFailure):
569
"""User is missing required permissions."""
570
def __init__(self, missing_permissions: List[str]): ...
571
missing_permissions: List[str]
572
573
class BotMissingPermissions(CheckFailure):
574
"""Bot is missing required permissions."""
575
def __init__(self, missing_permissions: List[str]): ...
576
missing_permissions: List[str]
577
578
class CommandOnCooldown(CheckFailure):
579
"""Command is on cooldown."""
580
def __init__(self, cooldown: Cooldown, retry_after: float): ...
581
cooldown: Cooldown
582
retry_after: float
583
584
# Sync errors
585
class CommandSyncFailure(AppCommandError):
586
"""Failed to sync commands with Discord."""
587
pass
588
589
class MissingApplicationID(CommandSyncFailure):
590
"""Application ID is missing."""
591
pass
592
593
# Translation errors
594
class TranslationError(AppCommandError):
595
"""Translation failed."""
596
def __init__(self, message: str, key: locale_str, locale: str): ...
597
message: str
598
key: locale_str
599
locale: str
600
```
601
602
### Localization Support
603
604
Built-in localization system for command names, descriptions, and responses.
605
606
```python { .api }
607
class locale_str:
608
"""
609
Represents a localizable string.
610
"""
611
def __init__(self, message: str, **kwargs): ...
612
613
message: str # Base message string
614
extras: Dict[str, Any] # Extra data for formatting
615
616
class Translator:
617
"""
618
Base class for command translators.
619
"""
620
async def translate(self, string: locale_str, locale: str, context: TranslationContext) -> Optional[str]:
621
"""Translate a localizable string."""
622
raise NotImplementedError
623
624
class TranslationContext:
625
"""
626
Context for translation requests.
627
"""
628
location: TranslationContextLocation # Where translation is used
629
data: Any # Context-specific data
630
631
class TranslationContextLocation(Enum):
632
"""Location where translation is being performed."""
633
command_name = 1
634
command_description = 2
635
parameter_name = 3
636
parameter_description = 4
637
choice_name = 5
638
other = 6
639
640
TranslationContextTypes = Union[
641
Command,
642
ContextMenu,
643
Parameter,
644
Choice,
645
Group
646
]
647
```
648
649
## Usage Examples
650
651
### Basic Slash Command
652
653
```python
654
import discord
655
from discord.ext import commands
656
657
class MyBot(commands.Bot):
658
def __init__(self):
659
intents = discord.Intents.default()
660
super().__init__(command_prefix='!', intents=intents)
661
662
async def setup_hook(self):
663
# Sync commands on startup
664
await self.tree.sync()
665
print(f"Synced {len(self.tree.get_commands())} commands")
666
667
bot = MyBot()
668
669
@bot.tree.command(name="hello", description="Say hello to someone")
670
@app_commands.describe(user="The user to greet")
671
async def hello(interaction: discord.Interaction, user: discord.Member):
672
await interaction.response.send_message(f"Hello {user.mention}!")
673
674
bot.run('YOUR_TOKEN')
675
```
676
677
### Slash Command with Choices and Validation
678
679
```python
680
@bot.tree.command(name="roll", description="Roll dice")
681
@app_commands.describe(
682
sides="Number of sides on the die",
683
count="Number of dice to roll"
684
)
685
@app_commands.choices(sides=[
686
app_commands.Choice(name="4-sided (d4)", value=4),
687
app_commands.Choice(name="6-sided (d6)", value=6),
688
app_commands.Choice(name="8-sided (d8)", value=8),
689
app_commands.Choice(name="10-sided (d10)", value=10),
690
app_commands.Choice(name="12-sided (d12)", value=12),
691
app_commands.Choice(name="20-sided (d20)", value=20),
692
])
693
async def roll_dice(
694
interaction: discord.Interaction,
695
sides: app_commands.Choice[int],
696
count: app_commands.Range[int, 1, 10] = 1
697
):
698
import random
699
700
rolls = [random.randint(1, sides.value) for _ in range(count)]
701
total = sum(rolls)
702
703
embed = discord.Embed(title="🎲 Dice Roll Results", color=0x00ff00)
704
embed.add_field(name="Rolls", value=" + ".join(map(str, rolls)), inline=False)
705
embed.add_field(name="Total", value=str(total), inline=True)
706
embed.add_field(name="Dice", value=f"{count}d{sides.value}", inline=True)
707
708
await interaction.response.send_message(embed=embed)
709
```
710
711
### Context Menu Command
712
713
```python
714
@bot.tree.context_menu(name="Get User Info")
715
async def user_info(interaction: discord.Interaction, user: discord.Member):
716
embed = discord.Embed(title=f"Info for {user.display_name}", color=user.color)
717
embed.set_thumbnail(url=user.display_avatar.url)
718
embed.add_field(name="ID", value=user.id, inline=True)
719
embed.add_field(name="Joined", value=f"<t:{int(user.joined_at.timestamp())}:R>", inline=True)
720
embed.add_field(name="Roles", value=len(user.roles) - 1, inline=True)
721
722
await interaction.response.send_message(embed=embed, ephemeral=True)
723
```
724
725
### Command Group with Subcommands
726
727
```python
728
@bot.tree.command(name="config", description="Bot configuration commands")
729
async def config_group(interaction: discord.Interaction):
730
# This won't be called as it has subcommands
731
pass
732
733
@config_group.subcommand(name="prefix", description="Set bot prefix")
734
@app_commands.describe(new_prefix="New command prefix")
735
async def config_prefix(interaction: discord.Interaction, new_prefix: str):
736
# Update prefix logic here
737
await interaction.response.send_message(f"Prefix set to `{new_prefix}`")
738
739
@config_group.subcommand(name="channel", description="Set bot channel")
740
@app_commands.describe(channel="Channel for bot commands")
741
async def config_channel(interaction: discord.Interaction, channel: discord.TextChannel):
742
# Update channel logic here
743
await interaction.response.send_message(f"Bot channel set to {channel.mention}")
744
```
745
746
### Autocomplete Example
747
748
```python
749
# Simple autocomplete
750
@bot.tree.command(name="timezone", description="Set your timezone")
751
async def timezone_command(interaction: discord.Interaction, timezone: str):
752
# Timezone setting logic here
753
await interaction.response.send_message(f"Timezone set to {timezone}")
754
755
@timezone_command.autocomplete('timezone')
756
async def timezone_autocomplete(interaction: discord.Interaction, current: str) -> List[app_commands.Choice[str]]:
757
timezones = ["UTC", "US/Eastern", "US/Central", "US/Mountain", "US/Pacific", "Europe/London", "Europe/Berlin"]
758
return [
759
app_commands.Choice(name=tz, value=tz)
760
for tz in timezones if current.lower() in tz.lower()
761
][:25] # Discord limits to 25 choices
762
```