0
# Nextcord Application Commands
1
2
Modern Discord application commands including slash commands, user commands, and message commands with comprehensive interaction handling capabilities.
3
4
## Slash Commands
5
6
Modern slash commands that provide a native Discord interface with autocomplete, options, and structured parameters.
7
8
### Command Decorators { .api }
9
10
```python
11
import nextcord
12
from nextcord import SlashOption, OptionType
13
from nextcord.ext import commands
14
from typing import Optional, List
15
16
def slash_command(
17
*,
18
name: Optional[str] = None,
19
description: Optional[str] = None,
20
guild_ids: Optional[List[int]] = None,
21
default_member_permissions: Optional[int] = None,
22
dm_permission: Optional[bool] = None,
23
nsfw: bool = False,
24
force_global: bool = False,
25
**kwargs
26
) -> Callable:
27
"""Decorator to create a slash command.
28
29
Parameters
30
----------
31
name: Optional[str]
32
The name of the command. If not provided, uses the function name.
33
description: Optional[str]
34
The description of the command. Required for slash commands.
35
guild_ids: Optional[List[int]]
36
List of guild IDs where this command should be registered.
37
default_member_permissions: Optional[int]
38
Default permissions required to use this command.
39
dm_permission: Optional[bool]
40
Whether this command can be used in DMs.
41
nsfw: bool
42
Whether this command is NSFW and requires an NSFW channel.
43
force_global: bool
44
Whether to force this command to be global even with guild_ids.
45
"""
46
...
47
48
# Basic slash command example
49
@bot.slash_command(description="Say hello to someone")
50
async def hello(interaction: nextcord.Interaction, user: nextcord.Member):
51
"""A simple slash command that greets a user."""
52
await interaction.response.send_message(f"Hello, {user.mention}!")
53
54
# Slash command with options
55
@bot.slash_command(description="Get information about a user")
56
async def userinfo(
57
interaction: nextcord.Interaction,
58
user: nextcord.Member = SlashOption(description="The user to get info about"),
59
show_avatar: bool = SlashOption(description="Show user avatar", default=True)
60
):
61
"""Get detailed information about a user."""
62
embed = nextcord.Embed(
63
title=f"User Info: {user.display_name}",
64
color=user.color
65
)
66
67
embed.add_field(name="Username", value=str(user), inline=True)
68
embed.add_field(name="ID", value=user.id, inline=True)
69
embed.add_field(name="Joined", value=user.joined_at.strftime("%Y-%m-%d"), inline=True)
70
71
if show_avatar:
72
embed.set_thumbnail(url=user.display_avatar.url)
73
74
await interaction.response.send_message(embed=embed)
75
```
76
77
### Slash Options { .api }
78
79
```python
80
from nextcord import SlashOption, OptionType, ChannelType
81
from typing import Union
82
83
class SlashOption:
84
"""Represents a slash command option parameter.
85
86
Parameters
87
----------
88
name: Optional[str]
89
The name of the option. If not provided, uses parameter name.
90
description: Optional[str]
91
The description of the option.
92
option_type: Optional[OptionType]
93
The type of the option. Auto-detected from type hints if not provided.
94
required: Optional[bool]
95
Whether this option is required. Auto-detected from default values.
96
default: Any
97
The default value for this option.
98
choices: Optional[Union[Dict[str, Union[str, int]], List[Union[str, int]]]]
99
Predefined choices for this option.
100
channel_types: Optional[List[ChannelType]]
101
Valid channel types (for channel options).
102
min_value: Optional[Union[int, float]]
103
Minimum value (for number options).
104
max_value: Optional[Union[int, float]]
105
Maximum value (for number options).
106
min_length: Optional[int]
107
Minimum length (for string options).
108
max_length: Optional[int]
109
Maximum length (for string options).
110
autocomplete: Optional[bool]
111
Whether this option should use autocomplete.
112
"""
113
114
def __init__(
115
self,
116
name: Optional[str] = None,
117
description: Optional[str] = None,
118
option_type: Optional[OptionType] = None,
119
required: Optional[bool] = None,
120
default: Any = MISSING,
121
choices: Optional[Union[Dict[str, Union[str, int]], List[Union[str, int]]]] = None,
122
channel_types: Optional[List[ChannelType]] = None,
123
min_value: Optional[Union[int, float]] = None,
124
max_value: Optional[Union[int, float]] = None,
125
min_length: Optional[int] = None,
126
max_length: Optional[int] = None,
127
autocomplete: Optional[bool] = None,
128
):
129
...
130
131
# Examples of different option types
132
@bot.slash_command(description="Demonstration of various option types")
133
async def options_demo(
134
interaction: nextcord.Interaction,
135
# String option with choices
136
color: str = SlashOption(
137
description="Pick a color",
138
choices=["red", "green", "blue", "yellow"]
139
),
140
# Integer option with min/max
141
amount: int = SlashOption(
142
description="Amount (1-100)",
143
min_value=1,
144
max_value=100
145
),
146
# Optional member parameter
147
target: Optional[nextcord.Member] = SlashOption(
148
description="Target member",
149
default=None
150
),
151
# Channel option with specific types
152
channel: Optional[nextcord.TextChannel] = SlashOption(
153
description="Text channel",
154
channel_types=[ChannelType.text, ChannelType.news],
155
default=None
156
),
157
# Boolean option
158
public: bool = SlashOption(
159
description="Make response public",
160
default=False
161
),
162
# String with length limits
163
message: str = SlashOption(
164
description="Your message",
165
min_length=1,
166
max_length=200
167
)
168
):
169
"""Demonstrate various slash option types."""
170
response = f"Color: {color}, Amount: {amount}, Message: {message}"
171
172
if target:
173
response += f", Target: {target.mention}"
174
if channel:
175
response += f", Channel: {channel.mention}"
176
177
await interaction.response.send_message(response, ephemeral=not public)
178
```
179
180
### Subcommands and Groups { .api }
181
182
```python
183
class SlashCommandGroup:
184
"""A group of related slash commands.
185
186
Attributes
187
----------
188
name: str
189
The name of the command group.
190
description: str
191
The description of the command group.
192
guild_ids: Optional[List[int]]
193
Guild IDs where this group should be registered.
194
"""
195
196
def __init__(
197
self,
198
name: str,
199
description: str,
200
*,
201
guild_ids: Optional[List[int]] = None,
202
default_member_permissions: Optional[int] = None,
203
dm_permission: Optional[bool] = None,
204
nsfw: bool = False,
205
):
206
...
207
208
def subcommand(
209
self,
210
name: Optional[str] = None,
211
description: Optional[str] = None,
212
**kwargs
213
) -> Callable:
214
"""Decorator to add a subcommand to this group."""
215
...
216
217
# Command group example
218
moderation = nextcord.SlashCommandGroup(
219
name="mod",
220
description="Moderation commands",
221
default_member_permissions=nextcord.Permissions(moderate_members=True)
222
)
223
224
@moderation.subcommand(description="Timeout a member")
225
async def timeout(
226
interaction: nextcord.Interaction,
227
member: nextcord.Member = SlashOption(description="Member to timeout"),
228
duration: int = SlashOption(description="Duration in minutes", min_value=1, max_value=40320),
229
reason: Optional[str] = SlashOption(description="Reason for timeout", default=None)
230
):
231
"""Timeout a member for a specified duration."""
232
from datetime import datetime, timedelta
233
234
until = datetime.utcnow() + timedelta(minutes=duration)
235
236
try:
237
await member.timeout(until=until, reason=reason)
238
await interaction.response.send_message(
239
f"⏰ {member.mention} has been timed out for {duration} minutes.",
240
ephemeral=True
241
)
242
except nextcord.Forbidden:
243
await interaction.response.send_message(
244
"❌ I don't have permission to timeout this member.",
245
ephemeral=True
246
)
247
248
@moderation.subcommand(description="Remove timeout from a member")
249
async def untimeout(
250
interaction: nextcord.Interaction,
251
member: nextcord.Member = SlashOption(description="Member to remove timeout from"),
252
reason: Optional[str] = SlashOption(description="Reason", default=None)
253
):
254
"""Remove timeout from a member."""
255
try:
256
await member.timeout(until=None, reason=reason)
257
await interaction.response.send_message(
258
f"✅ Timeout removed from {member.mention}.",
259
ephemeral=True
260
)
261
except nextcord.Forbidden:
262
await interaction.response.send_message(
263
"❌ I don't have permission to remove timeout from this member.",
264
ephemeral=True
265
)
266
267
# Register the command group
268
bot.add_application_command(moderation)
269
```
270
271
### Autocomplete { .api }
272
273
```python
274
from nextcord.ext import application_checks
275
276
@bot.slash_command(description="Search for items")
277
async def search(
278
interaction: nextcord.Interaction,
279
query: str = SlashOption(
280
description="Search query",
281
autocomplete=True
282
)
283
):
284
"""Search command with autocomplete."""
285
await interaction.response.send_message(f"Searching for: {query}")
286
287
@search.on_autocomplete("query")
288
async def search_autocomplete(interaction: nextcord.Interaction, query: str):
289
"""Provide autocomplete suggestions for the search command."""
290
291
# Example search items
292
items = [
293
"Apple", "Banana", "Cherry", "Date", "Elderberry",
294
"Fig", "Grape", "Honeydew", "Ice cream", "Jackfruit",
295
"Kiwi", "Lemon", "Mango", "Nectarine", "Orange"
296
]
297
298
# Filter items based on current input
299
if query:
300
filtered_items = [item for item in items if query.lower() in item.lower()]
301
else:
302
filtered_items = items[:10] # Show first 10 if no query
303
304
# Return up to 25 suggestions (Discord's limit)
305
suggestions = filtered_items[:25]
306
307
await interaction.response.send_autocomplete(
308
{item: item for item in suggestions}
309
)
310
311
# More complex autocomplete with database lookup
312
@bot.slash_command(description="Get user by name")
313
async def find_user(
314
interaction: nextcord.Interaction,
315
username: str = SlashOption(
316
description="Username to search for",
317
autocomplete=True
318
)
319
):
320
"""Find a user by username with autocomplete."""
321
# Search for the user
322
user = nextcord.utils.get(interaction.guild.members, name=username)
323
if user:
324
await interaction.response.send_message(f"Found user: {user.mention}")
325
else:
326
await interaction.response.send_message("User not found.")
327
328
@find_user.on_autocomplete("username")
329
async def username_autocomplete(interaction: nextcord.Interaction, username: str):
330
"""Provide username autocomplete suggestions."""
331
332
# Get guild members matching the query
333
if username:
334
matching_members = [
335
member for member in interaction.guild.members
336
if username.lower() in member.name.lower()
337
][:25]
338
else:
339
matching_members = interaction.guild.members[:25]
340
341
suggestions = {member.name: member.name for member in matching_members}
342
343
await interaction.response.send_autocomplete(suggestions)
344
```
345
346
## Context Menu Commands
347
348
Right-click context menu commands for users and messages.
349
350
### User Commands { .api }
351
352
```python
353
def user_command(
354
*,
355
name: Optional[str] = None,
356
guild_ids: Optional[List[int]] = None,
357
default_member_permissions: Optional[int] = None,
358
dm_permission: Optional[bool] = None,
359
nsfw: bool = False,
360
force_global: bool = False,
361
) -> Callable:
362
"""Decorator to create a user context menu command.
363
364
Parameters
365
----------
366
name: Optional[str]
367
The name of the command. If not provided, uses function name.
368
guild_ids: Optional[List[int]]
369
List of guild IDs where this command should be registered.
370
default_member_permissions: Optional[int]
371
Default permissions required to use this command.
372
dm_permission: Optional[bool]
373
Whether this command can be used in DMs.
374
nsfw: bool
375
Whether this command is NSFW.
376
force_global: bool
377
Whether to force this command to be global.
378
"""
379
...
380
381
# User context menu command example
382
@bot.user_command(name="Get Avatar")
383
async def get_avatar(interaction: nextcord.Interaction, user: nextcord.Member):
384
"""Get a user's avatar via right-click menu."""
385
embed = nextcord.Embed(
386
title=f"{user.display_name}'s Avatar",
387
color=user.color or nextcord.Color.blue()
388
)
389
embed.set_image(url=user.display_avatar.url)
390
embed.set_footer(text=f"User ID: {user.id}")
391
392
await interaction.response.send_message(embed=embed, ephemeral=True)
393
394
@bot.user_command(name="User Info")
395
async def user_info_context(interaction: nextcord.Interaction, user: nextcord.Member):
396
"""Get detailed user information via context menu."""
397
embed = nextcord.Embed(
398
title=f"User Info: {user.display_name}",
399
color=user.color or nextcord.Color.blue()
400
)
401
402
embed.add_field(name="Username", value=str(user), inline=True)
403
embed.add_field(name="ID", value=user.id, inline=True)
404
embed.add_field(name="Bot", value="Yes" if user.bot else "No", inline=True)
405
406
if user.joined_at:
407
embed.add_field(
408
name="Joined Server",
409
value=user.joined_at.strftime("%Y-%m-%d %H:%M UTC"),
410
inline=True
411
)
412
413
embed.add_field(
414
name="Account Created",
415
value=user.created_at.strftime("%Y-%m-%d %H:%M UTC"),
416
inline=True
417
)
418
419
if user.roles[1:]: # Exclude @everyone
420
roles = [role.mention for role in user.roles[1:][:10]] # Show first 10 roles
421
embed.add_field(
422
name="Roles",
423
value=", ".join(roles) + ("..." if len(user.roles) > 11 else ""),
424
inline=False
425
)
426
427
embed.set_thumbnail(url=user.display_avatar.url)
428
429
await interaction.response.send_message(embed=embed, ephemeral=True)
430
```
431
432
### Message Commands { .api }
433
434
```python
435
def message_command(
436
*,
437
name: Optional[str] = None,
438
guild_ids: Optional[List[int]] = None,
439
default_member_permissions: Optional[int] = None,
440
dm_permission: Optional[bool] = None,
441
nsfw: bool = False,
442
force_global: bool = False,
443
) -> Callable:
444
"""Decorator to create a message context menu command.
445
446
Parameters
447
----------
448
name: Optional[str]
449
The name of the command. If not provided, uses function name.
450
guild_ids: Optional[List[int]]
451
List of guild IDs where this command should be registered.
452
default_member_permissions: Optional[int]
453
Default permissions required to use this command.
454
dm_permission: Optional[bool]
455
Whether this command can be used in DMs.
456
nsfw: bool
457
Whether this command is NSFW.
458
force_global: bool
459
Whether to force this command to be global.
460
"""
461
...
462
463
# Message context menu command examples
464
@bot.message_command(name="Quote Message")
465
async def quote_message(interaction: nextcord.Interaction, message: nextcord.Message):
466
"""Quote a message via right-click menu."""
467
embed = nextcord.Embed(
468
description=message.content or "*No text content*",
469
timestamp=message.created_at,
470
color=nextcord.Color.blue()
471
)
472
473
embed.set_author(
474
name=message.author.display_name,
475
icon_url=message.author.display_avatar.url
476
)
477
478
embed.set_footer(text=f"Originally in #{message.channel.name}")
479
480
# Handle attachments
481
if message.attachments:
482
embed.add_field(
483
name="Attachments",
484
value=f"{len(message.attachments)} file(s)",
485
inline=True
486
)
487
488
await interaction.response.send_message(embed=embed)
489
490
@bot.message_command(name="Message Info")
491
async def message_info(interaction: nextcord.Interaction, message: nextcord.Message):
492
"""Get detailed message information."""
493
embed = nextcord.Embed(
494
title="Message Information",
495
color=nextcord.Color.blue()
496
)
497
498
embed.add_field(name="Author", value=message.author.mention, inline=True)
499
embed.add_field(name="Message ID", value=message.id, inline=True)
500
embed.add_field(name="Channel", value=message.channel.mention, inline=True)
501
502
embed.add_field(
503
name="Created At",
504
value=message.created_at.strftime("%Y-%m-%d %H:%M:%S UTC"),
505
inline=True
506
)
507
508
if message.edited_at:
509
embed.add_field(
510
name="Last Edited",
511
value=message.edited_at.strftime("%Y-%m-%d %H:%M:%S UTC"),
512
inline=True
513
)
514
515
embed.add_field(
516
name="Jump to Message",
517
value=f"[Click here]({message.jump_url})",
518
inline=True
519
)
520
521
# Content preview
522
if message.content:
523
content_preview = message.content[:100] + ("..." if len(message.content) > 100 else "")
524
embed.add_field(name="Content Preview", value=content_preview, inline=False)
525
526
# Attachments
527
if message.attachments:
528
attachment_info = []
529
for attachment in message.attachments[:5]: # Show first 5 attachments
530
size_kb = attachment.size // 1024
531
attachment_info.append(f"• {attachment.filename} ({size_kb} KB)")
532
533
embed.add_field(
534
name=f"Attachments ({len(message.attachments)})",
535
value="\n".join(attachment_info),
536
inline=False
537
)
538
539
# Reactions
540
if message.reactions:
541
reaction_info = []
542
for reaction in message.reactions[:10]: # Show first 10 reactions
543
reaction_info.append(f"{reaction.emoji} ({reaction.count})")
544
545
embed.add_field(
546
name="Reactions",
547
value=" ".join(reaction_info),
548
inline=False
549
)
550
551
await interaction.response.send_message(embed=embed, ephemeral=True)
552
553
@bot.message_command(name="Copy Message ID")
554
async def copy_message_id(interaction: nextcord.Interaction, message: nextcord.Message):
555
"""Copy a message ID to clipboard-friendly format."""
556
await interaction.response.send_message(
557
f"Message ID: `{message.id}`\n"
558
f"Jump URL: {message.jump_url}",
559
ephemeral=True
560
)
561
```
562
563
## Interactions
564
565
Interaction objects and response handling for application commands.
566
567
### Interaction Class { .api }
568
569
```python
570
class Interaction:
571
"""Represents a Discord interaction.
572
573
Attributes
574
----------
575
id: int
576
The interaction ID.
577
application_id: int
578
The application ID that received this interaction.
579
type: InteractionType
580
The type of interaction.
581
data: Optional[InteractionData]
582
The interaction data payload.
583
guild_id: Optional[int]
584
The guild ID where this interaction was sent from.
585
channel_id: Optional[int]
586
The channel ID where this interaction was sent from.
587
user: Union[User, Member]
588
The user who invoked this interaction.
589
token: str
590
The interaction token.
591
version: int
592
The version of interaction.
593
message: Optional[Message]
594
The message this interaction is attached to.
595
locale: Optional[str]
596
The selected language of the user who invoked this interaction.
597
guild_locale: Optional[str]
598
The guild's preferred locale.
599
"""
600
601
@property
602
def guild(self) -> Optional[Guild]:
603
"""Optional[Guild]: The guild this interaction was sent from."""
604
...
605
606
@property
607
def channel(self) -> Optional[Union[GuildChannel, PartialMessageable]]:
608
"""The channel this interaction was sent from."""
609
...
610
611
@property
612
def permissions(self) -> Permissions:
613
"""Permissions: The resolved permissions of the user in the channel."""
614
...
615
616
@property
617
def response(self) -> InteractionResponse:
618
"""InteractionResponse: Returns an object responsible for handling responses."""
619
...
620
621
@property
622
def followup(self) -> Webhook:
623
"""Webhook: Returns the follow-up webhook for this interaction."""
624
...
625
626
def is_expired(self) -> bool:
627
"""bool: Whether the interaction is expired and can no longer be responded to."""
628
...
629
630
async def edit_original_message(self, **kwargs) -> InteractionMessage:
631
"""Edit the original interaction response message."""
632
...
633
634
async def delete_original_message(self) -> None:
635
"""Delete the original interaction response message."""
636
...
637
638
async def original_message(self) -> InteractionMessage:
639
"""Fetch the original interaction response message."""
640
...
641
```
642
643
### Interaction Response { .api }
644
645
```python
646
class InteractionResponse:
647
"""Handles responding to Discord interactions.
648
649
This class is accessed via Interaction.response and provides
650
methods for sending initial responses to interactions.
651
"""
652
653
async def send_message(
654
self,
655
content: Optional[str] = None,
656
*,
657
embed: Optional[Embed] = None,
658
embeds: Optional[List[Embed]] = None,
659
file: Optional[File] = None,
660
files: Optional[List[File]] = None,
661
view: Optional[View] = None,
662
tts: bool = False,
663
ephemeral: bool = False,
664
allowed_mentions: Optional[AllowedMentions] = None,
665
suppress_embeds: bool = False,
666
delete_after: Optional[float] = None,
667
) -> None:
668
"""Send a message response to the interaction.
669
670
Parameters
671
----------
672
content: Optional[str]
673
The message content to send.
674
embed: Optional[Embed]
675
An embed to send with the message.
676
embeds: Optional[List[Embed]]
677
A list of embeds to send. Maximum of 10.
678
file: Optional[File]
679
A file to send with the message.
680
files: Optional[List[File]]
681
A list of files to send. Maximum of 10.
682
view: Optional[View]
683
A UI view to attach to the message.
684
tts: bool
685
Whether the message should be text-to-speech.
686
ephemeral: bool
687
Whether the message should be ephemeral (only visible to the user).
688
allowed_mentions: Optional[AllowedMentions]
689
Controls which mentions are processed.
690
suppress_embeds: bool
691
Whether to suppress embeds in the message.
692
delete_after: Optional[float]
693
Delete the message after this many seconds.
694
"""
695
...
696
697
async def defer(self, *, ephemeral: bool = False, thinking: bool = False) -> None:
698
"""Defer the interaction response.
699
700
This allows you to respond later using followup messages.
701
702
Parameters
703
----------
704
ephemeral: bool
705
Whether the deferred response should be ephemeral.
706
thinking: bool
707
Whether to show "Bot is thinking..." message.
708
"""
709
...
710
711
async def edit_message(
712
self,
713
content: Optional[str] = MISSING,
714
*,
715
embed: Optional[Embed] = MISSING,
716
embeds: Optional[List[Embed]] = MISSING,
717
file: Optional[File] = MISSING,
718
files: Optional[List[File]] = MISSING,
719
attachments: Optional[List[Attachment]] = MISSING,
720
view: Optional[View] = MISSING,
721
allowed_mentions: Optional[AllowedMentions] = MISSING,
722
delete_after: Optional[float] = None,
723
) -> None:
724
"""Edit the message that triggered this interaction.
725
726
Note: This is only available for component interactions.
727
"""
728
...
729
730
async def send_autocomplete(
731
self,
732
choices: Dict[str, Union[str, int, float]]
733
) -> None:
734
"""Send autocomplete suggestions.
735
736
Parameters
737
----------
738
choices: Dict[str, Union[str, int, float]]
739
A dictionary of choices where keys are display names
740
and values are the actual values to be used.
741
"""
742
...
743
744
async def send_modal(self, modal: Modal) -> None:
745
"""Send a modal dialog in response to the interaction.
746
747
Parameters
748
----------
749
modal: Modal
750
The modal dialog to send.
751
"""
752
...
753
754
@property
755
def is_done(self) -> bool:
756
"""bool: Whether an initial response has been sent."""
757
...
758
```
759
760
### Follow-up Messages { .api }
761
762
```python
763
# Follow-up messages are sent after the initial response
764
@bot.slash_command(description="Example of follow-up messages")
765
async def followup_example(interaction: nextcord.Interaction):
766
"""Demonstrate follow-up message usage."""
767
768
# Send initial response
769
await interaction.response.send_message("Processing your request...")
770
771
# Simulate some work
772
import asyncio
773
await asyncio.sleep(2)
774
775
# Send follow-up message
776
await interaction.followup.send("Step 1 complete!")
777
778
await asyncio.sleep(1)
779
780
# Another follow-up
781
embed = nextcord.Embed(title="Final Result", description="Task completed successfully!")
782
await interaction.followup.send(embed=embed)
783
784
# You can also edit the original message
785
await interaction.edit_original_message(content="✅ All steps completed!")
786
787
@bot.slash_command(description="Example with deferred response")
788
async def long_task(interaction: nextcord.Interaction):
789
"""Example of using deferred response for long tasks."""
790
791
# Defer the response to give more time
792
await interaction.response.defer(ephemeral=True)
793
794
# Simulate long-running task
795
import asyncio
796
await asyncio.sleep(5)
797
798
# Send the actual response via followup
799
embed = nextcord.Embed(
800
title="Long Task Complete",
801
description="The task that took 5 seconds has finished!",
802
color=nextcord.Color.green()
803
)
804
805
await interaction.followup.send(embed=embed)
806
```
807
808
## Application Command Checks
809
810
Permission and validation checks for application commands.
811
812
### Built-in Checks { .api }
813
814
```python
815
from nextcord.ext import application_checks
816
817
# Permission-based checks
818
@application_checks.has_permissions(manage_messages=True)
819
@bot.slash_command(description="Delete messages")
820
async def purge(
821
interaction: nextcord.Interaction,
822
amount: int = SlashOption(description="Number of messages to delete", min_value=1, max_value=100)
823
):
824
"""Delete messages (requires manage_messages permission)."""
825
deleted = await interaction.channel.purge(limit=amount)
826
await interaction.response.send_message(
827
f"Deleted {len(deleted)} messages.",
828
ephemeral=True
829
)
830
831
# Role-based checks
832
@application_checks.has_any_role("Admin", "Moderator")
833
@bot.slash_command(description="Admin only command")
834
async def admin_command(interaction: nextcord.Interaction):
835
"""Command only available to users with Admin or Moderator role."""
836
await interaction.response.send_message("Admin command executed!", ephemeral=True)
837
838
# Guild-only check
839
@application_checks.guild_only()
840
@bot.slash_command(description="Server only command")
841
async def server_only(interaction: nextcord.Interaction):
842
"""This command can only be used in servers, not DMs."""
843
await interaction.response.send_message(f"Server: {interaction.guild.name}")
844
845
# Owner check
846
@application_checks.is_owner()
847
@bot.slash_command(description="Bot owner only")
848
async def owner_only(interaction: nextcord.Interaction):
849
"""Command only available to the bot owner."""
850
await interaction.response.send_message("Owner command executed!", ephemeral=True)
851
852
# Custom check function
853
def is_in_voice_channel():
854
"""Check if user is in a voice channel."""
855
async def predicate(interaction: nextcord.Interaction) -> bool:
856
if not interaction.user.voice:
857
await interaction.response.send_message(
858
"You must be in a voice channel to use this command!",
859
ephemeral=True
860
)
861
return False
862
return True
863
return application_checks.check(predicate)
864
865
@is_in_voice_channel()
866
@bot.slash_command(description="Voice channel required")
867
async def voice_command(interaction: nextcord.Interaction):
868
"""Command that requires being in a voice channel."""
869
channel = interaction.user.voice.channel
870
await interaction.response.send_message(f"You're in {channel.name}!")
871
```
872
873
### Custom Check Decorators { .api }
874
875
```python
876
from functools import wraps
877
from nextcord.ext.application_checks import ApplicationCheckFailure
878
879
class NotInMaintenanceMode(ApplicationCheckFailure):
880
"""Custom exception for maintenance mode check."""
881
pass
882
883
def not_in_maintenance():
884
"""Custom check to prevent command usage during maintenance."""
885
def decorator(func):
886
@wraps(func)
887
async def wrapper(interaction: nextcord.Interaction, *args, **kwargs):
888
# Check some maintenance flag (could be from database, config, etc.)
889
maintenance_mode = False # Replace with actual check
890
891
if maintenance_mode:
892
await interaction.response.send_message(
893
"🔧 Bot is currently in maintenance mode. Please try again later.",
894
ephemeral=True
895
)
896
raise NotInMaintenanceMode("Bot is in maintenance mode")
897
898
return await func(interaction, *args, **kwargs)
899
return wrapper
900
return decorator
901
902
def cooldown_check(rate: int, per: float):
903
"""Custom cooldown check for application commands."""
904
def decorator(func):
905
cooldowns = {}
906
907
@wraps(func)
908
async def wrapper(interaction: nextcord.Interaction, *args, **kwargs):
909
import time
910
911
user_id = interaction.user.id
912
current_time = time.time()
913
914
if user_id in cooldowns:
915
time_left = cooldowns[user_id] + per - current_time
916
if time_left > 0:
917
await interaction.response.send_message(
918
f"Command on cooldown. Try again in {time_left:.1f} seconds.",
919
ephemeral=True
920
)
921
return
922
923
cooldowns[user_id] = current_time
924
return await func(interaction, *args, **kwargs)
925
926
return wrapper
927
return decorator
928
929
# Usage of custom checks
930
@not_in_maintenance()
931
@cooldown_check(rate=1, per=30.0) # 1 use per 30 seconds
932
@bot.slash_command(description="Command with custom checks")
933
async def custom_checked_command(interaction: nextcord.Interaction):
934
"""Command with maintenance and cooldown checks."""
935
await interaction.response.send_message("Command executed successfully!")
936
```
937
938
## Error Handling
939
940
Comprehensive error handling for application commands.
941
942
### Error Handler { .api }
943
944
```python
945
@bot.event
946
async def on_application_command_error(interaction: nextcord.Interaction, error: Exception):
947
"""Global error handler for application commands."""
948
949
if isinstance(error, application_checks.MissingPermissions):
950
missing = ", ".join(error.missing_permissions)
951
await interaction.response.send_message(
952
f"❌ You're missing the following permissions: {missing}",
953
ephemeral=True
954
)
955
956
elif isinstance(error, application_checks.MissingAnyRole):
957
roles = ", ".join(error.missing_roles)
958
await interaction.response.send_message(
959
f"❌ You need one of these roles: {roles}",
960
ephemeral=True
961
)
962
963
elif isinstance(error, application_checks.NotOwner):
964
await interaction.response.send_message(
965
"❌ This command is only available to the bot owner.",
966
ephemeral=True
967
)
968
969
elif isinstance(error, application_checks.NoPrivateMessage):
970
await interaction.response.send_message(
971
"❌ This command cannot be used in direct messages.",
972
ephemeral=True
973
)
974
975
elif isinstance(error, nextcord.errors.ApplicationInvokeError):
976
# Handle errors within command execution
977
original_error = error.original
978
979
if isinstance(original_error, nextcord.Forbidden):
980
await interaction.response.send_message(
981
"❌ I don't have permission to perform this action.",
982
ephemeral=True
983
)
984
elif isinstance(original_error, nextcord.NotFound):
985
await interaction.response.send_message(
986
"❌ The requested resource was not found.",
987
ephemeral=True
988
)
989
else:
990
# Log unexpected errors
991
import traceback
992
print(f"Unexpected error in command: {error}")
993
traceback.print_exception(type(original_error), original_error, original_error.__traceback__)
994
995
if not interaction.response.is_done():
996
await interaction.response.send_message(
997
"❌ An unexpected error occurred. Please try again later.",
998
ephemeral=True
999
)
1000
1001
else:
1002
# Handle other unexpected errors
1003
print(f"Unhandled application command error: {error}")
1004
1005
if not interaction.response.is_done():
1006
await interaction.response.send_message(
1007
"❌ An error occurred while processing your command.",
1008
ephemeral=True
1009
)
1010
1011
# Command-specific error handling
1012
@bot.slash_command(description="Command with specific error handling")
1013
async def error_prone_command(
1014
interaction: nextcord.Interaction,
1015
user: nextcord.Member = SlashOption(description="Target user")
1016
):
1017
"""Command that demonstrates error handling."""
1018
try:
1019
# Some operation that might fail
1020
await user.send("Hello from the bot!")
1021
await interaction.response.send_message(f"Message sent to {user.mention}!")
1022
1023
except nextcord.Forbidden:
1024
await interaction.response.send_message(
1025
f"❌ I can't send a DM to {user.mention}. They may have DMs disabled.",
1026
ephemeral=True
1027
)
1028
except nextcord.HTTPException as e:
1029
await interaction.response.send_message(
1030
f"❌ Failed to send message: {e}",
1031
ephemeral=True
1032
)
1033
except Exception as e:
1034
await interaction.response.send_message(
1035
"❌ An unexpected error occurred.",
1036
ephemeral=True
1037
)
1038
# Log the error for debugging
1039
import traceback
1040
traceback.print_exc()
1041
```
1042
1043
This comprehensive documentation covers all aspects of nextcord's application command system, providing developers with the knowledge needed to implement modern Discord interactions effectively.