1+ import copy
12import logging
23import typing
34import discord
@@ -104,16 +105,16 @@ def get_cog_commands(self, cog: commands.Cog):
104105 self .commands [x .name ] = x
105106 else :
106107 if x .base in self .commands :
107- for i in self . commands [ x . base ] .allowed_guild_ids :
108- if i not in x .allowed_guild_ids :
109- x .allowed_guild_ids .append (i )
108+ for i in x .allowed_guild_ids :
109+ if i not in self . commands [ x . base ] .allowed_guild_ids :
110+ self . commands [ x . base ] .allowed_guild_ids .append (i )
110111 self .commands [x .base ].has_subcommands = True
111112 else :
112113 _cmd = {
113114 "func" : None ,
114115 "description" : x .base_description ,
115116 "auto_convert" : {},
116- "guild_ids" : x .allowed_guild_ids ,
117+ "guild_ids" : x .allowed_guild_ids . copy () ,
117118 "api_options" : [],
118119 "has_subcommands" : True
119120 }
@@ -188,65 +189,88 @@ async def to_dict(self):
188189 Commands are in the format specified by discord `here <https://discord.com/developers/docs/interactions/slash-commands#applicationcommand>`_
189190 """
190191 await self ._discord .wait_until_ready () # In case commands are still not registered to SlashCommand.
192+ all_guild_ids = []
193+ for x in self .commands :
194+ for i in self .commands [x ].allowed_guild_ids :
195+ if i not in all_guild_ids :
196+ all_guild_ids .append (i )
191197 cmds = {
192198 "global" : [],
193- "guild" : {}
199+ "guild" : {x : [] for x in all_guild_ids }
194200 }
201+ wait = {} # Before merging to return dict, let's first put commands to temporary dict.
195202 for x in self .commands :
196203 selected = self .commands [x ]
197- if selected .has_subcommands and selected .func :
198- # Registering both subcommand and command with same base name / name
199- # will result in only subcommand being registered,
200- # so we will warn this at registering subcommands.
201- self .logger .warning (f"Detected command name with same subcommand base name! "
202- f"This command will only have subcommand: { x } " )
203-
204- options = []
205- if selected .has_subcommands :
206- tgt = self .subcommands [x ]
207- for y in tgt :
208- sub = tgt [y ]
209- if isinstance (sub , model .SubcommandObject ):
210- _dict = {
211- "name" : sub .name ,
212- "description" : sub .description or "No Description." ,
213- "type" : model .SlashCommandOptionType .SUB_COMMAND ,
214- "options" : sub .options or []
215- }
216- options .append (_dict )
217- else :
218- base_dict = {
219- "name" : y ,
220- "description" : "No Description." ,
221- "type" : model .SlashCommandOptionType .SUB_COMMAND_GROUP ,
222- "options" : []
223- }
224- for z in sub :
225- sub_sub = sub [z ]
226- _dict = {
227- "name" : sub_sub .name ,
228- "description" : sub_sub .description or "No Description." ,
229- "type" : model .SlashCommandOptionType .SUB_COMMAND ,
230- "options" : sub_sub .options or []
231- }
232- base_dict ["options" ].append (_dict )
233- if sub_sub .subcommand_group_description :
234- base_dict ["description" ] = sub_sub .subcommand_group_description
235- options .append (base_dict )
236-
237204 command_dict = {
238205 "name" : x ,
239206 "description" : selected .description or "No Description." ,
240- "options" : selected .options if not options else options
207+ "options" : selected .options or []
241208 }
242209 if selected .allowed_guild_ids :
243210 for y in selected .allowed_guild_ids :
244- try :
245- cmds ["guild" ][y ].append (command_dict )
246- except KeyError :
247- cmds ["guild" ][y ] = [command_dict ]
211+ if y not in wait :
212+ wait [y ] = {}
213+ wait [y ][x ] = copy .deepcopy (command_dict )
214+ else :
215+ if "global" not in wait :
216+ wait ["global" ] = {}
217+ wait ["global" ][x ] = copy .deepcopy (command_dict )
218+
219+ # Separated normal command add and subcommand add not to
220+ # merge subcommands to one. More info at Issue #88
221+ # https://github.com/eunwoo1104/discord-py-slash-command/issues/88
222+
223+ for x in self .commands :
224+ if not self .commands [x ].has_subcommands :
225+ continue
226+ tgt = self .subcommands [x ]
227+ for y in tgt :
228+ sub = tgt [y ]
229+ if isinstance (sub , model .SubcommandObject ):
230+ _dict = {
231+ "name" : sub .name ,
232+ "description" : sub .description or "No Description." ,
233+ "type" : model .SlashCommandOptionType .SUB_COMMAND ,
234+ "options" : sub .options or []
235+ }
236+ if sub .allowed_guild_ids :
237+ for z in sub .allowed_guild_ids :
238+ wait [z ][x ]["options" ].append (_dict )
239+ else :
240+ wait ["global" ][x ]["options" ].append (_dict )
241+ else :
242+ queue = {}
243+ base_dict = {
244+ "name" : y ,
245+ "description" : "No Description." ,
246+ "type" : model .SlashCommandOptionType .SUB_COMMAND_GROUP ,
247+ "options" : []
248+ }
249+ for z in sub :
250+ sub_sub = sub [z ]
251+ _dict = {
252+ "name" : sub_sub .name ,
253+ "description" : sub_sub .description or "No Description." ,
254+ "type" : model .SlashCommandOptionType .SUB_COMMAND ,
255+ "options" : sub_sub .options or []
256+ }
257+ if sub_sub .allowed_guild_ids :
258+ for i in sub_sub .allowed_guild_ids :
259+ if i not in queue :
260+ queue [i ] = copy .deepcopy (base_dict )
261+ queue [i ]["options" ].append (_dict )
262+ else :
263+ if "global" not in queue :
264+ queue ["global" ] = copy .deepcopy (base_dict )
265+ queue ["global" ]["options" ].append (_dict )
266+ for i in queue :
267+ wait [i ][x ]["options" ].append (queue [i ])
268+
269+ for x in wait :
270+ if x == "global" :
271+ [cmds ["global" ].append (n ) for n in wait ["global" ].values ()]
248272 else :
249- cmds ["global" ] .append (command_dict )
273+ [ cmds ["guild" ][ x ] .append (n ) for n in wait [ x ]. values ()]
250274
251275 return cmds
252276
@@ -326,8 +350,10 @@ def add_slash_command(self,
326350 "connector" : connector or {},
327351 "has_subcommands" : has_subcommands
328352 }
329- self .commands [name ] = model .CommandObject (name , _cmd )
353+ obj = model .CommandObject (name , _cmd )
354+ self .commands [name ] = obj
330355 self .logger .debug (f"Added command `{ name } `" )
356+ return obj
331357
332358 def add_subcommand (self ,
333359 cmd ,
@@ -371,18 +397,17 @@ def add_subcommand(self,
371397 description = description or getdoc (cmd )
372398
373399 if base in self .commands :
374- tgt = self .commands [base ]
375- for x in tgt .allowed_guild_ids :
376- if x not in guild_ids :
377- guild_ids .append (x )
400+ for x in guild_ids :
401+ if x not in self .commands [base ].allowed_guild_ids :
402+ self .commands [base ].allowed_guild_ids .append (x )
378403
379404 if options is None :
380405 options = manage_commands .generate_options (cmd , description )
381406
382407 _cmd = {
383408 "func" : None ,
384409 "description" : base_description ,
385- "guild_ids" : guild_ids ,
410+ "guild_ids" : guild_ids . copy () ,
386411 "api_options" : [],
387412 "connector" : {},
388413 "has_subcommands" : True
@@ -401,7 +426,6 @@ def add_subcommand(self,
401426 self .commands [base ] = model .CommandObject (base , _cmd )
402427 else :
403428 self .commands [base ].has_subcommands = True
404- self .commands [base ].allowed_guild_ids = guild_ids
405429 if self .commands [base ].description :
406430 _cmd ["description" ] = self .commands [base ].description
407431 if base not in self .subcommands :
@@ -411,12 +435,15 @@ def add_subcommand(self,
411435 self .subcommands [base ][subcommand_group ] = {}
412436 if name in self .subcommands [base ][subcommand_group ]:
413437 raise error .DuplicateCommand (f"{ base } { subcommand_group } { name } " )
414- self .subcommands [base ][subcommand_group ][name ] = model .SubcommandObject (_sub , base , name , subcommand_group )
438+ obj = model .SubcommandObject (_sub , base , name , subcommand_group )
439+ self .subcommands [base ][subcommand_group ][name ] = obj
415440 else :
416441 if name in self .subcommands [base ]:
417442 raise error .DuplicateCommand (f"{ base } { name } " )
418- self .subcommands [base ][name ] = model .SubcommandObject (_sub , base , name )
443+ obj = model .SubcommandObject (_sub , base , name )
444+ self .subcommands [base ][name ] = obj
419445 self .logger .debug (f"Added subcommand `{ base } { subcommand_group or '' } { name or cmd .__name__ } `" )
446+ return obj
420447
421448 def slash (self ,
422449 * ,
@@ -485,8 +512,8 @@ async def _pick(ctx, choice1, choice2): # Command with 1 or more args.
485512 guild_ids = [guild_id ]
486513
487514 def wrapper (cmd ):
488- self .add_slash_command (cmd , name , description , guild_ids , options , connector )
489- return cmd
515+ obj = self .add_slash_command (cmd , name , description , guild_ids , options , connector )
516+ return obj
490517
491518 return wrapper
492519
@@ -557,8 +584,8 @@ async def _group_kick_user(ctx, user):
557584 subcommand_group_description = subcommand_group_description or sub_group_desc
558585
559586 def wrapper (cmd ):
560- self .add_subcommand (cmd , base , subcommand_group , name , description , base_description , subcommand_group_description , guild_ids , options , connector )
561- return cmd
587+ obj = self .add_subcommand (cmd , base , subcommand_group , name , description , base_description , subcommand_group_description , guild_ids , options , connector )
588+ return obj
562589
563590 return wrapper
564591
0 commit comments