0
# Command Framework
1
2
Traditional message-based command framework with comprehensive command handling, argument parsing, permission checks, cooldowns, error handling, and organized command structure through cogs for text-based bot functionality.
3
4
## Capabilities
5
6
### Bot Classes
7
8
Command-enabled bot classes extending basic client functionality with message command processing.
9
10
```python { .api }
11
class Bot(commands.BotBase, Client):
12
def __init__(
13
self,
14
command_prefix: Union[str, Iterable[str], Callable[[Bot, Message], Union[str, Iterable[str]]]],
15
*,
16
help_command: Optional[commands.HelpCommand] = ...,
17
description: Optional[str] = None,
18
**options
19
):
20
"""
21
Initialize a message command bot.
22
23
Parameters:
24
- command_prefix: Command prefix(es) or callable returning prefix(es)
25
- help_command: Custom help command implementation
26
- description: Bot description for help command
27
- options: Additional client options
28
"""
29
30
@property
31
def commands(self) -> Set[commands.Command]:
32
"""All registered commands."""
33
34
@property
35
def cogs(self) -> Mapping[str, commands.Cog]:
36
"""All loaded cogs."""
37
38
def add_command(self, command: commands.Command) -> None:
39
"""
40
Add a command to the bot.
41
42
Parameters:
43
- command: Command to add
44
"""
45
46
def remove_command(self, name: str) -> Optional[commands.Command]:
47
"""
48
Remove a command by name.
49
50
Parameters:
51
- name: Command name
52
53
Returns:
54
Removed command if found
55
"""
56
57
def get_command(self, name: str) -> Optional[commands.Command]:
58
"""
59
Get a command by name.
60
61
Parameters:
62
- name: Command name (supports subcommands with spaces)
63
64
Returns:
65
Command if found
66
"""
67
68
def walk_commands(self) -> Generator[commands.Command, None, None]:
69
"""
70
Iterate over all commands including subcommands.
71
72
Yields:
73
All registered commands
74
"""
75
76
async def get_prefix(self, message: Message) -> Union[List[str], str]:
77
"""
78
Get command prefix for a message.
79
80
Parameters:
81
- message: Message to get prefix for
82
83
Returns:
84
Prefix or list of prefixes
85
"""
86
87
async def get_context(self, message: Message, *, cls: Type[Context] = None) -> Context:
88
"""
89
Get command context from a message.
90
91
Parameters:
92
- message: Message to create context from
93
- cls: Custom context class
94
95
Returns:
96
Command context
97
"""
98
99
async def process_commands(self, message: Message) -> None:
100
"""
101
Process a message for commands.
102
103
Parameters:
104
- message: Message to process
105
"""
106
107
async def invoke(self, ctx: commands.Context) -> None:
108
"""
109
Invoke a command from context.
110
111
Parameters:
112
- ctx: Command context
113
"""
114
115
def command(self, name: str = None, **kwargs):
116
"""
117
Decorator for registering commands.
118
119
Parameters:
120
- name: Command name (optional)
121
- kwargs: Command options
122
"""
123
124
def group(self, name: str = None, **kwargs):
125
"""
126
Decorator for registering command groups.
127
128
Parameters:
129
- name: Group name (optional)
130
- kwargs: Group options
131
"""
132
133
def listen(self, name: str = None):
134
"""
135
Decorator for event listeners.
136
137
Parameters:
138
- name: Event name (optional)
139
"""
140
141
def add_cog(self, cog: commands.Cog, *, override: bool = False) -> None:
142
"""
143
Add a cog to the bot.
144
145
Parameters:
146
- cog: Cog instance to add
147
- override: Whether to override existing commands
148
"""
149
150
def remove_cog(self, name: str) -> Optional[commands.Cog]:
151
"""
152
Remove a cog by name.
153
154
Parameters:
155
- name: Cog name
156
157
Returns:
158
Removed cog if found
159
"""
160
161
def get_cog(self, name: str) -> Optional[commands.Cog]:
162
"""
163
Get a cog by name.
164
165
Parameters:
166
- name: Cog name
167
168
Returns:
169
Cog if found
170
"""
171
172
async def load_extension(self, name: str, *, package: Optional[str] = None) -> None:
173
"""
174
Load an extension module.
175
176
Parameters:
177
- name: Extension module name
178
- package: Package name for relative imports
179
"""
180
181
async def unload_extension(self, name: str, *, package: Optional[str] = None) -> None:
182
"""
183
Unload an extension module.
184
185
Parameters:
186
- name: Extension module name
187
- package: Package name for relative imports
188
"""
189
190
async def reload_extension(self, name: str, *, package: Optional[str] = None) -> None:
191
"""
192
Reload an extension module.
193
194
Parameters:
195
- name: Extension module name
196
- package: Package name for relative imports
197
"""
198
199
class AutoShardedBot(commands.AutoShardedBotBase, Bot):
200
"""Auto-sharded message command bot."""
201
202
def __init__(self, command_prefix, **options):
203
"""Initialize auto-sharded command bot."""
204
```
205
206
### Command Objects
207
208
Command representation and execution with parameter handling and validation.
209
210
```python { .api }
211
class Command:
212
def __init__(
213
self,
214
func: Callable,
215
*,
216
name: str = None,
217
aliases: List[str] = None,
218
help: str = None,
219
brief: str = None,
220
usage: str = None,
221
description: str = None,
222
enabled: bool = True,
223
hidden: bool = False,
224
ignore_extra: bool = True,
225
cooldown_after_parsing: bool = False,
226
extras: Dict[str, Any] = None,
227
**kwargs
228
):
229
"""
230
Initialize a command.
231
232
Parameters:
233
- func: Command callback function
234
- name: Command name
235
- aliases: Command aliases
236
- help: Help text
237
- brief: Brief description
238
- usage: Usage syntax
239
- description: Long description
240
- enabled: Whether command is enabled
241
- hidden: Whether to hide from help
242
- ignore_extra: Ignore extra arguments
243
- cooldown_after_parsing: Apply cooldown after parsing
244
- extras: Extra metadata
245
"""
246
247
name: str
248
callback: Callable
249
help: Optional[str]
250
brief: Optional[str]
251
usage: Optional[str]
252
aliases: List[str]
253
enabled: bool
254
hidden: bool
255
checks: List[Check]
256
description: Optional[str]
257
signature: str
258
extras: Dict[str, Any]
259
260
@property
261
def cog(self) -> Optional[Cog]:
262
"""Cog the command belongs to."""
263
264
@property
265
def short_doc(self) -> Optional[str]:
266
"""Short documentation string."""
267
268
@property
269
def signature(self) -> str:
270
"""Command signature with parameters."""
271
272
def can_run(self, ctx: Context) -> bool:
273
"""
274
Check if command can be run in context.
275
276
Parameters:
277
- ctx: Command context
278
279
Returns:
280
True if command can run
281
"""
282
283
async def invoke(self, ctx: Context) -> None:
284
"""
285
Invoke the command.
286
287
Parameters:
288
- ctx: Command context
289
"""
290
291
async def reinvoke(self, ctx: Context, *, call_hooks: bool = False) -> None:
292
"""
293
Reinvoke the command bypassing cooldowns.
294
295
Parameters:
296
- ctx: Command context
297
- call_hooks: Whether to call before/after hooks
298
"""
299
300
def error(self, coro):
301
"""
302
Decorator for command error handlers.
303
304
Parameters:
305
- coro: Error handler coroutine
306
"""
307
308
def before_invoke(self, coro):
309
"""
310
Decorator for pre-command hooks.
311
312
Parameters:
313
- coro: Hook coroutine
314
"""
315
316
def after_invoke(self, coro):
317
"""
318
Decorator for post-command hooks.
319
320
Parameters:
321
- coro: Hook coroutine
322
"""
323
324
class Group(Command):
325
"""Command group container for subcommands."""
326
327
def __init__(
328
self,
329
*args,
330
invoke_without_subcommand: bool = False,
331
case_insensitive: bool = False,
332
**kwargs
333
):
334
"""
335
Initialize a command group.
336
337
Parameters:
338
- invoke_without_subcommand: Allow invoking group without subcommand
339
- case_insensitive: Case insensitive subcommand matching
340
"""
341
342
@property
343
def commands(self) -> Set[Command]:
344
"""Subcommands in this group."""
345
346
def add_command(self, command: Command) -> None:
347
"""
348
Add a subcommand.
349
350
Parameters:
351
- command: Subcommand to add
352
"""
353
354
def remove_command(self, name: str) -> Optional[Command]:
355
"""
356
Remove a subcommand.
357
358
Parameters:
359
- name: Subcommand name
360
361
Returns:
362
Removed command if found
363
"""
364
365
def get_command(self, name: str) -> Optional[Command]:
366
"""
367
Get a subcommand.
368
369
Parameters:
370
- name: Subcommand name
371
372
Returns:
373
Command if found
374
"""
375
376
def walk_commands(self) -> Generator[Command, None, None]:
377
"""
378
Walk all commands in group recursively.
379
380
Yields:
381
All commands and subcommands
382
"""
383
384
def command(self, *args, **kwargs):
385
"""Decorator for adding subcommands."""
386
387
def group(self, *args, **kwargs):
388
"""Decorator for adding subgroups."""
389
```
390
391
### Context Objects
392
393
Command invocation context providing access to message, channel, guild, and bot state.
394
395
```python { .api }
396
class Context:
397
def __init__(
398
self,
399
*,
400
message: Message,
401
bot: Bot,
402
prefix: str = None,
403
command: Command = None,
404
invoked_with: str = None,
405
invoked_parents: List[str] = None,
406
invoked_subcommand: Command = None,
407
subcommand_passed: str = None,
408
command_failed: bool = False,
409
current_parameter: Parameter = None
410
):
411
"""
412
Initialize command context.
413
414
Parameters:
415
- message: Message that triggered command
416
- bot: Bot instance
417
- prefix: Used command prefix
418
- command: Invoked command
419
- invoked_with: Command name used for invocation
420
- invoked_parents: Parent command names
421
- invoked_subcommand: Invoked subcommand
422
- subcommand_passed: Subcommand argument
423
- command_failed: Whether command execution failed
424
- current_parameter: Currently parsing parameter
425
"""
426
427
message: Message
428
bot: Bot
429
command: Optional[Command]
430
invoked_with: Optional[str]
431
invoked_parents: List[str]
432
invoked_subcommand: Optional[Command]
433
subcommand_passed: Optional[str]
434
command_failed: bool
435
prefix: Optional[str]
436
args: List[Any]
437
kwargs: Dict[str, Any]
438
439
@property
440
def author(self) -> Union[User, Member]:
441
"""Message author."""
442
443
@property
444
def guild(self) -> Optional[Guild]:
445
"""Guild where command was invoked."""
446
447
@property
448
def channel(self) -> Messageable:
449
"""Channel where command was invoked."""
450
451
@property
452
def me(self) -> Union[Member, ClientUser]:
453
"""Bot's member object in guild."""
454
455
@property
456
def voice_client(self) -> Optional[VoiceProtocol]:
457
"""Voice client for the guild."""
458
459
@property
460
def valid(self) -> bool:
461
"""Whether context is valid for command invocation."""
462
463
@property
464
def clean_prefix(self) -> str:
465
"""Cleaned command prefix."""
466
467
@property
468
def cog(self) -> Optional[Cog]:
469
"""Cog of the invoked command."""
470
471
async def invoke(
472
self,
473
command: Command,
474
*args,
475
**kwargs
476
) -> None:
477
"""
478
Invoke another command.
479
480
Parameters:
481
- command: Command to invoke
482
- args: Command arguments
483
- kwargs: Command keyword arguments
484
"""
485
486
async def reinvoke(self, *, call_hooks: bool = False, restart: bool = True) -> None:
487
"""
488
Reinvoke the current command.
489
490
Parameters:
491
- call_hooks: Whether to call before/after hooks
492
- restart: Whether to restart from beginning
493
"""
494
495
async def send(
496
self,
497
content: str = None,
498
*,
499
tts: bool = False,
500
embed: Embed = None,
501
embeds: List[Embed] = None,
502
file: File = None,
503
files: List[File] = None,
504
allowed_mentions: AllowedMentions = None,
505
reference: Union[Message, MessageReference, PartialMessage] = None,
506
mention_author: bool = None,
507
view: View = None,
508
components: Union[ActionRow, List[ActionRow]] = None,
509
delete_after: float = None,
510
suppress_embeds: bool = False,
511
flags: MessageFlags = None,
512
ephemeral: bool = False
513
) -> Message:
514
"""
515
Send a message to the context channel.
516
517
Parameters:
518
- content: Message content
519
- tts: Text-to-speech
520
- embed: Single embed
521
- embeds: List of embeds
522
- file: Single file
523
- files: List of files
524
- allowed_mentions: Mention configuration
525
- reference: Message to reply to
526
- mention_author: Whether to mention author in reply
527
- view: UI components view
528
- components: Raw components
529
- delete_after: Auto-delete after seconds
530
- suppress_embeds: Suppress embeds
531
- flags: Message flags
532
- ephemeral: Send as ephemeral (slash command contexts only)
533
534
Returns:
535
Sent message
536
"""
537
538
async def reply(self, content: str = None, **kwargs) -> Message:
539
"""
540
Reply to the context message.
541
542
Parameters:
543
- content: Reply content
544
- kwargs: Additional send parameters
545
546
Returns:
547
Sent reply message
548
"""
549
550
def typing(self) -> Typing:
551
"""
552
Context manager for typing indicator.
553
554
Returns:
555
Typing context manager
556
"""
557
558
def history(self, **kwargs) -> AsyncIterator[Message]:
559
"""
560
Get channel message history.
561
562
Parameters:
563
- kwargs: History parameters
564
565
Yields:
566
Messages from channel history
567
"""
568
569
async def fetch_message(self, id: int) -> Message:
570
"""
571
Fetch a message by ID from context channel.
572
573
Parameters:
574
- id: Message ID
575
576
Returns:
577
Message object
578
"""
579
580
def get_parameter(self, name: str) -> Optional[Parameter]:
581
"""
582
Get command parameter by name.
583
584
Parameters:
585
- name: Parameter name
586
587
Returns:
588
Parameter if found
589
"""
590
591
class GuildContext(Context):
592
"""Guild-specific context with additional guild properties."""
593
594
author: Member
595
guild: Guild
596
me: Member
597
598
@property
599
def permissions(self) -> Permissions:
600
"""Author's permissions in channel."""
601
602
@property
603
def bot_permissions(self) -> Permissions:
604
"""Bot's permissions in channel."""
605
```
606
607
### Cog System
608
609
Modular command organization system for grouping related functionality.
610
611
```python { .api }
612
class Cog:
613
"""Base class for command cogs."""
614
615
def __init_subclass__(cls, **kwargs):
616
"""Initialize cog subclass."""
617
618
@classmethod
619
def listener(cls, name: str = None):
620
"""
621
Decorator for event listeners in cogs.
622
623
Parameters:
624
- name: Event name (optional)
625
"""
626
627
def get_commands(self) -> List[Command]:
628
"""
629
Get all commands in this cog.
630
631
Returns:
632
List of cog commands
633
"""
634
635
def walk_commands(self) -> Generator[Command, None, None]:
636
"""
637
Walk all commands in cog.
638
639
Yields:
640
All cog commands
641
"""
642
643
def get_listeners(self) -> List[Tuple[str, Callable]]:
644
"""
645
Get all event listeners in this cog.
646
647
Returns:
648
List of (event_name, callback) tuples
649
"""
650
651
@property
652
def qualified_name(self) -> str:
653
"""Qualified cog name."""
654
655
@property
656
def description(self) -> Optional[str]:
657
"""Cog description."""
658
659
def cog_load(self) -> None:
660
"""Called when cog is loaded."""
661
662
def cog_unload(self) -> None:
663
"""Called when cog is unloaded."""
664
665
async def cog_check(self, ctx: Context) -> bool:
666
"""
667
Global check for all commands in cog.
668
669
Parameters:
670
- ctx: Command context
671
672
Returns:
673
True if commands can run
674
"""
675
676
async def cog_command_error(self, ctx: Context, error: Exception) -> None:
677
"""
678
Error handler for cog commands.
679
680
Parameters:
681
- ctx: Command context
682
- error: Exception raised
683
"""
684
685
async def cog_before_invoke(self, ctx: Context) -> None:
686
"""
687
Called before any cog command is invoked.
688
689
Parameters:
690
- ctx: Command context
691
"""
692
693
async def cog_after_invoke(self, ctx: Context) -> None:
694
"""
695
Called after any cog command is invoked.
696
697
Parameters:
698
- ctx: Command context
699
"""
700
701
def command(name: str = None, **attrs):
702
"""
703
Decorator for creating commands.
704
705
Parameters:
706
- name: Command name
707
- attrs: Command attributes
708
"""
709
710
def group(name: str = None, **attrs):
711
"""
712
Decorator for creating command groups.
713
714
Parameters:
715
- name: Group name
716
- attrs: Group attributes
717
"""
718
719
async def setup(bot: Bot):
720
"""
721
Setup function for extension modules.
722
723
Parameters:
724
- bot: Bot instance
725
"""
726
```
727
728
### Parameter System
729
730
Advanced parameter parsing, type conversion, and validation for command arguments.
731
732
```python { .api }
733
class Parameter:
734
"""Command parameter information."""
735
736
def __init__(
737
self,
738
name: str,
739
kind: inspect.Parameter.kind,
740
*,
741
default: Any = inspect.Parameter.empty,
742
annotation: Any = inspect.Parameter.empty,
743
converter: Converter = None,
744
description: str = None,
745
displayed_default: str = None
746
):
747
"""
748
Initialize parameter.
749
750
Parameters:
751
- name: Parameter name
752
- kind: Parameter kind
753
- default: Default value
754
- annotation: Type annotation
755
- converter: Type converter
756
- description: Parameter description
757
- displayed_default: Default value display
758
"""
759
760
name: str
761
default: Any
762
annotation: Any
763
converter: Converter
764
kind: inspect.Parameter.kind
765
description: Optional[str]
766
displayed_default: Optional[str]
767
768
class Greedy:
769
"""Greedy converter for consuming multiple arguments."""
770
771
def __init__(self, converter: Converter):
772
"""
773
Initialize greedy converter.
774
775
Parameters:
776
- converter: Converter for individual arguments
777
"""
778
779
def param(
780
default: Any = ...,
781
*,
782
converter: Converter = None,
783
description: str = None,
784
displayed_default: str = None,
785
name: str = None
786
) -> Any:
787
"""
788
Configure command parameter.
789
790
Parameters:
791
- default: Default value
792
- converter: Type converter
793
- description: Parameter description
794
- displayed_default: Default display value
795
- name: Parameter name override
796
797
Returns:
798
Configured parameter
799
"""
800
801
# Built-in converters
802
class MemberConverter:
803
"""Convert argument to Member."""
804
805
async def convert(self, ctx: Context, argument: str) -> Member:
806
"""Convert string to Member."""
807
808
class UserConverter:
809
"""Convert argument to User."""
810
811
async def convert(self, ctx: Context, argument: str) -> User:
812
"""Convert string to User."""
813
814
class GuildConverter:
815
"""Convert argument to Guild."""
816
817
async def convert(self, ctx: Context, argument: str) -> Guild:
818
"""Convert string to Guild."""
819
820
class RoleConverter:
821
"""Convert argument to Role."""
822
823
async def convert(self, ctx: Context, argument: str) -> Role:
824
"""Convert string to Role."""
825
826
class TextChannelConverter:
827
"""Convert argument to TextChannel."""
828
829
async def convert(self, ctx: Context, argument: str) -> TextChannel:
830
"""Convert string to TextChannel."""
831
832
class VoiceChannelConverter:
833
"""Convert argument to VoiceChannel."""
834
835
async def convert(self, ctx: Context, argument: str) -> VoiceChannel:
836
"""Convert string to VoiceChannel."""
837
838
class CategoryChannelConverter:
839
"""Convert argument to CategoryChannel."""
840
841
async def convert(self, ctx: Context, argument: str) -> CategoryChannel:
842
"""Convert string to CategoryChannel."""
843
844
class EmojiConverter:
845
"""Convert argument to Emoji."""
846
847
async def convert(self, ctx: Context, argument: str) -> Emoji:
848
"""Convert string to Emoji."""
849
850
class PartialEmojiConverter:
851
"""Convert argument to PartialEmoji."""
852
853
async def convert(self, ctx: Context, argument: str) -> PartialEmoji:
854
"""Convert string to PartialEmoji."""
855
856
class ColourConverter:
857
"""Convert argument to Colour."""
858
859
async def convert(self, ctx: Context, argument: str) -> Colour:
860
"""Convert string to Colour."""
861
862
class MessageConverter:
863
"""Convert argument to Message."""
864
865
async def convert(self, ctx: Context, argument: str) -> Message:
866
"""Convert string to Message."""
867
868
class GameConverter:
869
"""Convert argument to Game activity."""
870
871
async def convert(self, ctx: Context, argument: str) -> Game:
872
"""Convert string to Game."""
873
874
class InviteConverter:
875
"""Convert argument to Invite."""
876
877
async def convert(self, ctx: Context, argument: str) -> Invite:
878
"""Convert string to Invite."""
879
880
class GuildStickerConverter:
881
"""Convert argument to GuildSticker."""
882
883
async def convert(self, ctx: Context, argument: str) -> GuildSticker:
884
"""Convert string to GuildSticker."""
885
886
class ScheduledEventConverter:
887
"""Convert argument to GuildScheduledEvent."""
888
889
async def convert(self, ctx: Context, argument: str) -> GuildScheduledEvent:
890
"""Convert string to GuildScheduledEvent."""
891
892
class ThreadConverter:
893
"""Convert argument to Thread."""
894
895
async def convert(self, ctx: Context, argument: str) -> Thread:
896
"""Convert string to Thread."""
897
898
# Union converters
899
typing.Union[Member, User] # Tries Member first, then User
900
typing.Optional[Member] # Optional Member (can be None)
901
typing.Literal['option1', 'option2'] # Literal choices
902
```
903
904
### Checks and Permissions
905
906
Permission checking system for command access control and security.
907
908
```python { .api }
909
def check(predicate: Callable[[Context], bool]):
910
"""
911
Decorator for custom permission checks.
912
913
Parameters:
914
- predicate: Check function
915
"""
916
917
def check_any(*checks: Check):
918
"""
919
Decorator requiring any of the provided checks to pass.
920
921
Parameters:
922
- checks: Check decorators
923
"""
924
925
def has_role(name: Union[str, int]):
926
"""
927
Check if user has specific role.
928
929
Parameters:
930
- name: Role name or ID
931
"""
932
933
def has_any_role(*names: Union[str, int]):
934
"""
935
Check if user has any of the specified roles.
936
937
Parameters:
938
- names: Role names or IDs
939
"""
940
941
def has_permissions(**perms: bool):
942
"""
943
Check if user has specific permissions.
944
945
Parameters:
946
- perms: Permission flags
947
"""
948
949
def has_guild_permissions(**perms: bool):
950
"""
951
Check if user has guild permissions.
952
953
Parameters:
954
- perms: Permission flags
955
"""
956
957
def bot_has_permissions(**perms: bool):
958
"""
959
Check if bot has specific permissions.
960
961
Parameters:
962
- perms: Permission flags
963
"""
964
965
def bot_has_guild_permissions(**perms: bool):
966
"""
967
Check if bot has guild permissions.
968
969
Parameters:
970
- perms: Permission flags
971
"""
972
973
def is_owner():
974
"""Check if user is bot owner."""
975
976
def is_nsfw():
977
"""Check if channel is NSFW."""
978
979
def guild_only():
980
"""Check if command is in a guild."""
981
982
def dm_only():
983
"""Check if command is in DMs."""
984
985
def cooldown(rate: int, per: float, type: BucketType = BucketType.default):
986
"""
987
Apply cooldown to command.
988
989
Parameters:
990
- rate: Number of uses allowed
991
- per: Time period in seconds
992
- type: Cooldown bucket type
993
"""
994
995
def dynamic_cooldown(cooldown: Callable[[Context], Optional[Cooldown]], type: BucketType = BucketType.default):
996
"""
997
Apply dynamic cooldown to command.
998
999
Parameters:
1000
- cooldown: Function returning cooldown configuration
1001
- type: Cooldown bucket type
1002
"""
1003
1004
def max_concurrency(number: int, *, per: BucketType = BucketType.default, wait: bool = False):
1005
"""
1006
Limit concurrent command usage.
1007
1008
Parameters:
1009
- number: Maximum concurrent uses
1010
- per: Concurrency bucket type
1011
- wait: Whether to wait for slot
1012
"""
1013
1014
class BucketType(enum.Enum):
1015
"""Cooldown bucket types."""
1016
1017
default = 0
1018
user = 1
1019
guild = 2
1020
channel = 3
1021
member = 4
1022
category = 5
1023
role = 6
1024
```
1025
1026
### Help System
1027
1028
Configurable help command system for documenting bot commands and usage.
1029
1030
```python { .api }
1031
class HelpCommand:
1032
"""Base class for help commands."""
1033
1034
def __init__(
1035
self,
1036
*,
1037
verify_checks: bool = True,
1038
command_attrs: Dict[str, Any] = None,
1039
sort_commands: bool = True,
1040
show_hidden: bool = False,
1041
dm_help: Optional[bool] = None,
1042
dm_help_threshold: Optional[int] = 1000
1043
):
1044
"""
1045
Initialize help command.
1046
1047
Parameters:
1048
- verify_checks: Check command permissions
1049
- command_attrs: Help command attributes
1050
- sort_commands: Sort commands alphabetically
1051
- show_hidden: Show hidden commands
1052
- dm_help: Send help in DMs
1053
- dm_help_threshold: Character threshold for DMing
1054
"""
1055
1056
context: Optional[Context]
1057
verify_checks: bool
1058
command_attrs: Dict[str, Any]
1059
sort_commands: bool
1060
show_hidden: bool
1061
dm_help: Optional[bool]
1062
dm_help_threshold: Optional[int]
1063
1064
def add_indented_commands(
1065
self,
1066
commands: List[Command],
1067
*,
1068
heading: str,
1069
max_size: Optional[int] = None
1070
) -> None:
1071
"""
1072
Add indented command list to paginator.
1073
1074
Parameters:
1075
- commands: Commands to add
1076
- heading: Section heading
1077
- max_size: Maximum name length
1078
"""
1079
1080
def get_opening_note(self) -> str:
1081
"""
1082
Get help opening note.
1083
1084
Returns:
1085
Opening note text
1086
"""
1087
1088
def get_ending_note(self) -> str:
1089
"""
1090
Get help ending note.
1091
1092
Returns:
1093
Ending note text
1094
"""
1095
1096
def add_bot_commands_formatting(self, commands: List[Command], heading: str) -> None:
1097
"""
1098
Add bot commands to help.
1099
1100
Parameters:
1101
- commands: Commands to format
1102
- heading: Section heading
1103
"""
1104
1105
def add_subcommand_formatting(self, command: Command) -> None:
1106
"""
1107
Add subcommand information.
1108
1109
Parameters:
1110
- command: Subcommand to format
1111
"""
1112
1113
def add_aliases_formatting(self, aliases: List[str]) -> None:
1114
"""
1115
Add command aliases.
1116
1117
Parameters:
1118
- aliases: Command aliases
1119
"""
1120
1121
def add_command_formatting(self, command: Command) -> None:
1122
"""
1123
Add single command information.
1124
1125
Parameters:
1126
- command: Command to format
1127
"""
1128
1129
def get_destination(self) -> Messageable:
1130
"""
1131
Get help destination channel.
1132
1133
Returns:
1134
Destination for help message
1135
"""
1136
1137
async def filter_commands(
1138
self,
1139
commands: Iterable[Command],
1140
*,
1141
sort: bool = False,
1142
key: Optional[Callable[[Command], Any]] = None
1143
) -> List[Command]:
1144
"""
1145
Filter commands based on checks.
1146
1147
Parameters:
1148
- commands: Commands to filter
1149
- sort: Whether to sort commands
1150
- key: Sort key function
1151
1152
Returns:
1153
Filtered command list
1154
"""
1155
1156
def get_max_size(self, commands: List[Command]) -> int:
1157
"""
1158
Get maximum command name length.
1159
1160
Parameters:
1161
- commands: Commands to check
1162
1163
Returns:
1164
Maximum name length
1165
"""
1166
1167
def get_command_signature(self, command: Command) -> str:
1168
"""
1169
Get command signature.
1170
1171
Parameters:
1172
- command: Command to get signature for
1173
1174
Returns:
1175
Command signature string
1176
"""
1177
1178
async def send_bot_help(self, mapping: Dict[Optional[Cog], List[Command]]) -> None:
1179
"""
1180
Send bot-wide help.
1181
1182
Parameters:
1183
- mapping: Cog to commands mapping
1184
"""
1185
1186
async def send_cog_help(self, cog: Cog) -> None:
1187
"""
1188
Send cog-specific help.
1189
1190
Parameters:
1191
- cog: Cog to get help for
1192
"""
1193
1194
async def send_group_help(self, group: Group) -> None:
1195
"""
1196
Send group command help.
1197
1198
Parameters:
1199
- group: Command group
1200
"""
1201
1202
async def send_command_help(self, command: Command) -> None:
1203
"""
1204
Send single command help.
1205
1206
Parameters:
1207
- command: Command to get help for
1208
"""
1209
1210
async def prepare_help_command(self, ctx: Context, command: Optional[str] = None) -> None:
1211
"""
1212
Prepare help command for execution.
1213
1214
Parameters:
1215
- ctx: Command context
1216
- command: Specific command help requested
1217
"""
1218
1219
async def command_not_found(self, string: str) -> str:
1220
"""
1221
Handle command not found.
1222
1223
Parameters:
1224
- string: Command name that wasn't found
1225
1226
Returns:
1227
Error message
1228
"""
1229
1230
async def subcommand_not_found(self, command: Command, string: str) -> str:
1231
"""
1232
Handle subcommand not found.
1233
1234
Parameters:
1235
- command: Parent command
1236
- string: Subcommand name that wasn't found
1237
1238
Returns:
1239
Error message
1240
"""
1241
1242
async def send_error_message(self, error: str) -> None:
1243
"""
1244
Send error message.
1245
1246
Parameters:
1247
- error: Error message to send
1248
"""
1249
1250
async def on_help_command_error(self, ctx: Context, error: CommandError) -> None:
1251
"""
1252
Handle help command errors.
1253
1254
Parameters:
1255
- ctx: Command context
1256
- error: Error that occurred
1257
"""
1258
1259
class DefaultHelpCommand(HelpCommand):
1260
"""Default help command implementation."""
1261
1262
def __init__(
1263
self,
1264
*,
1265
paginator: Optional[Paginator] = None,
1266
**options
1267
):
1268
"""
1269
Initialize default help command.
1270
1271
Parameters:
1272
- paginator: Custom paginator
1273
- options: Base class options
1274
"""
1275
1276
class MinimalHelpCommand(HelpCommand):
1277
"""Minimal help command implementation."""
1278
1279
def __init__(self, **options):
1280
"""Initialize minimal help command."""
1281
1282
class Paginator:
1283
"""Text paginator for long help content."""
1284
1285
def __init__(
1286
self,
1287
prefix: str = '```',
1288
suffix: str = '```',
1289
max_size: int = 2000,
1290
linesep: str = '\n'
1291
):
1292
"""
1293
Initialize paginator.
1294
1295
Parameters:
1296
- prefix: Page prefix
1297
- suffix: Page suffix
1298
- max_size: Maximum page size
1299
- linesep: Line separator
1300
"""
1301
1302
@property
1303
def pages(self) -> List[str]:
1304
"""Paginated pages."""
1305
1306
def add_line(self, line: str = '', *, empty: bool = False) -> None:
1307
"""
1308
Add line to paginator.
1309
1310
Parameters:
1311
- line: Line content
1312
- empty: Whether to add empty line
1313
"""
1314
1315
def close_page(self) -> None:
1316
"""Close current page."""
1317
```
1318
1319
## Usage Examples
1320
1321
### Basic Command Bot Setup
1322
1323
```python
1324
import disnake
1325
from disnake.ext import commands
1326
1327
# Create bot with command prefix
1328
bot = commands.Bot(
1329
command_prefix='!',
1330
description='A helpful bot',
1331
intents=disnake.Intents.all(),
1332
help_command=commands.DefaultHelpCommand(),
1333
case_insensitive=True
1334
)
1335
1336
@bot.event
1337
async def on_ready():
1338
print(f'Bot ready: {bot.user}')
1339
1340
@bot.event
1341
async def on_command_error(ctx, error):
1342
"""Global command error handler."""
1343
if isinstance(error, commands.CommandNotFound):
1344
return # Ignore unknown commands
1345
1346
elif isinstance(error, commands.MissingRequiredArgument):
1347
await ctx.send(f"Missing argument: `{error.param}`")
1348
1349
elif isinstance(error, commands.BadArgument):
1350
await ctx.send(f"Invalid argument: {error}")
1351
1352
elif isinstance(error, commands.CheckFailure):
1353
await ctx.send("You don't have permission to use this command.")
1354
1355
elif isinstance(error, commands.CommandOnCooldown):
1356
await ctx.send(f"Command on cooldown. Try again in {error.retry_after:.2f} seconds.")
1357
1358
else:
1359
print(f'Command error: {error}')
1360
await ctx.send("An error occurred while processing the command.")
1361
1362
# Basic commands
1363
@bot.command()
1364
async def ping(ctx):
1365
"""Check bot latency."""
1366
latency = round(bot.latency * 1000)
1367
await ctx.send(f'Pong! Latency: {latency}ms')
1368
1369
@bot.command()
1370
async def echo(ctx, *, message):
1371
"""Echo a message."""
1372
await ctx.send(message)
1373
1374
@bot.command()
1375
async def userinfo(ctx, user: disnake.User = None):
1376
"""Get information about a user."""
1377
if user is None:
1378
user = ctx.author
1379
1380
embed = disnake.Embed(title=f"User Info: {user}", color=0x00ff00)
1381
embed.set_thumbnail(url=user.display_avatar.url)
1382
embed.add_field(name="ID", value=user.id, inline=True)
1383
embed.add_field(name="Created", value=f"<t:{int(user.created_at.timestamp())}:F>", inline=True)
1384
1385
if isinstance(user, disnake.Member):
1386
embed.add_field(name="Joined", value=f"<t:{int(user.joined_at.timestamp())}:F>", inline=True)
1387
embed.add_field(name="Roles", value=len(user.roles) - 1, inline=True)
1388
1389
await ctx.send(embed=embed)
1390
1391
bot.run('YOUR_BOT_TOKEN')
1392
```
1393
1394
### Command Groups and Subcommands
1395
1396
```python
1397
@bot.group(invoke_without_subcommand=True, case_insensitive=True)
1398
async def config(ctx):
1399
"""Configuration commands."""
1400
if ctx.invoked_subcommand is None:
1401
await ctx.send("Available config options: prefix, welcome, autorole")
1402
1403
@config.command()
1404
@commands.has_permissions(administrator=True)
1405
async def prefix(ctx, new_prefix: str = None):
1406
"""View or change bot prefix."""
1407
if new_prefix is None:
1408
current_prefix = await bot.get_prefix(ctx.message)
1409
await ctx.send(f"Current prefix: `{current_prefix[0]}`")
1410
else:
1411
# In a real bot, save to database
1412
bot.command_prefix = new_prefix
1413
await ctx.send(f"Prefix changed to: `{new_prefix}`")
1414
1415
@config.command()
1416
@commands.has_permissions(manage_guild=True)
1417
async def welcome(ctx, channel: disnake.TextChannel = None):
1418
"""Set welcome channel."""
1419
if channel is None:
1420
await ctx.send("Please specify a channel.")
1421
return
1422
1423
# Save to database in real implementation
1424
await ctx.send(f"Welcome channel set to {channel.mention}")
1425
1426
@bot.group()
1427
@commands.has_permissions(manage_roles=True)
1428
async def role(ctx):
1429
"""Role management commands."""
1430
if ctx.invoked_subcommand is None:
1431
await ctx.send_help(ctx.command)
1432
1433
@role.command()
1434
async def create(ctx, *, name: str):
1435
"""Create a new role."""
1436
role = await ctx.guild.create_role(name=name, reason=f"Created by {ctx.author}")
1437
await ctx.send(f"Created role: {role.mention}")
1438
1439
@role.command()
1440
async def delete(ctx, role: disnake.Role):
1441
"""Delete a role."""
1442
if role >= ctx.author.top_role:
1443
return await ctx.send("You cannot delete this role.")
1444
1445
await role.delete(reason=f"Deleted by {ctx.author}")
1446
await ctx.send(f"Deleted role: {role.name}")
1447
1448
@role.command()
1449
async def give(ctx, member: disnake.Member, role: disnake.Role):
1450
"""Give a role to a member."""
1451
if role >= ctx.author.top_role:
1452
return await ctx.send("You cannot assign this role.")
1453
1454
if role in member.roles:
1455
return await ctx.send(f"{member} already has the {role.name} role.")
1456
1457
await member.add_roles(role, reason=f"Added by {ctx.author}")
1458
await ctx.send(f"Gave {role.name} to {member.mention}")
1459
1460
@role.command()
1461
async def remove(ctx, member: disnake.Member, role: disnake.Role):
1462
"""Remove a role from a member."""
1463
if role >= ctx.author.top_role:
1464
return await ctx.send("You cannot manage this role.")
1465
1466
if role not in member.roles:
1467
return await ctx.send(f"{member} doesn't have the {role.name} role.")
1468
1469
await member.remove_roles(role, reason=f"Removed by {ctx.author}")
1470
await ctx.send(f"Removed {role.name} from {member.mention}")
1471
```
1472
1473
### Advanced Parameter Handling
1474
1475
```python
1476
from typing import Union, Optional, Literal
1477
1478
@bot.command()
1479
async def kick(
1480
ctx,
1481
member: disnake.Member,
1482
*,
1483
reason: Optional[str] = "No reason provided"
1484
):
1485
"""Kick a member from the server."""
1486
if not ctx.author.guild_permissions.kick_members:
1487
return await ctx.send("You don't have permission to kick members.")
1488
1489
if member.top_role >= ctx.author.top_role:
1490
return await ctx.send("You cannot kick this member.")
1491
1492
await member.kick(reason=reason)
1493
await ctx.send(f"Kicked {member} for: {reason}")
1494
1495
@bot.command()
1496
async def ban(
1497
ctx,
1498
user: Union[disnake.Member, disnake.User],
1499
delete_days: Optional[int] = 1,
1500
*,
1501
reason: Optional[str] = "No reason provided"
1502
):
1503
"""Ban a user from the server."""
1504
if not ctx.author.guild_permissions.ban_members:
1505
return await ctx.send("You don't have permission to ban members.")
1506
1507
if isinstance(user, disnake.Member) and user.top_role >= ctx.author.top_role:
1508
return await ctx.send("You cannot ban this member.")
1509
1510
if not 0 <= delete_days <= 7:
1511
return await ctx.send("Delete days must be between 0 and 7.")
1512
1513
await ctx.guild.ban(user, delete_message_days=delete_days, reason=reason)
1514
await ctx.send(f"Banned {user} for: {reason}")
1515
1516
@bot.command()
1517
async def purge(
1518
ctx,
1519
limit: int = 10,
1520
target: Union[disnake.Member, disnake.User, str, None] = None
1521
):
1522
"""Purge messages from channel."""
1523
if not ctx.author.guild_permissions.manage_messages:
1524
return await ctx.send("You don't have permission to purge messages.")
1525
1526
if limit > 100:
1527
return await ctx.send("Cannot purge more than 100 messages at once.")
1528
1529
def message_check(message):
1530
if target is None:
1531
return True
1532
elif isinstance(target, (disnake.Member, disnake.User)):
1533
return message.author == target
1534
elif isinstance(target, str):
1535
return target.lower() in message.content.lower()
1536
return False
1537
1538
deleted = await ctx.channel.purge(limit=limit, check=message_check)
1539
await ctx.send(f"Purged {len(deleted)} messages.", delete_after=5)
1540
1541
@bot.command()
1542
async def remind(
1543
ctx,
1544
time: str,
1545
*,
1546
reminder: str
1547
):
1548
"""Set a reminder (e.g., !remind 1h Take break)."""
1549
import re
1550
from datetime import datetime, timedelta
1551
1552
# Parse time string
1553
time_regex = re.compile(r'(\d+)([smhd])')
1554
matches = time_regex.findall(time.lower())
1555
1556
if not matches:
1557
return await ctx.send("Invalid time format. Use: 1s, 1m, 1h, 1d")
1558
1559
total_seconds = 0
1560
for amount, unit in matches:
1561
amount = int(amount)
1562
if unit == 's':
1563
total_seconds += amount
1564
elif unit == 'm':
1565
total_seconds += amount * 60
1566
elif unit == 'h':
1567
total_seconds += amount * 3600
1568
elif unit == 'd':
1569
total_seconds += amount * 86400
1570
1571
if total_seconds > 86400 * 30: # 30 days max
1572
return await ctx.send("Reminder cannot be longer than 30 days.")
1573
1574
# Schedule reminder (in real bot, use a task scheduler)
1575
reminder_time = datetime.utcnow() + timedelta(seconds=total_seconds)
1576
1577
await ctx.send(f"Reminder set for <t:{int(reminder_time.timestamp())}:F>")
1578
1579
# Simple sleep for demo (use proper task scheduling in production)
1580
await asyncio.sleep(total_seconds)
1581
1582
try:
1583
await ctx.author.send(f"⏰ Reminder: {reminder}")
1584
except disnake.Forbidden:
1585
await ctx.send(f"⏰ {ctx.author.mention}, reminder: {reminder}")
1586
1587
# Custom converter example
1588
class ColorConverter(commands.Converter):
1589
async def convert(self, ctx, argument):
1590
# Try hex color
1591
if argument.startswith('#'):
1592
try:
1593
return disnake.Color(int(argument[1:], 16))
1594
except ValueError:
1595
pass
1596
1597
# Try color name
1598
try:
1599
return getattr(disnake.Color, argument.lower())()
1600
except AttributeError:
1601
pass
1602
1603
# Try RGB values
1604
if ',' in argument:
1605
try:
1606
r, g, b = map(int, argument.split(','))
1607
return disnake.Color.from_rgb(r, g, b)
1608
except ValueError:
1609
pass
1610
1611
raise commands.BadArgument(f"Could not convert '{argument}' to a color.")
1612
1613
@bot.command()
1614
async def color(ctx, color: ColorConverter):
1615
"""Test color conversion."""
1616
embed = disnake.Embed(title="Color Test", color=color)
1617
embed.add_field(name="RGB", value=f"{color.r}, {color.g}, {color.b}")
1618
embed.add_field(name="Hex", value=f"#{color.value:06x}")
1619
await ctx.send(embed=embed)
1620
1621
# Flag system example
1622
class ModFlags(commands.FlagConverter):
1623
silent: bool = commands.flag(default=False, description="Don't send notification")
1624
reason: Optional[str] = commands.flag(default=None, description="Reason for action")
1625
duration: Optional[str] = commands.flag(default=None, description="Duration for temporary actions")
1626
1627
@bot.command()
1628
async def tempban(ctx, member: disnake.Member, **flags: ModFlags):
1629
"""Temporarily ban a member with flags."""
1630
if not ctx.author.guild_permissions.ban_members:
1631
return await ctx.send("You don't have permission to ban members.")
1632
1633
duration = flags.duration or "1d"
1634
reason = flags.reason or "No reason provided"
1635
silent = flags.silent
1636
1637
# Parse duration and implement temp ban logic
1638
await ctx.send(f"Temp banned {member} for {duration}. Reason: {reason}. Silent: {silent}")
1639
```
1640
1641
### Cog System Implementation
1642
1643
```python
1644
# cogs/moderation.py
1645
class Moderation(commands.Cog):
1646
"""Moderation commands and features."""
1647
1648
def __init__(self, bot):
1649
self.bot = bot
1650
self.muted_members = set() # In production, use database
1651
1652
async def cog_check(self, ctx):
1653
"""Check if user has moderation permissions."""
1654
return ctx.author.guild_permissions.manage_messages
1655
1656
async def cog_command_error(self, ctx, error):
1657
"""Handle cog command errors."""
1658
if isinstance(error, commands.CheckFailure):
1659
await ctx.send("You need moderation permissions to use this command.")
1660
else:
1661
# Re-raise for global handler
1662
raise error
1663
1664
@commands.Cog.listener()
1665
async def on_message(self, message):
1666
"""Monitor messages for auto-moderation."""
1667
if message.author.bot or not message.guild:
1668
return
1669
1670
# Auto-delete messages with too many mentions
1671
if len(message.mentions) > 5:
1672
await message.delete()
1673
await message.channel.send(
1674
f"{message.author.mention}, please don't spam mentions.",
1675
delete_after=5
1676
)
1677
1678
@commands.Cog.listener()
1679
async def on_member_join(self, member):
1680
"""Auto-mute members if they were previously muted."""
1681
if member.id in self.muted_members:
1682
mute_role = disnake.utils.get(member.guild.roles, name="Muted")
1683
if mute_role:
1684
await member.add_roles(mute_role)
1685
1686
@commands.command()
1687
async def warn(self, ctx, member: disnake.Member, *, reason):
1688
"""Warn a member."""
1689
# In production, save to database
1690
embed = disnake.Embed(
1691
title="Member Warned",
1692
description=f"{member.mention} has been warned.",
1693
color=0xffaa00
1694
)
1695
embed.add_field(name="Reason", value=reason)
1696
embed.add_field(name="Moderator", value=ctx.author.mention)
1697
1698
await ctx.send(embed=embed)
1699
1700
try:
1701
await member.send(f"You have been warned in {ctx.guild.name}: {reason}")
1702
except disnake.Forbidden:
1703
pass
1704
1705
@commands.command()
1706
async def mute(self, ctx, member: disnake.Member, duration: str = "10m", *, reason="No reason"):
1707
"""Mute a member."""
1708
mute_role = disnake.utils.get(ctx.guild.roles, name="Muted")
1709
1710
if not mute_role:
1711
# Create mute role
1712
mute_role = await ctx.guild.create_role(
1713
name="Muted",
1714
permissions=disnake.Permissions(send_messages=False, speak=False)
1715
)
1716
1717
# Set permissions for all channels
1718
for channel in ctx.guild.channels:
1719
await channel.set_permissions(
1720
mute_role,
1721
send_messages=False,
1722
speak=False
1723
)
1724
1725
await member.add_roles(mute_role, reason=reason)
1726
self.muted_members.add(member.id)
1727
1728
await ctx.send(f"Muted {member} for {duration}. Reason: {reason}")
1729
1730
# Schedule unmute (simplified - use proper task scheduler)
1731
# parse_duration(duration) would return seconds
1732
# await asyncio.sleep(seconds)
1733
# await member.remove_roles(mute_role)
1734
1735
@commands.command()
1736
async def unmute(self, ctx, member: disnake.Member):
1737
"""Unmute a member."""
1738
mute_role = disnake.utils.get(ctx.guild.roles, name="Muted")
1739
1740
if not mute_role or mute_role not in member.roles:
1741
return await ctx.send("Member is not muted.")
1742
1743
await member.remove_roles(mute_role)
1744
self.muted_members.discard(member.id)
1745
await ctx.send(f"Unmuted {member}")
1746
1747
@commands.group()
1748
async def automod(self, ctx):
1749
"""Auto-moderation settings."""
1750
if ctx.invoked_subcommand is None:
1751
await ctx.send_help(ctx.command)
1752
1753
@automod.command()
1754
async def spam(self, ctx, enabled: bool):
1755
"""Toggle spam protection."""
1756
# Save setting to database
1757
await ctx.send(f"Spam protection {'enabled' if enabled else 'disabled'}.")
1758
1759
# cogs/fun.py
1760
class Fun(commands.Cog):
1761
"""Fun and entertainment commands."""
1762
1763
def __init__(self, bot):
1764
self.bot = bot
1765
1766
@commands.command()
1767
@commands.cooldown(1, 5, commands.BucketType.user)
1768
async def roll(self, ctx, dice: str = "1d6"):
1769
"""Roll dice (e.g., 1d6, 2d20)."""
1770
import random
1771
import re
1772
1773
match = re.match(r'(\d+)d(\d+)', dice)
1774
if not match:
1775
return await ctx.send("Invalid dice format. Use format like: 1d6, 2d20")
1776
1777
count, sides = map(int, match.groups())
1778
1779
if count > 10 or sides > 100:
1780
return await ctx.send("Too many dice or sides!")
1781
1782
rolls = [random.randint(1, sides) for _ in range(count)]
1783
result = sum(rolls)
1784
1785
embed = disnake.Embed(title=f"🎲 Dice Roll: {dice}")
1786
embed.add_field(name="Rolls", value=" + ".join(map(str, rolls)))
1787
embed.add_field(name="Total", value=result)
1788
1789
await ctx.send(embed=embed)
1790
1791
@commands.command()
1792
async def choose(self, ctx, *choices):
1793
"""Choose randomly from options."""
1794
if len(choices) < 2:
1795
return await ctx.send("Please provide at least 2 choices.")
1796
1797
choice = random.choice(choices)
1798
await ctx.send(f"I choose: **{choice}**")
1799
1800
@commands.command()
1801
async def coinflip(self, ctx):
1802
"""Flip a coin."""
1803
result = random.choice(["Heads", "Tails"])
1804
emoji = "🟡" if result == "Heads" else "⚫"
1805
await ctx.send(f"{emoji} **{result}**!")
1806
1807
# cogs/utility.py
1808
class Utility(commands.Cog):
1809
"""Utility commands."""
1810
1811
def __init__(self, bot):
1812
self.bot = bot
1813
1814
@commands.command()
1815
async def serverinfo(self, ctx):
1816
"""Show server information."""
1817
guild = ctx.guild
1818
1819
embed = disnake.Embed(title=guild.name, color=0x00ff00)
1820
embed.set_thumbnail(url=guild.icon.url if guild.icon else None)
1821
1822
embed.add_field(name="Owner", value=guild.owner.mention)
1823
embed.add_field(name="Created", value=f"<t:{int(guild.created_at.timestamp())}:F>")
1824
embed.add_field(name="Members", value=guild.member_count)
1825
embed.add_field(name="Channels", value=len(guild.channels))
1826
embed.add_field(name="Roles", value=len(guild.roles))
1827
embed.add_field(name="Boost Level", value=guild.premium_tier)
1828
1829
await ctx.send(embed=embed)
1830
1831
@commands.command()
1832
async def avatar(self, ctx, user: disnake.User = None):
1833
"""Show user avatar."""
1834
user = user or ctx.author
1835
1836
embed = disnake.Embed(title=f"{user}'s Avatar")
1837
embed.set_image(url=user.display_avatar.url)
1838
await ctx.send(embed=embed)
1839
1840
@commands.command()
1841
async def poll(self, ctx, question, *options):
1842
"""Create a poll with reactions."""
1843
if len(options) > 10:
1844
return await ctx.send("Too many options! Maximum 10.")
1845
1846
if len(options) < 2:
1847
return await ctx.send("Need at least 2 options!")
1848
1849
reactions = ['1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣', '6️⃣', '7️⃣', '8️⃣', '9️⃣', '🔟']
1850
1851
embed = disnake.Embed(title=f"📊 {question}", color=0x0099ff)
1852
1853
for i, option in enumerate(options):
1854
embed.add_field(
1855
name=f"{reactions[i]} Option {i+1}",
1856
value=option,
1857
inline=False
1858
)
1859
1860
message = await ctx.send(embed=embed)
1861
1862
for i in range(len(options)):
1863
await message.add_reaction(reactions[i])
1864
1865
# Load cogs
1866
async def setup(bot):
1867
await bot.add_cog(Moderation(bot))
1868
await bot.add_cog(Fun(bot))
1869
await bot.add_cog(Utility(bot))
1870
1871
# In main bot file
1872
async def load_extensions():
1873
initial_extensions = [
1874
'cogs.moderation',
1875
'cogs.fun',
1876
'cogs.utility'
1877
]
1878
1879
for extension in initial_extensions:
1880
try:
1881
await bot.load_extension(extension)
1882
print(f'Loaded {extension}')
1883
except Exception as e:
1884
print(f'Failed to load {extension}: {e}')
1885
1886
# Extension management commands
1887
@bot.command()
1888
@commands.is_owner()
1889
async def load(ctx, extension):
1890
"""Load an extension."""
1891
try:
1892
await bot.load_extension(f'cogs.{extension}')
1893
await ctx.send(f'✅ Loaded {extension}')
1894
except Exception as e:
1895
await ctx.send(f'❌ Failed to load {extension}: {e}')
1896
1897
@bot.command()
1898
@commands.is_owner()
1899
async def unload(ctx, extension):
1900
"""Unload an extension."""
1901
try:
1902
await bot.unload_extension(f'cogs.{extension}')
1903
await ctx.send(f'✅ Unloaded {extension}')
1904
except Exception as e:
1905
await ctx.send(f'❌ Failed to unload {extension}: {e}')
1906
1907
@bot.command()
1908
@commands.is_owner()
1909
async def reload(ctx, extension):
1910
"""Reload an extension."""
1911
try:
1912
await bot.reload_extension(f'cogs.{extension}')
1913
await ctx.send(f'🔄 Reloaded {extension}')
1914
except Exception as e:
1915
await ctx.send(f'❌ Failed to reload {extension}: {e}')
1916
1917
# Setup hook
1918
async def main():
1919
await load_extensions()
1920
await bot.start('YOUR_BOT_TOKEN')
1921
1922
if __name__ == '__main__':
1923
asyncio.run(main())
1924
```