@@ -26,6 +26,7 @@ class SlashCommand:
26
26
:ivar auto_register: Whether to register commands automatically.
27
27
:ivar has_listener: Whether discord client has listener add function.
28
28
"""
29
+
29
30
def __init__ (self ,
30
31
client : typing .Union [discord .Client , commands .Bot ],
31
32
auto_register : bool = False ,
@@ -38,7 +39,8 @@ def __init__(self,
38
39
self .auto_register = auto_register
39
40
if self .auto_register :
40
41
self ._discord .loop .create_task (self .register_all_commands ())
41
- if not isinstance (client , commands .Bot ) and not isinstance (client , commands .AutoShardedBot ) and not override_type :
42
+ if not isinstance (client , commands .Bot ) and not isinstance (client ,
43
+ commands .AutoShardedBot ) and not override_type :
42
44
self .logger .info ("Detected discord.Client! Overriding on_socket_response." )
43
45
self ._discord .on_socket_response = self .on_socket_response
44
46
self .has_listener = False
@@ -50,6 +52,9 @@ def remove(self):
50
52
"""
51
53
Removes :func:`on_socket_response` event listener from discord.py Client.
52
54
55
+ .. warning::
56
+ This is deprecated and will be removed soon.
57
+
53
58
.. note::
54
59
This only works if it is :class:`discord.ext.commands.Bot` or
55
60
:class:`discord.ext.commands.AutoShardedBot`.
@@ -58,35 +63,107 @@ def remove(self):
58
63
return
59
64
self ._discord .remove_listener (self .on_socket_response )
60
65
66
+ def get_cog_commands (self , cog : commands .Cog ):
67
+ """
68
+ Gets slash command from :class:`discord.ext.commands.Cog`.
69
+
70
+ :param cog: Cog that has slash commands.
71
+ :type cog: discord.ext.commands.Cog
72
+ """
73
+ func_list = [getattr (cog , x ) for x in dir (cog )]
74
+ res = [x for x in func_list if
75
+ isinstance (x , model .CogCommandObject ) or isinstance (x , model .CogSubcommandObject )]
76
+ for x in res :
77
+ x .cog = cog
78
+ if isinstance (x , model .CogCommandObject ):
79
+ self .commands [x .name ] = x
80
+ else :
81
+ if x .base in self .commands .keys ():
82
+ self .commands [x .base ].allowed_guild_ids += x .allowed_guild_ids
83
+ self .commands [x .base ].has_subcommands = True
84
+ else :
85
+ _cmd = {
86
+ "func" : None ,
87
+ "description" : "No description." ,
88
+ "auto_convert" : {},
89
+ "guild_ids" : x .allowed_guild_ids ,
90
+ "api_options" : [],
91
+ "has_subcommands" : True
92
+ }
93
+ self .commands [x .base ] = model .CommandObject (x .base , _cmd )
94
+ if x .base not in self .subcommands .keys ():
95
+ self .subcommands [x .base ] = {}
96
+ if x .subcommand_group :
97
+ if x .subcommand_group not in self .subcommands :
98
+ self .subcommands [x .base ][x .subcommand_group ] = {}
99
+ self .subcommands [x .base ][x .subcommand_group ][x .name ] = x
100
+ else :
101
+ self .subcommands [x .base ][x .name ] = x
102
+
103
+ def remove_cog_commands (self , cog ):
104
+ """
105
+ Removes slash command from :class:`discord.ext.commands.Cog`.
106
+
107
+ :param cog: Cog that has slash commands.
108
+ :type cog: discord.ext.commands.Cog
109
+ """
110
+ func_list = [getattr (cog , x ) for x in dir (cog )]
111
+ res = [x for x in func_list if
112
+ isinstance (x , model .CogCommandObject ) or isinstance (x , model .CogSubcommandObject )]
113
+ for x in res :
114
+ if isinstance (x , model .CogCommandObject ):
115
+ if x .name not in self .commands .keys ():
116
+ continue # Just in case it is removed due to subcommand.
117
+ if x .name in self .subcommands .keys ():
118
+ self .commands [x .name ].func = None
119
+ continue # Let's remove completely when every subcommand is removed.
120
+ del self .commands [x .name ]
121
+ else :
122
+ if x .base not in self .subcommands .keys ():
123
+ continue # Just in case...
124
+ if x .subcommand_group :
125
+ del self .subcommands [x .base ][x .subcommand_group ][x .name ]
126
+ if not self .subcommands [x .base ][x .subcommand_group ]:
127
+ del self .subcommands [x .base ][x .subcommand_group ]
128
+ else :
129
+ del self .subcommands [x .base ][x .name ]
130
+ if not self .subcommands [x .base ]:
131
+ del self .subcommands [x .base ]
132
+ if x .base in self .commands .keys ():
133
+ if self .commands [x .base ].func :
134
+ self .commands [x .base ].has_subcommands = False
135
+ else :
136
+ del self .commands [x .base ]
137
+
61
138
async def register_all_commands (self ):
62
139
"""
63
140
Registers all slash commands except subcommands to Discord API.\n
64
141
If ``auto_register`` is ``True``, then this will be automatically called.
65
142
"""
66
- await self ._discord .wait_until_ready () # In case commands are still not registered to SlashCommand.
143
+ await self ._discord .wait_until_ready () # In case commands are still not registered to SlashCommand.
67
144
self .logger .info ("Registering commands..." )
68
145
for x in self .commands .keys ():
69
146
selected = self .commands [x ]
70
- if selected [ " has_subcommands" ] and "func" not in selected . keys ( ):
147
+ if selected . has_subcommands and not hasattr ( selected , "invoke" ):
71
148
# Just in case it has subcommands but also has base command.
72
149
# More specific, it will skip if it has subcommands and doesn't have base command coroutine.
73
150
self .logger .debug ("Skipping registering subcommands." )
74
151
continue
75
- if selected [ "guild_ids" ] :
76
- for y in selected [ "guild_ids" ] :
152
+ if selected . allowed_guild_ids :
153
+ for y in selected . allowed_guild_ids :
77
154
await manage_commands .add_slash_command (self ._discord .user .id ,
78
155
self ._discord .http .token ,
79
156
y ,
80
157
x ,
81
- selected [ " description" ] ,
82
- selected [ "api_options" ] )
158
+ selected . description ,
159
+ selected . options )
83
160
else :
84
161
await manage_commands .add_slash_command (self ._discord .user .id ,
85
162
self ._discord .http .token ,
86
163
None ,
87
164
x ,
88
- selected [ " description" ] ,
89
- selected [ "api_options" ] )
165
+ selected . description ,
166
+ selected . options )
90
167
self .logger .info ("Completed registering all commands!" )
91
168
92
169
def add_slash_command (self ,
@@ -129,7 +206,7 @@ def add_slash_command(self,
129
206
"api_options" : options if options else [],
130
207
"has_subcommands" : has_subcommands
131
208
}
132
- self .commands [name ] = _cmd
209
+ self .commands [name ] = model . CommandObject ( name , _cmd )
133
210
self .logger .debug (f"Added command `{ name } `" )
134
211
135
212
def add_subcommand (self ,
@@ -163,7 +240,11 @@ def add_subcommand(self,
163
240
name = cmd .__name__ if not name else name
164
241
name = name .lower ()
165
242
_cmd = {
243
+ "func" : None ,
244
+ "description" : "No description." ,
245
+ "auto_convert" : {},
166
246
"guild_ids" : guild_ids ,
247
+ "api_options" : [],
167
248
"has_subcommands" : True
168
249
}
169
250
_sub = {
@@ -174,18 +255,20 @@ def add_subcommand(self,
174
255
"guild_ids" : guild_ids ,
175
256
}
176
257
if base not in self .commands .keys ():
177
- self .commands [base ] = _cmd
258
+ self .commands [base ] = model . CommandObject ( base , _cmd )
178
259
else :
179
- self .subcommands [base ]["has_subcommands" ] = True
260
+ self .commands [base ].has_subcommands = True
261
+ self .commands [base ].allowed_guild_ids += guild_ids
180
262
if base not in self .subcommands .keys ():
181
263
self .subcommands [base ] = {}
182
264
if subcommand_group :
183
265
if subcommand_group not in self .subcommands [base ].keys ():
184
266
self .subcommands [base ][subcommand_group ] = {}
185
- self .subcommands [base ][subcommand_group ][name ] = _sub
267
+ self .subcommands [base ][subcommand_group ][name ] = model . SubcommandObject ( _sub , base , name , subcommand_group )
186
268
else :
187
- self .subcommands [base ][name ] = _sub
188
- self .logger .debug (f"Added subcommand `{ base } { subcommand_group if subcommand_group else '' } { cmd .__name__ if not name else name } `" )
269
+ self .subcommands [base ][name ] = model .SubcommandObject (_sub , base , name )
270
+ self .logger .debug (
271
+ f"Added subcommand `{ base } { subcommand_group if subcommand_group else '' } { cmd .__name__ if not name else name } `" )
189
272
190
273
def slash (self ,
191
274
* ,
@@ -259,6 +342,7 @@ async def _pick(ctx, choice1, choice2): # Command with 1 or more args.
259
342
def wrapper (cmd ):
260
343
self .add_slash_command (cmd , name , description , auto_convert , guild_ids , options )
261
344
return cmd
345
+
262
346
return wrapper
263
347
264
348
def subcommand (self ,
@@ -311,6 +395,7 @@ async def _group_kick_user(ctx, user):
311
395
def wrapper (cmd ):
312
396
self .add_subcommand (cmd , base , subcommand_group , name , description , auto_convert , guild_ids )
313
397
return cmd
398
+
314
399
return wrapper
315
400
316
401
async def process_options (self , guild : discord .Guild , options : list , auto_convert : dict ) -> list :
@@ -388,18 +473,21 @@ async def on_socket_response(self, msg):
388
473
return
389
474
to_use = msg ["d" ]
390
475
if to_use ["data" ]["name" ] in self .commands .keys ():
391
- selected_cmd = self .commands [to_use ["data" ]["name" ]]
392
476
ctx = model .SlashContext (self .req , to_use , self ._discord , self .logger )
393
- if selected_cmd ["guild_ids" ]:
394
- if ctx .guild .id not in selected_cmd ["guild_ids" ]:
477
+ cmd_name = to_use ["data" ]["name" ]
478
+ if cmd_name not in self .commands .keys () and cmd_name in self .subcommands .keys ():
479
+ return await self .handle_subcommand (ctx , to_use )
480
+ selected_cmd = self .commands [to_use ["data" ]["name" ]]
481
+ if selected_cmd .allowed_guild_ids :
482
+ if ctx .guild .id not in selected_cmd .allowed_guild_ids :
395
483
return
396
- if selected_cmd [ " has_subcommands" ] :
484
+ if selected_cmd . has_subcommands :
397
485
return await self .handle_subcommand (ctx , to_use )
398
- args = await self .process_options (ctx .guild , to_use ["data" ]["options" ], selected_cmd [ " auto_convert" ] ) \
486
+ args = await self .process_options (ctx .guild , to_use ["data" ]["options" ], selected_cmd . auto_convert ) \
399
487
if "options" in to_use ["data" ] else []
400
488
self ._discord .dispatch ("slash_command" , ctx )
401
489
try :
402
- await selected_cmd [ "func" ] (ctx , * args )
490
+ await selected_cmd . invoke (ctx , * args )
403
491
except Exception as ex :
404
492
await self .on_slash_command_error (ctx , ex )
405
493
@@ -429,20 +517,20 @@ async def handle_subcommand(self, ctx: model.SlashContext, data: dict):
429
517
return
430
518
ctx .subcommand_group = sub_group
431
519
selected = base [sub_name ][sub_group ]
432
- args = await self .process_options (ctx .guild , x ["options" ], selected [ " auto_convert" ] ) \
520
+ args = await self .process_options (ctx .guild , x ["options" ], selected . auto_convert ) \
433
521
if "options" in x .keys () else []
434
522
self ._discord .dispatch ("slash_command" , ctx )
435
523
try :
436
- await selected [ "func" ] (ctx , * args )
524
+ await selected . invoke (ctx , * args )
437
525
except Exception as ex :
438
526
await self .on_slash_command_error (ctx , ex )
439
527
return
440
528
selected = base [sub_name ]
441
- args = await self .process_options (ctx .guild , sub_opts , selected [ " auto_convert" ] ) \
529
+ args = await self .process_options (ctx .guild , sub_opts , selected . auto_convert ) \
442
530
if "options" in sub .keys () else []
443
531
self ._discord .dispatch ("slash_command" , ctx )
444
532
try :
445
- await selected [ "func" ] (ctx , * args )
533
+ await selected . invoke (ctx , * args )
446
534
except Exception as ex :
447
535
await self .on_slash_command_error (ctx , ex )
448
536
0 commit comments