1
1
import logging
2
2
import typing
3
3
import discord
4
+ from inspect import iscoroutinefunction
4
5
from discord .ext import commands
5
6
from . import http
6
7
from . import model
7
8
from . import error
8
9
from .utils import manage_commands
9
- from inspect import iscoroutinefunction
10
10
11
11
12
12
class SlashCommand :
@@ -90,7 +90,7 @@ def get_cog_commands(self, cog: commands.Cog):
90
90
else :
91
91
_cmd = {
92
92
"func" : None ,
93
- "description" : "No description." ,
93
+ "description" : x . base_desc ,
94
94
"auto_convert" : {},
95
95
"guild_ids" : x .allowed_guild_ids ,
96
96
"api_options" : [],
@@ -150,10 +150,59 @@ async def register_all_commands(self):
150
150
self .logger .info ("Registering commands..." )
151
151
for x in self .commands .keys ():
152
152
selected = self .commands [x ]
153
+ if selected .has_subcommands and hasattr (selected , "invoke" ):
154
+ # Registering both subcommand and command with same base name / name
155
+ # will result in only one type of command being registered,
156
+ # so we will only register subcommands.
157
+ self .logger .warning (f"Detected command name with same subcommand base name! Skipping registering this command: { x } " )
158
+ continue
153
159
if selected .has_subcommands and not hasattr (selected , "invoke" ):
154
- # Just in case it has subcommands but also has base command.
155
- # More specific, it will skip if it has subcommands and doesn't have base command coroutine.
156
- self .logger .debug ("Skipping registering subcommands." )
160
+ tgt = self .subcommands [x ]
161
+ options = []
162
+ for y in tgt .keys ():
163
+ sub = tgt [y ]
164
+ if isinstance (sub , model .SubcommandObject ):
165
+ _dict = {
166
+ "name" : sub .name ,
167
+ "description" : sub .description if sub .description else "No Description." ,
168
+ "type" : 1 ,
169
+ "options" : sub .options if sub .options else []
170
+ }
171
+ options .append (_dict )
172
+ else :
173
+ base_dict = {
174
+ "name" : sub .subcommand_group ,
175
+ "description" : "No Description." ,
176
+ "type" : 2 ,
177
+ "options" : []
178
+ }
179
+ for z in sub .keys ():
180
+ sub_sub = sub [z ]
181
+ _dict = {
182
+ "name" : sub_sub .name ,
183
+ "description" : sub_sub .description if sub_sub .description else "No Description." ,
184
+ "type" : 1 ,
185
+ "options" : sub_sub .options if sub_sub .options else []
186
+ }
187
+ base_dict ["options" ].append (_dict )
188
+ if sub_sub .sub_group_desc :
189
+ base_dict ["description" ] = sub_sub .sub_group_desc
190
+ options .append (base_dict )
191
+ if selected .allowed_guild_ids :
192
+ for y in selected .allowed_guild_ids :
193
+ await manage_commands .add_slash_command (self ._discord .user .id ,
194
+ self ._discord .http .token ,
195
+ y ,
196
+ x ,
197
+ selected .description ,
198
+ options )
199
+ else :
200
+ await manage_commands .add_slash_command (self ._discord .user .id ,
201
+ self ._discord .http .token ,
202
+ None ,
203
+ x ,
204
+ selected .description ,
205
+ options )
157
206
continue
158
207
if selected .allowed_guild_ids :
159
208
for y in selected .allowed_guild_ids :
@@ -260,8 +309,11 @@ def add_subcommand(self,
260
309
subcommand_group = None ,
261
310
name = None ,
262
311
description : str = None ,
312
+ base_desc : str = None ,
313
+ sub_group_desc : str = None ,
263
314
auto_convert : dict = None ,
264
- guild_ids : typing .List [int ] = None ):
315
+ guild_ids : typing .List [int ] = None ,
316
+ options : list = None ):
265
317
"""
266
318
Registers subcommand to SlashCommand.
267
319
@@ -275,10 +327,16 @@ def add_subcommand(self,
275
327
:type name: str
276
328
:param description: Description of the subcommand. Default ``None``.
277
329
:type description: str
330
+ :param base_desc: Description of the base command. Default ``None``.
331
+ :type base_desc: str
332
+ :param sub_group_desc: Description of the subcommand_group. Default ``None``.
333
+ :type sub_group_desc: str
278
334
:param auto_convert: Dictionary of how to convert option values. Default ``None``.
279
335
:type auto_convert: dict
280
336
:param guild_ids: List of guild ID of where the command will be used. Default ``None``, which will be global command.
281
337
:type guild_ids: List[int]
338
+ :param options: Options of the subcommand.
339
+ :type options: list
282
340
"""
283
341
base = base .lower ()
284
342
subcommand_group = subcommand_group .lower () if subcommand_group else subcommand_group
@@ -296,8 +354,11 @@ def add_subcommand(self,
296
354
"func" : cmd ,
297
355
"name" : name ,
298
356
"description" : description ,
357
+ "base_desc" : base_desc ,
358
+ "sub_group_desc" : sub_group_desc ,
299
359
"auto_convert" : auto_convert ,
300
360
"guild_ids" : guild_ids ,
361
+ "api_options" : options if options else []
301
362
}
302
363
if base not in self .commands .keys ():
303
364
self .commands [base ] = model .CommandObject (base , _cmd )
@@ -328,7 +389,7 @@ def slash(self,
328
389
All decorator args must be passed as keyword-only args.\n
329
390
1 arg for command coroutine is required for ctx(:class:`.model.SlashContext`),
330
391
and if your slash command has some args, then those args are also required.\n
331
- All args are passed in order .
392
+ All args must be passed as keyword-args .
332
393
333
394
.. note::
334
395
Role, User, and Channel types are passed as id if you don't set ``auto_convert``, since API doesn't give type of the option for now.\n
@@ -396,11 +457,15 @@ def subcommand(self,
396
457
subcommand_group = None ,
397
458
name = None ,
398
459
description : str = None ,
460
+ base_desc : str = None ,
461
+ sub_group_desc : str = None ,
399
462
auto_convert : dict = None ,
400
- guild_ids : typing .List [int ] = None ):
463
+ guild_ids : typing .List [int ] = None ,
464
+ options : typing .List [dict ] = None ):
401
465
"""
402
466
Decorator that registers subcommand.\n
403
- Unlike discord.py, you don't need base command.
467
+ Unlike discord.py, you don't need base command.\n
468
+ All args must be passed as keyword-args.
404
469
405
470
Example:
406
471
@@ -431,14 +496,28 @@ async def _group_kick_user(ctx, user):
431
496
:type name: str
432
497
:param description: Description of the subcommand. Default ``None``.
433
498
:type description: str
499
+ :param base_desc: Description of the base command. Default ``None``.
500
+ :type base_desc: str
501
+ :param sub_group_desc: Description of the subcommand_group. Default ``None``.
502
+ :type sub_group_desc: str
434
503
:param auto_convert: Dictionary of how to convert option values. Default ``None``.
435
504
:type auto_convert: dict
436
505
:param guild_ids: List of guild ID of where the command will be used. Default ``None``, which will be global command.
437
506
:type guild_ids: List[int]
507
+ :param options: Options of the subcommand. This will affect ``auto_convert`` and command data at Discord API. Default ``None``.
508
+ :type options: List[dict]
438
509
"""
439
510
511
+ if options :
512
+ # Overrides original auto_convert.
513
+ auto_convert = {}
514
+ for x in options :
515
+ if x ["type" ] < 3 :
516
+ raise Exception ("You can't use subcommand or subcommand_group type!" )
517
+ auto_convert [x ["name" ]] = x ["type" ]
518
+
440
519
def wrapper (cmd ):
441
- self .add_subcommand (cmd , base , subcommand_group , name , description , auto_convert , guild_ids )
520
+ self .add_subcommand (cmd , base , subcommand_group , sub_group_desc , name , description , auto_convert , guild_ids , options )
442
521
return cmd
443
522
444
523
return wrapper
0 commit comments