GoldyBot.objects.command

  1from __future__ import annotations
  2import asyncio
  3import importlib
  4from typing import Dict, List
  5import nextcord
  6from nextcord.ext import commands
  7
  8import GoldyBot
  9
 10MODULE_NAME = "COMMAND"
 11
 12MISSING = nextcord.utils.MISSING
 13
 14# Embeds
 15#---------
 16class Embeds():
 17    def __init__(self):
 18        self.command_usage_embed = GoldyBot.utility.goldy.embed.Embed(title=GoldyBot.utility.msgs.bot.CommandUsage.Embed.title)
 19        self.command_usage_embed.set_thumbnail(url=GoldyBot.utility.msgs.bot.CommandUsage.Embed.thumbnail)
 20
 21        self.no_perms_embed = GoldyBot.utility.goldy.embed.Embed(title=GoldyBot.utility.msgs.bot.CommandNoPerms.Embed.title)
 22        self.no_perms_embed.color = GoldyBot.utility.msgs.bot.CommandNoPerms.Embed.colour
 23        self.no_perms_embed.set_thumbnail(url=GoldyBot.utility.msgs.bot.CommandNoPerms.Embed.thumbnail)
 24
 25        self.guild_not_registered_embed = GoldyBot.utility.goldy.embed.Embed(title=GoldyBot.utility.msgs.bot.CommandGuildNotRegistered.Embed.title)
 26        self.guild_not_registered_embed.color = GoldyBot.utility.msgs.bot.CommandGuildNotRegistered.Embed.colour
 27        self.guild_not_registered_embed.set_thumbnail(url=GoldyBot.utility.msgs.bot.CommandGuildNotRegistered.Embed.thumbnail)
 28
 29class Command(Embeds):
 30    def __init__(self, func, command_object:nextcord.BaseApplicationCommand | commands.Command=None, command_name=None, 
 31        required_roles:list=[], slash_options:dict={}, help_des:str=None, hidden:bool=False, 
 32        parent_cmd:Command=None
 33    ):
 34        """Generates goldy bot command object with command function object."""
 35        self.func:function = func
 36        self.command = command_object
 37        """Nextcord command object."""
 38
 39        if command_name == None:
 40            self.command_name = self.func.__name__
 41        else:
 42            self.command_name = command_name
 43        self.required_roles_ = required_roles
 44        self.slash_options_ = slash_options
 45        self.params_ = list(self.func.__code__.co_varnames)
 46        self.params_amount_ = self.func.__code__.co_argcount
 47        self.help_des_ = help_des
 48        self.is_hidden_ = hidden
 49
 50        self.parent_cmd_ = parent_cmd
 51        
 52        self.in_extension_ = False
 53
 54        if self.params[0] == "self":
 55            self.in_extension_ = True
 56            self.params_.pop(0)
 57
 58        # Add command to extension in cache.
 59        if self.in_extension:
 60            if self.module.is_internal_module:
 61                GoldyBot.cache.main_cache_dict["internal_modules"][f"{self.module_name}"]["extensions"][f"{self.extension_name}"]["commands"].append(self)
 62            else:
 63                GoldyBot.cache.main_cache_dict["modules"][f"{self.module_name}"]["extensions"][f"{self.extension_name}"]["commands"].append(self)
 64        else:
 65            GoldyBot.cache.main_cache_dict["internal_modules"]["goldy"]["extensions"]["core"]["commands"].append(self)
 66
 67        super().__init__()
 68
 69    @property
 70    def code_name(self) -> str:
 71        """Returns code name of command."""
 72        return self.command_name
 73
 74    @property
 75    def params(self) -> list:
 76        """Returns list of function parameters."""
 77        if self.in_extension:
 78            return self.params_[0:self.params_amount_ - 1]
 79        else:
 80            return self.params_[0:self.params_amount_]
 81
 82    @property
 83    def extension_name(self) -> str | None:
 84        """Returns extension's code name."""
 85        if self.in_extension:
 86            # Command is in extension.
 87            return str(self.func).split(" ")[1].split(".")[0]
 88        else:
 89            # Command is not in any extension.
 90            return None
 91
 92    @property
 93    def extension(self) -> GoldyBot.ext.extensions.Extension | None:
 94        """Finds and returns the object of the command's extension."""
 95        if self.in_extension:
 96            return GoldyBot.cache.FindExtensions().find_object_by_extension_name(extension_name=self.extension_name)
 97        else:
 98            None
 99
100    @property
101    def in_extension(self) -> bool:
102        """Returns true/false if the command is in a extension."""
103        if self.in_extension_:
104            return True
105        else:
106            return False
107
108    @property
109    def module_name(self) -> str:
110        """Returns name of module the command is located in."""
111        return self.extension.module_name
112
113    @property
114    def module(self) -> GoldyBot.modules.Module:
115        return GoldyBot.cache.FindModules().find_object_by_module_name(module_name=self.module_name)
116
117    @property
118    def is_hidden(self):
119        """Is the command hidden."""
120        return self.is_hidden_
121
122    @property
123    def parent_cmd(self) -> Command|None:
124        """Returns the command object of the parent command if command has parent."""
125        return self.parent_cmd_
126
127    @property
128    def is_child(self):
129        """Returns if command is child or not."""
130        if self.parent_cmd == None:
131            return False
132        else:
133            return True
134
135    def mention(self) -> str:
136        """Returns mention of slash command, if registered as a slash command."""
137        #TODO: Perhaps have this return codeblock if this command is not a slash command.
138        try:
139            if self.is_child == False:
140                return f"</{self.code_name}:{self.command.command_ids[0]}>"
141            else:
142                return f"</{self.parent_cmd.code_name} {self.code_name}:{self.command.command_ids[0]}>"
143        except (TypeError, AttributeError):
144            GoldyBot.log("warn", f"[{MODULE_NAME}] Tried to mention slash command '{self.code_name}' but could not find it's command id so I'm returning the mention without it.")
145            
146            if self.is_child == False: return f"</{self.code_name}:0>"
147            else: return f"</{self.parent_cmd.code_name} {self.code_name}:0>"
148
149    def create_slash(self) -> nextcord.BaseApplicationCommand:
150        """Creates slash command."""
151
152        GoldyBot.logging.log(f"[{MODULE_NAME}] [{self.command_name.upper()}] Creating slash command...")
153        slash_command_params = self.slash_commands_params_generator()
154
155        return_data = {}
156
157        #  Make slash command only visible to admins if it is hidden.
158        default_member_permissions = None
159        if self.is_hidden: default_member_permissions = nextcord.Permissions(permissions=8)
160        
161
162        if self.in_extension == True: # Run in EXTENSION!
163            exec(f"""
164@client.slash_command(name=command_name, description=help_des, guild_ids=guilds_allowed_in, default_member_permissions=default_member_permissions)
165async def slash_command_(interaction: Interaction{slash_command_params[0]}):
166    ctx = GoldyBot.objects.slash.InteractionToCtx(interaction)
167    try:
168        if self.allowed_to_run(ctx):
169            await func(class_, ctx{slash_command_params[1]})
170            GoldyBot.logging.log(f"[{MODULE_NAME}] The slash command '{self.code_name}' was executed.")
171
172    except GoldyBot.errors.MemberHasNoPermsForCommand:
173        if hidden == False:
174            no_perms_embed.description = GoldyBot.utility.msgs.bot.CommandNoPerms.Embed.des.format(ctx.author.mention)
175            message = await ctx.send(embed=no_perms_embed)
176            await asyncio.sleep(6)
177            await message.delete()
178
179    except GoldyBot.errors.GuildNotRegistered:
180        if hidden == False:
181            guild_not_registered_embed.description = GoldyBot.utility.msgs.bot.CommandGuildNotRegistered.Embed.des.format(ctx.author.mention)
182            message = await ctx.send(embed=guild_not_registered_embed)
183            await asyncio.sleep(15)
184            message.delete()
185
186    except Exception as e:
187        GoldyBot.logging.log("error", e)
188            """, 
189            
190            {"func":self.func, "client":GoldyBot.cache.main_cache_dict["client"], "command_name":self.command_name, "help_des":self.get_help_des(), "self":self,
191            "Interaction": GoldyBot.nextcord.Interaction, "GoldyBot": GoldyBot, "asyncio":asyncio, "nextcord":nextcord, "class_":self.extension, "no_perms_embed":self.no_perms_embed, 
192            "guild_not_registered_embed":self.guild_not_registered_embed, "hidden":self.is_hidden, "guilds_allowed_in":self.guilds_allowed_in, 
193            "default_member_permissions":default_member_permissions}, return_data)
194            
195        else: # Run as NORMAL command!
196            exec(f"""
197@client.slash_command(name=command_name, description=help_des, guild_ids=guilds_allowed_in, default_member_permissions=default_member_permissions)
198async def slash_command_(interaction: Interaction{slash_command_params[0]}):
199    ctx = GoldyBot.objects.slash.InteractionToCtx(interaction)
200    try:
201        if self.allowed_to_run(ctx):
202            await func(ctx{slash_command_params[1]})
203            GoldyBot.logging.log(f"[{MODULE_NAME}] The slash command '{self.code_name}' was executed.")
204
205    except GoldyBot.errors.MemberHasNoPermsForCommand:
206        if hidden == False:
207            no_perms_embed.description = GoldyBot.utility.msgs.bot.CommandNoPerms.Embed.des.format(ctx.author.mention)
208            message = await ctx.send(embed=no_perms_embed)
209            await asyncio.sleep(15)
210            await message.delete()
211
212    except GoldyBot.errors.GuildNotRegistered:
213        if hidden == False:
214            guild_not_registered_embed.description = GoldyBot.utility.msgs.bot.CommandGuildNotRegistered.Embed.des.format(ctx.author.mention)
215            message = await ctx.send(embed=guild_not_registered_embed)
216            await asyncio.sleep(15)
217            await message.delete()
218
219    except Exception as e:
220        GoldyBot.logging.log("error", e)
221            """, 
222            
223            {"func":self.func, "client":GoldyBot.cache.main_cache_dict["client"], "command_name":self.command_name, "help_des":self.get_help_des(), "self":self,
224            "Interaction": GoldyBot.nextcord.Interaction, "GoldyBot": GoldyBot, "asyncio":asyncio, "nextcord":nextcord, "no_perms_embed":self.no_perms_embed, 
225            "guild_not_registered_embed":self.guild_not_registered_embed, "hidden":self.is_hidden, "guilds_allowed_in":self.guilds_allowed_in, 
226            "default_member_permissions":default_member_permissions}, return_data)
227
228        GoldyBot.logging.log(f"[{MODULE_NAME}] [{self.command_name.upper()}] Slash command created!")
229
230        self.command = return_data["slash_command_"]
231        return return_data["slash_command_"]
232
233    def remove(self):
234        """Removes command from nextcord."""
235        command_application_object = self.command
236
237        # Remove command from extension in cache.
238        if self.in_extension:
239            if self.module.is_internal_module:
240                GoldyBot.cache.main_cache_dict["internal_modules"][f"{self.module_name}"]["extensions"][f"{self.extension_name}"]["commands"].remove(self)
241            else:
242                GoldyBot.cache.main_cache_dict["modules"][f"{self.module_name}"]["extensions"][f"{self.extension_name}"]["commands"].remove(self)
243        else:
244            GoldyBot.cache.main_cache_dict["internal_modules"]["goldy"]["extensions"]["core"]["commands"].remove(self)
245
246        # Remove from nextcord.
247        client = GoldyBot.cache.client()
248        client.remove_command(name=self.code_name)
249
250        for guild_id in self.guilds_allowed_in:
251            print(command_application_object.command_ids) #TODO: Problem here!
252            GoldyBot.async_loop.run_until_complete(client.delete_application_commands(command_application_object, guild_id=guild_id))
253            
254            GoldyBot.cache.main_cache_dict["client"] = client
255
256        GoldyBot.logging.log("info_5", f"[{MODULE_NAME}] Removed the command '{self.code_name}'!")
257        return True
258
259    def any_args_missing(self, command_executers_args:tuple) -> bool:
260        """Checks if the args given by the command executer matches what parameters the command needs."""
261        if len(command_executers_args) == len(self.params[1:]):
262            return True
263        else:
264            return False
265
266    def slash_commands_params_generator(self):
267        """Generates a string of params for slash commands. This is more of an in-house thing."""
268        params_amount = len(self.params[1:])
269        slash_command_params = ""
270        slash_command_args = ""
271
272        if params_amount >= 1:
273            slash_command_params = ", "
274            slash_command_args = ", "
275            count = 0
276            for param in self.params[1:]:
277                count += 1
278
279                try:
280                    # String
281                    if isinstance(self.slash_options_[param.lower()], str):
282                        default_value = f"='{self.slash_options_[param.lower()]}'"
283
284                    # Slash Option
285                    if isinstance(self.slash_options_[param.lower()], nextcord.SlashOption):
286                        slash_option:nextcord.SlashOption = self.slash_options_[param.lower()]
287
288                        if isinstance(slash_option.name, str):
289                            name = f"'{slash_option.name}'"
290                        else:
291                            name = None
292
293                        if isinstance(slash_option.description, str):
294                            description = f"'{slash_option.description}'"
295                        else:
296                            description = None
297
298                        if isinstance(slash_option.required, nextcord.utils._MissingSentinel):
299                            required = None
300                        else:
301                            required = slash_option.required
302
303                        if isinstance(slash_option.choices, nextcord.utils._MissingSentinel):
304                            choices = None
305                        else:
306                            choices = slash_option.choices
307
308                        """
309                        if isinstance(slash_option.min_value, nextcord.utils._MissingSentinel):
310                            min_value = None
311                        else:
312                            min_value = slash_option.min_value
313                            
314                        if isinstance(slash_option.max_value, nextcord.utils._MissingSentinel):
315                            max_value = None
316                        else:
317                            max_value = slash_option.max_value
318                        """
319
320                        if isinstance(slash_option.autocomplete, nextcord.utils._MissingSentinel):
321                            autocomplete = None
322                        else:
323                            autocomplete = slash_option.autocomplete
324
325                        if isinstance(slash_option.default, str):
326                            default = f"'{slash_option.default}'"
327                        else:
328                            default = None
329                        
330                        default_value = f"""=nextcord.SlashOption(name={name}, description={description}, required={required}, 
331                        choices={choices}, autocomplete={autocomplete}, default={default}, verify={slash_option._verify})"""
332                    
333                    else:
334                        default_value = f"={self.slash_options_[param.lower()]}"
335
336                except KeyError:
337                    default_value = ""
338
339                if count >= params_amount:
340                    slash_command_params += f"{param}{default_value}"
341                    slash_command_args += f"{param}"
342                else:
343                    slash_command_params += f"{param}{default_value}, "
344                    slash_command_args += f"{param}, "
345
346        return (slash_command_params, slash_command_args)
347
348    def update_command_object(self, command_object:commands.Command):
349        """Adds/updates this command object to the class."""
350        self.command = command_object
351
352    def sub_command(self, command_name:str=None, required_roles:list=[], help_des:str=None, slash_options:Dict[str, nextcord.SlashOption]={}, also_run_parent_CMD:bool=True):
353        """Create a lovely sub command from this slash command. 😀 (Only works with slash commands.)"""
354
355        def decorate(func):
356            def inner(func, command_name, required_roles, help_des, slash_options):
357                parent_command = self.command
358
359                return_data = {}
360
361                goldy_sub_command = Command(func, command_name=command_name, required_roles=required_roles, slash_options=slash_options, help_des=help_des, parent_cmd=self)
362                slash_command_params = goldy_sub_command.slash_commands_params_generator()
363                parent_slash_command_params = self.slash_commands_params_generator()
364
365                if self.in_extension == True: # Run in EXTENSION!
366                    exec(f"""
367@parent_command.subcommand(name=command_name, description=help_des)
368async def slash_command_(interaction: Interaction{slash_command_params[0]}):
369    ctx = GoldyBot.objects.slash.InteractionToCtx(interaction)
370    try:
371        if self.allowed_to_run(ctx):
372            continue_onto_subcommand = None
373            if also_run_parent_CMD == True: continue_onto_subcommand = await goldy_parent_command.func(class_, ctx{parent_slash_command_params[1]})
374            if not continue_onto_subcommand == False:
375                await func(class_, ctx{slash_command_params[1]})
376                GoldyBot.logging.log(f"[{MODULE_NAME}] The slash subcommand '{goldy_sub_command.code_name}' was executed.")
377            else:
378                GoldyBot.logging.log(f"[{MODULE_NAME}] The slash subcommand '{goldy_sub_command.code_name}' never executed because parent command returned 'False'.")
379
380    except GoldyBot.errors.MemberHasNoPermsForCommand:
381        if hidden == False:
382            no_perms_embed.description = GoldyBot.utility.msgs.bot.CommandNoPerms.Embed.des.format(ctx.author.mention)
383            message = await ctx.send(embed=no_perms_embed)
384            await asyncio.sleep(6)
385            await message.delete()
386
387    except GoldyBot.errors.GuildNotRegistered:
388        if hidden == False:
389            guild_not_registered_embed.description = GoldyBot.utility.msgs.bot.CommandGuildNotRegistered.Embed.des.format(ctx.author.mention)
390            message = await ctx.send(embed=guild_not_registered_embed)
391            await asyncio.sleep(15)
392            message.delete()
393
394    except Exception as e:
395        GoldyBot.logging.log("error", e)
396                    """, 
397                                                                        # Lambda expression: Returns nc_co
398                    {"func":goldy_sub_command.func, "parent_command": parent_command, "command_name":goldy_sub_command.command_name, "help_des":goldy_sub_command.get_help_des(), 
399                    "self":goldy_sub_command, "Interaction": GoldyBot.nextcord.Interaction, "GoldyBot": GoldyBot, "asyncio":asyncio, "nextcord":nextcord, 
400                    "class_":goldy_sub_command.extension, "no_perms_embed":self.no_perms_embed, "guild_not_registered_embed":self.guild_not_registered_embed, 
401                    "hidden":goldy_sub_command.is_hidden, "also_run_parent_CMD":also_run_parent_CMD, "goldy_parent_command": self}, return_data)
402                    
403                else: # Run as NORMAL command!
404                    exec(f"""
405@parent_command.subcommand(name=command_name, description=help_des)
406async def slash_command_(interaction: Interaction{slash_command_params[0]}):
407    ctx = GoldyBot.objects.slash.InteractionToCtx(interaction)
408    try:
409        if self.allowed_to_run(ctx):
410            if also_run_parent_CMD == True: await goldy_parent_command.func(ctx{parent_slash_command_params[1]})
411            await func(ctx{slash_command_params[1]})
412            GoldyBot.logging.log(f"[{MODULE_NAME}] The slash command '{goldy_sub_command.code_name}' was executed.")
413
414    except GoldyBot.errors.MemberHasNoPermsForCommand:
415        if hidden == False:
416            no_perms_embed.description = GoldyBot.utility.msgs.bot.CommandNoPerms.Embed.des.format(ctx.author.mention)
417            message = await ctx.send(embed=no_perms_embed)
418            await asyncio.sleep(15)
419            await message.delete()
420
421    except GoldyBot.errors.GuildNotRegistered:
422        if hidden == False:
423            guild_not_registered_embed.description = GoldyBot.utility.msgs.bot.CommandGuildNotRegistered.Embed.des.format(ctx.author.mention)
424            message = await ctx.send(embed=guild_not_registered_embed)
425            await asyncio.sleep(15)
426            await message.delete()
427
428    except Exception as e:
429        GoldyBot.logging.log("error", e)
430                    """, 
431                    
432                    {"func":goldy_sub_command.func, "parent_command": parent_command, "command_name":goldy_sub_command.command_name, "help_des":goldy_sub_command.get_help_des(), 
433                    "self":goldy_sub_command, "Interaction": GoldyBot.nextcord.Interaction, "GoldyBot": GoldyBot, "asyncio":asyncio, "nextcord":nextcord, 
434                    "no_perms_embed":self.no_perms_embed, "guild_not_registered_embed":self.guild_not_registered_embed, "hidden":goldy_sub_command.is_hidden, 
435                    "also_run_parent_CMD":also_run_parent_CMD, "goldy_parent_command": self}, return_data)
436
437                goldy_sub_command.update_command_object(return_data["slash_command_"])
438
439                GoldyBot.logging.log("info_5", f"[{MODULE_NAME}] Sub Command '{goldy_sub_command.command_name}' of '{parent_command.name}' has been loaded.")
440                return goldy_sub_command
441
442            return inner(func, command_name, required_roles, help_des, slash_options)
443
444        return decorate
445
446    def allowed_to_run(self, ctx):
447        """Checks if the command is allowed to run with current circumstances."""
448        goldy_config = GoldyBot.config.Config(GoldyBot.files.File(GoldyBot.paths.GOLDY_CONFIG_JSON))
449        
450        # Check if guild is registered.
451        #--------------------------------
452        if str(ctx.guild.id) in goldy_config.read("allowed_guilds"):
453            guild_code_name = GoldyBot.cache.FindGuilds(goldy_config).find_object_by_id(ctx.guild.id).code_name
454            guild_config = GoldyBot.utility.guilds.config.GuildConfig(GoldyBot.config.Config(GoldyBot.files.File(GoldyBot.paths.CONFIG + f"/{guild_code_name}/config.json")))
455            
456            if guild_config.is_extension_allowed(self.extension_name):
457
458                if not self.required_roles_ == []:
459                    # If the required roles contain 'bot_dev' and the bot dev is running the command allow the command to execute.
460                    #----------------------------------------------------------------------------------------------------------------
461                    if "bot_dev" in self.required_roles_:
462                        if str(ctx.author.id) in GoldyBot.settings.BOT_DEVS:
463                            return True
464
465                    # If the required roles contain 'bot_admin' and a bot admin is running the command allow the command to execute. (NEW)
466                    #----------------------------------------------------------------------------------------------------------------
467                    if "bot_admin" in self.required_roles_:
468                        if str(ctx.author.id) in goldy_config.read("admin_users"):
469                            return True
470
471                    # Check if member has any of the required roles.
472                    #----------------------------------------------------
473                    for role_code_name in self.required_roles_:
474                        if not role_code_name in ["bot_dev", "bot_admin"]:
475                            role = guild_config.get_role(ctx, role_code_name)
476                            if GoldyBot.objects.member.Member(ctx).has_role(role):
477                                return True
478
479                    raise GoldyBot.errors.MemberHasNoPermsForCommand(f"The member '{ctx.author.name}' does not have the right permissions to use this command.")
480
481                else:
482                    return True
483                
484        else:
485            raise GoldyBot.errors.GuildNotRegistered(f"The guild '{ctx.guild.name}' has not been registered.")
486
487    @property
488    def guilds_allowed_in(self) -> List[int]:
489        """Returns the ids of the guilds this command is allowed to function in."""
490        goldy_config = GoldyBot.config.Config(GoldyBot.files.File(GoldyBot.paths.GOLDY_CONFIG_JSON))
491        allowed_guilds = goldy_config.read("allowed_guilds")
492
493        guilds_command_is_allowed_in:List[int] = []
494
495        for guild_id in allowed_guilds:
496            try:
497                guild_config = GoldyBot.utility.guilds.config.GuildConfig(GoldyBot.config.Config(GoldyBot.files.File(GoldyBot.paths.CONFIG + f"/{allowed_guilds[guild_id]}/config.json")))
498                
499                if guild_config.is_extension_allowed(self.extension_name):
500                    guilds_command_is_allowed_in.append(int(guild_id))
501            except FileNotFoundError:
502                pass
503            
504        if guilds_command_is_allowed_in == []: guilds_command_is_allowed_in = [123]
505
506        print(guilds_command_is_allowed_in)
507
508        return guilds_command_is_allowed_in
509
510    def get_help_des(self) -> str:
511        """Returns the command's help description."""
512        if self.help_des_ == None:
513            try:
514                return (importlib.import_module(f'.{self.command_name}', package="GoldyBot.utility.msgs")).help_des
515
516            except ImportError:
517                GoldyBot.logging.log("info", f"[{MODULE_NAME}] The command '{self.command_name}' does not have a 'msg' module, so the help command will not display a help description for it.")
518                return "None"
519
520            except AttributeError:
521                GoldyBot.logging.log("info", f"[{MODULE_NAME}] The command '{self.command_name}' does not contain 'help_des' variable in it's 'msg' module, so the help command will not display a help description.")
522                return "None"
523        else:
524            return self.help_des_
class Embeds:
17class Embeds():
18    def __init__(self):
19        self.command_usage_embed = GoldyBot.utility.goldy.embed.Embed(title=GoldyBot.utility.msgs.bot.CommandUsage.Embed.title)
20        self.command_usage_embed.set_thumbnail(url=GoldyBot.utility.msgs.bot.CommandUsage.Embed.thumbnail)
21
22        self.no_perms_embed = GoldyBot.utility.goldy.embed.Embed(title=GoldyBot.utility.msgs.bot.CommandNoPerms.Embed.title)
23        self.no_perms_embed.color = GoldyBot.utility.msgs.bot.CommandNoPerms.Embed.colour
24        self.no_perms_embed.set_thumbnail(url=GoldyBot.utility.msgs.bot.CommandNoPerms.Embed.thumbnail)
25
26        self.guild_not_registered_embed = GoldyBot.utility.goldy.embed.Embed(title=GoldyBot.utility.msgs.bot.CommandGuildNotRegistered.Embed.title)
27        self.guild_not_registered_embed.color = GoldyBot.utility.msgs.bot.CommandGuildNotRegistered.Embed.colour
28        self.guild_not_registered_embed.set_thumbnail(url=GoldyBot.utility.msgs.bot.CommandGuildNotRegistered.Embed.thumbnail)
Embeds()
18    def __init__(self):
19        self.command_usage_embed = GoldyBot.utility.goldy.embed.Embed(title=GoldyBot.utility.msgs.bot.CommandUsage.Embed.title)
20        self.command_usage_embed.set_thumbnail(url=GoldyBot.utility.msgs.bot.CommandUsage.Embed.thumbnail)
21
22        self.no_perms_embed = GoldyBot.utility.goldy.embed.Embed(title=GoldyBot.utility.msgs.bot.CommandNoPerms.Embed.title)
23        self.no_perms_embed.color = GoldyBot.utility.msgs.bot.CommandNoPerms.Embed.colour
24        self.no_perms_embed.set_thumbnail(url=GoldyBot.utility.msgs.bot.CommandNoPerms.Embed.thumbnail)
25
26        self.guild_not_registered_embed = GoldyBot.utility.goldy.embed.Embed(title=GoldyBot.utility.msgs.bot.CommandGuildNotRegistered.Embed.title)
27        self.guild_not_registered_embed.color = GoldyBot.utility.msgs.bot.CommandGuildNotRegistered.Embed.colour
28        self.guild_not_registered_embed.set_thumbnail(url=GoldyBot.utility.msgs.bot.CommandGuildNotRegistered.Embed.thumbnail)
class Command(Embeds):
 30class Command(Embeds):
 31    def __init__(self, func, command_object:nextcord.BaseApplicationCommand | commands.Command=None, command_name=None, 
 32        required_roles:list=[], slash_options:dict={}, help_des:str=None, hidden:bool=False, 
 33        parent_cmd:Command=None
 34    ):
 35        """Generates goldy bot command object with command function object."""
 36        self.func:function = func
 37        self.command = command_object
 38        """Nextcord command object."""
 39
 40        if command_name == None:
 41            self.command_name = self.func.__name__
 42        else:
 43            self.command_name = command_name
 44        self.required_roles_ = required_roles
 45        self.slash_options_ = slash_options
 46        self.params_ = list(self.func.__code__.co_varnames)
 47        self.params_amount_ = self.func.__code__.co_argcount
 48        self.help_des_ = help_des
 49        self.is_hidden_ = hidden
 50
 51        self.parent_cmd_ = parent_cmd
 52        
 53        self.in_extension_ = False
 54
 55        if self.params[0] == "self":
 56            self.in_extension_ = True
 57            self.params_.pop(0)
 58
 59        # Add command to extension in cache.
 60        if self.in_extension:
 61            if self.module.is_internal_module:
 62                GoldyBot.cache.main_cache_dict["internal_modules"][f"{self.module_name}"]["extensions"][f"{self.extension_name}"]["commands"].append(self)
 63            else:
 64                GoldyBot.cache.main_cache_dict["modules"][f"{self.module_name}"]["extensions"][f"{self.extension_name}"]["commands"].append(self)
 65        else:
 66            GoldyBot.cache.main_cache_dict["internal_modules"]["goldy"]["extensions"]["core"]["commands"].append(self)
 67
 68        super().__init__()
 69
 70    @property
 71    def code_name(self) -> str:
 72        """Returns code name of command."""
 73        return self.command_name
 74
 75    @property
 76    def params(self) -> list:
 77        """Returns list of function parameters."""
 78        if self.in_extension:
 79            return self.params_[0:self.params_amount_ - 1]
 80        else:
 81            return self.params_[0:self.params_amount_]
 82
 83    @property
 84    def extension_name(self) -> str | None:
 85        """Returns extension's code name."""
 86        if self.in_extension:
 87            # Command is in extension.
 88            return str(self.func).split(" ")[1].split(".")[0]
 89        else:
 90            # Command is not in any extension.
 91            return None
 92
 93    @property
 94    def extension(self) -> GoldyBot.ext.extensions.Extension | None:
 95        """Finds and returns the object of the command's extension."""
 96        if self.in_extension:
 97            return GoldyBot.cache.FindExtensions().find_object_by_extension_name(extension_name=self.extension_name)
 98        else:
 99            None
100
101    @property
102    def in_extension(self) -> bool:
103        """Returns true/false if the command is in a extension."""
104        if self.in_extension_:
105            return True
106        else:
107            return False
108
109    @property
110    def module_name(self) -> str:
111        """Returns name of module the command is located in."""
112        return self.extension.module_name
113
114    @property
115    def module(self) -> GoldyBot.modules.Module:
116        return GoldyBot.cache.FindModules().find_object_by_module_name(module_name=self.module_name)
117
118    @property
119    def is_hidden(self):
120        """Is the command hidden."""
121        return self.is_hidden_
122
123    @property
124    def parent_cmd(self) -> Command|None:
125        """Returns the command object of the parent command if command has parent."""
126        return self.parent_cmd_
127
128    @property
129    def is_child(self):
130        """Returns if command is child or not."""
131        if self.parent_cmd == None:
132            return False
133        else:
134            return True
135
136    def mention(self) -> str:
137        """Returns mention of slash command, if registered as a slash command."""
138        #TODO: Perhaps have this return codeblock if this command is not a slash command.
139        try:
140            if self.is_child == False:
141                return f"</{self.code_name}:{self.command.command_ids[0]}>"
142            else:
143                return f"</{self.parent_cmd.code_name} {self.code_name}:{self.command.command_ids[0]}>"
144        except (TypeError, AttributeError):
145            GoldyBot.log("warn", f"[{MODULE_NAME}] Tried to mention slash command '{self.code_name}' but could not find it's command id so I'm returning the mention without it.")
146            
147            if self.is_child == False: return f"</{self.code_name}:0>"
148            else: return f"</{self.parent_cmd.code_name} {self.code_name}:0>"
149
150    def create_slash(self) -> nextcord.BaseApplicationCommand:
151        """Creates slash command."""
152
153        GoldyBot.logging.log(f"[{MODULE_NAME}] [{self.command_name.upper()}] Creating slash command...")
154        slash_command_params = self.slash_commands_params_generator()
155
156        return_data = {}
157
158        #  Make slash command only visible to admins if it is hidden.
159        default_member_permissions = None
160        if self.is_hidden: default_member_permissions = nextcord.Permissions(permissions=8)
161        
162
163        if self.in_extension == True: # Run in EXTENSION!
164            exec(f"""
165@client.slash_command(name=command_name, description=help_des, guild_ids=guilds_allowed_in, default_member_permissions=default_member_permissions)
166async def slash_command_(interaction: Interaction{slash_command_params[0]}):
167    ctx = GoldyBot.objects.slash.InteractionToCtx(interaction)
168    try:
169        if self.allowed_to_run(ctx):
170            await func(class_, ctx{slash_command_params[1]})
171            GoldyBot.logging.log(f"[{MODULE_NAME}] The slash command '{self.code_name}' was executed.")
172
173    except GoldyBot.errors.MemberHasNoPermsForCommand:
174        if hidden == False:
175            no_perms_embed.description = GoldyBot.utility.msgs.bot.CommandNoPerms.Embed.des.format(ctx.author.mention)
176            message = await ctx.send(embed=no_perms_embed)
177            await asyncio.sleep(6)
178            await message.delete()
179
180    except GoldyBot.errors.GuildNotRegistered:
181        if hidden == False:
182            guild_not_registered_embed.description = GoldyBot.utility.msgs.bot.CommandGuildNotRegistered.Embed.des.format(ctx.author.mention)
183            message = await ctx.send(embed=guild_not_registered_embed)
184            await asyncio.sleep(15)
185            message.delete()
186
187    except Exception as e:
188        GoldyBot.logging.log("error", e)
189            """, 
190            
191            {"func":self.func, "client":GoldyBot.cache.main_cache_dict["client"], "command_name":self.command_name, "help_des":self.get_help_des(), "self":self,
192            "Interaction": GoldyBot.nextcord.Interaction, "GoldyBot": GoldyBot, "asyncio":asyncio, "nextcord":nextcord, "class_":self.extension, "no_perms_embed":self.no_perms_embed, 
193            "guild_not_registered_embed":self.guild_not_registered_embed, "hidden":self.is_hidden, "guilds_allowed_in":self.guilds_allowed_in, 
194            "default_member_permissions":default_member_permissions}, return_data)
195            
196        else: # Run as NORMAL command!
197            exec(f"""
198@client.slash_command(name=command_name, description=help_des, guild_ids=guilds_allowed_in, default_member_permissions=default_member_permissions)
199async def slash_command_(interaction: Interaction{slash_command_params[0]}):
200    ctx = GoldyBot.objects.slash.InteractionToCtx(interaction)
201    try:
202        if self.allowed_to_run(ctx):
203            await func(ctx{slash_command_params[1]})
204            GoldyBot.logging.log(f"[{MODULE_NAME}] The slash command '{self.code_name}' was executed.")
205
206    except GoldyBot.errors.MemberHasNoPermsForCommand:
207        if hidden == False:
208            no_perms_embed.description = GoldyBot.utility.msgs.bot.CommandNoPerms.Embed.des.format(ctx.author.mention)
209            message = await ctx.send(embed=no_perms_embed)
210            await asyncio.sleep(15)
211            await message.delete()
212
213    except GoldyBot.errors.GuildNotRegistered:
214        if hidden == False:
215            guild_not_registered_embed.description = GoldyBot.utility.msgs.bot.CommandGuildNotRegistered.Embed.des.format(ctx.author.mention)
216            message = await ctx.send(embed=guild_not_registered_embed)
217            await asyncio.sleep(15)
218            await message.delete()
219
220    except Exception as e:
221        GoldyBot.logging.log("error", e)
222            """, 
223            
224            {"func":self.func, "client":GoldyBot.cache.main_cache_dict["client"], "command_name":self.command_name, "help_des":self.get_help_des(), "self":self,
225            "Interaction": GoldyBot.nextcord.Interaction, "GoldyBot": GoldyBot, "asyncio":asyncio, "nextcord":nextcord, "no_perms_embed":self.no_perms_embed, 
226            "guild_not_registered_embed":self.guild_not_registered_embed, "hidden":self.is_hidden, "guilds_allowed_in":self.guilds_allowed_in, 
227            "default_member_permissions":default_member_permissions}, return_data)
228
229        GoldyBot.logging.log(f"[{MODULE_NAME}] [{self.command_name.upper()}] Slash command created!")
230
231        self.command = return_data["slash_command_"]
232        return return_data["slash_command_"]
233
234    def remove(self):
235        """Removes command from nextcord."""
236        command_application_object = self.command
237
238        # Remove command from extension in cache.
239        if self.in_extension:
240            if self.module.is_internal_module:
241                GoldyBot.cache.main_cache_dict["internal_modules"][f"{self.module_name}"]["extensions"][f"{self.extension_name}"]["commands"].remove(self)
242            else:
243                GoldyBot.cache.main_cache_dict["modules"][f"{self.module_name}"]["extensions"][f"{self.extension_name}"]["commands"].remove(self)
244        else:
245            GoldyBot.cache.main_cache_dict["internal_modules"]["goldy"]["extensions"]["core"]["commands"].remove(self)
246
247        # Remove from nextcord.
248        client = GoldyBot.cache.client()
249        client.remove_command(name=self.code_name)
250
251        for guild_id in self.guilds_allowed_in:
252            print(command_application_object.command_ids) #TODO: Problem here!
253            GoldyBot.async_loop.run_until_complete(client.delete_application_commands(command_application_object, guild_id=guild_id))
254            
255            GoldyBot.cache.main_cache_dict["client"] = client
256
257        GoldyBot.logging.log("info_5", f"[{MODULE_NAME}] Removed the command '{self.code_name}'!")
258        return True
259
260    def any_args_missing(self, command_executers_args:tuple) -> bool:
261        """Checks if the args given by the command executer matches what parameters the command needs."""
262        if len(command_executers_args) == len(self.params[1:]):
263            return True
264        else:
265            return False
266
267    def slash_commands_params_generator(self):
268        """Generates a string of params for slash commands. This is more of an in-house thing."""
269        params_amount = len(self.params[1:])
270        slash_command_params = ""
271        slash_command_args = ""
272
273        if params_amount >= 1:
274            slash_command_params = ", "
275            slash_command_args = ", "
276            count = 0
277            for param in self.params[1:]:
278                count += 1
279
280                try:
281                    # String
282                    if isinstance(self.slash_options_[param.lower()], str):
283                        default_value = f"='{self.slash_options_[param.lower()]}'"
284
285                    # Slash Option
286                    if isinstance(self.slash_options_[param.lower()], nextcord.SlashOption):
287                        slash_option:nextcord.SlashOption = self.slash_options_[param.lower()]
288
289                        if isinstance(slash_option.name, str):
290                            name = f"'{slash_option.name}'"
291                        else:
292                            name = None
293
294                        if isinstance(slash_option.description, str):
295                            description = f"'{slash_option.description}'"
296                        else:
297                            description = None
298
299                        if isinstance(slash_option.required, nextcord.utils._MissingSentinel):
300                            required = None
301                        else:
302                            required = slash_option.required
303
304                        if isinstance(slash_option.choices, nextcord.utils._MissingSentinel):
305                            choices = None
306                        else:
307                            choices = slash_option.choices
308
309                        """
310                        if isinstance(slash_option.min_value, nextcord.utils._MissingSentinel):
311                            min_value = None
312                        else:
313                            min_value = slash_option.min_value
314                            
315                        if isinstance(slash_option.max_value, nextcord.utils._MissingSentinel):
316                            max_value = None
317                        else:
318                            max_value = slash_option.max_value
319                        """
320
321                        if isinstance(slash_option.autocomplete, nextcord.utils._MissingSentinel):
322                            autocomplete = None
323                        else:
324                            autocomplete = slash_option.autocomplete
325
326                        if isinstance(slash_option.default, str):
327                            default = f"'{slash_option.default}'"
328                        else:
329                            default = None
330                        
331                        default_value = f"""=nextcord.SlashOption(name={name}, description={description}, required={required}, 
332                        choices={choices}, autocomplete={autocomplete}, default={default}, verify={slash_option._verify})"""
333                    
334                    else:
335                        default_value = f"={self.slash_options_[param.lower()]}"
336
337                except KeyError:
338                    default_value = ""
339
340                if count >= params_amount:
341                    slash_command_params += f"{param}{default_value}"
342                    slash_command_args += f"{param}"
343                else:
344                    slash_command_params += f"{param}{default_value}, "
345                    slash_command_args += f"{param}, "
346
347        return (slash_command_params, slash_command_args)
348
349    def update_command_object(self, command_object:commands.Command):
350        """Adds/updates this command object to the class."""
351        self.command = command_object
352
353    def sub_command(self, command_name:str=None, required_roles:list=[], help_des:str=None, slash_options:Dict[str, nextcord.SlashOption]={}, also_run_parent_CMD:bool=True):
354        """Create a lovely sub command from this slash command. 😀 (Only works with slash commands.)"""
355
356        def decorate(func):
357            def inner(func, command_name, required_roles, help_des, slash_options):
358                parent_command = self.command
359
360                return_data = {}
361
362                goldy_sub_command = Command(func, command_name=command_name, required_roles=required_roles, slash_options=slash_options, help_des=help_des, parent_cmd=self)
363                slash_command_params = goldy_sub_command.slash_commands_params_generator()
364                parent_slash_command_params = self.slash_commands_params_generator()
365
366                if self.in_extension == True: # Run in EXTENSION!
367                    exec(f"""
368@parent_command.subcommand(name=command_name, description=help_des)
369async def slash_command_(interaction: Interaction{slash_command_params[0]}):
370    ctx = GoldyBot.objects.slash.InteractionToCtx(interaction)
371    try:
372        if self.allowed_to_run(ctx):
373            continue_onto_subcommand = None
374            if also_run_parent_CMD == True: continue_onto_subcommand = await goldy_parent_command.func(class_, ctx{parent_slash_command_params[1]})
375            if not continue_onto_subcommand == False:
376                await func(class_, ctx{slash_command_params[1]})
377                GoldyBot.logging.log(f"[{MODULE_NAME}] The slash subcommand '{goldy_sub_command.code_name}' was executed.")
378            else:
379                GoldyBot.logging.log(f"[{MODULE_NAME}] The slash subcommand '{goldy_sub_command.code_name}' never executed because parent command returned 'False'.")
380
381    except GoldyBot.errors.MemberHasNoPermsForCommand:
382        if hidden == False:
383            no_perms_embed.description = GoldyBot.utility.msgs.bot.CommandNoPerms.Embed.des.format(ctx.author.mention)
384            message = await ctx.send(embed=no_perms_embed)
385            await asyncio.sleep(6)
386            await message.delete()
387
388    except GoldyBot.errors.GuildNotRegistered:
389        if hidden == False:
390            guild_not_registered_embed.description = GoldyBot.utility.msgs.bot.CommandGuildNotRegistered.Embed.des.format(ctx.author.mention)
391            message = await ctx.send(embed=guild_not_registered_embed)
392            await asyncio.sleep(15)
393            message.delete()
394
395    except Exception as e:
396        GoldyBot.logging.log("error", e)
397                    """, 
398                                                                        # Lambda expression: Returns nc_co
399                    {"func":goldy_sub_command.func, "parent_command": parent_command, "command_name":goldy_sub_command.command_name, "help_des":goldy_sub_command.get_help_des(), 
400                    "self":goldy_sub_command, "Interaction": GoldyBot.nextcord.Interaction, "GoldyBot": GoldyBot, "asyncio":asyncio, "nextcord":nextcord, 
401                    "class_":goldy_sub_command.extension, "no_perms_embed":self.no_perms_embed, "guild_not_registered_embed":self.guild_not_registered_embed, 
402                    "hidden":goldy_sub_command.is_hidden, "also_run_parent_CMD":also_run_parent_CMD, "goldy_parent_command": self}, return_data)
403                    
404                else: # Run as NORMAL command!
405                    exec(f"""
406@parent_command.subcommand(name=command_name, description=help_des)
407async def slash_command_(interaction: Interaction{slash_command_params[0]}):
408    ctx = GoldyBot.objects.slash.InteractionToCtx(interaction)
409    try:
410        if self.allowed_to_run(ctx):
411            if also_run_parent_CMD == True: await goldy_parent_command.func(ctx{parent_slash_command_params[1]})
412            await func(ctx{slash_command_params[1]})
413            GoldyBot.logging.log(f"[{MODULE_NAME}] The slash command '{goldy_sub_command.code_name}' was executed.")
414
415    except GoldyBot.errors.MemberHasNoPermsForCommand:
416        if hidden == False:
417            no_perms_embed.description = GoldyBot.utility.msgs.bot.CommandNoPerms.Embed.des.format(ctx.author.mention)
418            message = await ctx.send(embed=no_perms_embed)
419            await asyncio.sleep(15)
420            await message.delete()
421
422    except GoldyBot.errors.GuildNotRegistered:
423        if hidden == False:
424            guild_not_registered_embed.description = GoldyBot.utility.msgs.bot.CommandGuildNotRegistered.Embed.des.format(ctx.author.mention)
425            message = await ctx.send(embed=guild_not_registered_embed)
426            await asyncio.sleep(15)
427            await message.delete()
428
429    except Exception as e:
430        GoldyBot.logging.log("error", e)
431                    """, 
432                    
433                    {"func":goldy_sub_command.func, "parent_command": parent_command, "command_name":goldy_sub_command.command_name, "help_des":goldy_sub_command.get_help_des(), 
434                    "self":goldy_sub_command, "Interaction": GoldyBot.nextcord.Interaction, "GoldyBot": GoldyBot, "asyncio":asyncio, "nextcord":nextcord, 
435                    "no_perms_embed":self.no_perms_embed, "guild_not_registered_embed":self.guild_not_registered_embed, "hidden":goldy_sub_command.is_hidden, 
436                    "also_run_parent_CMD":also_run_parent_CMD, "goldy_parent_command": self}, return_data)
437
438                goldy_sub_command.update_command_object(return_data["slash_command_"])
439
440                GoldyBot.logging.log("info_5", f"[{MODULE_NAME}] Sub Command '{goldy_sub_command.command_name}' of '{parent_command.name}' has been loaded.")
441                return goldy_sub_command
442
443            return inner(func, command_name, required_roles, help_des, slash_options)
444
445        return decorate
446
447    def allowed_to_run(self, ctx):
448        """Checks if the command is allowed to run with current circumstances."""
449        goldy_config = GoldyBot.config.Config(GoldyBot.files.File(GoldyBot.paths.GOLDY_CONFIG_JSON))
450        
451        # Check if guild is registered.
452        #--------------------------------
453        if str(ctx.guild.id) in goldy_config.read("allowed_guilds"):
454            guild_code_name = GoldyBot.cache.FindGuilds(goldy_config).find_object_by_id(ctx.guild.id).code_name
455            guild_config = GoldyBot.utility.guilds.config.GuildConfig(GoldyBot.config.Config(GoldyBot.files.File(GoldyBot.paths.CONFIG + f"/{guild_code_name}/config.json")))
456            
457            if guild_config.is_extension_allowed(self.extension_name):
458
459                if not self.required_roles_ == []:
460                    # If the required roles contain 'bot_dev' and the bot dev is running the command allow the command to execute.
461                    #----------------------------------------------------------------------------------------------------------------
462                    if "bot_dev" in self.required_roles_:
463                        if str(ctx.author.id) in GoldyBot.settings.BOT_DEVS:
464                            return True
465
466                    # If the required roles contain 'bot_admin' and a bot admin is running the command allow the command to execute. (NEW)
467                    #----------------------------------------------------------------------------------------------------------------
468                    if "bot_admin" in self.required_roles_:
469                        if str(ctx.author.id) in goldy_config.read("admin_users"):
470                            return True
471
472                    # Check if member has any of the required roles.
473                    #----------------------------------------------------
474                    for role_code_name in self.required_roles_:
475                        if not role_code_name in ["bot_dev", "bot_admin"]:
476                            role = guild_config.get_role(ctx, role_code_name)
477                            if GoldyBot.objects.member.Member(ctx).has_role(role):
478                                return True
479
480                    raise GoldyBot.errors.MemberHasNoPermsForCommand(f"The member '{ctx.author.name}' does not have the right permissions to use this command.")
481
482                else:
483                    return True
484                
485        else:
486            raise GoldyBot.errors.GuildNotRegistered(f"The guild '{ctx.guild.name}' has not been registered.")
487
488    @property
489    def guilds_allowed_in(self) -> List[int]:
490        """Returns the ids of the guilds this command is allowed to function in."""
491        goldy_config = GoldyBot.config.Config(GoldyBot.files.File(GoldyBot.paths.GOLDY_CONFIG_JSON))
492        allowed_guilds = goldy_config.read("allowed_guilds")
493
494        guilds_command_is_allowed_in:List[int] = []
495
496        for guild_id in allowed_guilds:
497            try:
498                guild_config = GoldyBot.utility.guilds.config.GuildConfig(GoldyBot.config.Config(GoldyBot.files.File(GoldyBot.paths.CONFIG + f"/{allowed_guilds[guild_id]}/config.json")))
499                
500                if guild_config.is_extension_allowed(self.extension_name):
501                    guilds_command_is_allowed_in.append(int(guild_id))
502            except FileNotFoundError:
503                pass
504            
505        if guilds_command_is_allowed_in == []: guilds_command_is_allowed_in = [123]
506
507        print(guilds_command_is_allowed_in)
508
509        return guilds_command_is_allowed_in
510
511    def get_help_des(self) -> str:
512        """Returns the command's help description."""
513        if self.help_des_ == None:
514            try:
515                return (importlib.import_module(f'.{self.command_name}', package="GoldyBot.utility.msgs")).help_des
516
517            except ImportError:
518                GoldyBot.logging.log("info", f"[{MODULE_NAME}] The command '{self.command_name}' does not have a 'msg' module, so the help command will not display a help description for it.")
519                return "None"
520
521            except AttributeError:
522                GoldyBot.logging.log("info", f"[{MODULE_NAME}] The command '{self.command_name}' does not contain 'help_des' variable in it's 'msg' module, so the help command will not display a help description.")
523                return "None"
524        else:
525            return self.help_des_
Command( func, command_object: nextcord.application_command.BaseApplicationCommand | nextcord.ext.commands.core.Command = None, command_name=None, required_roles: list = [], slash_options: dict = {}, help_des: str = None, hidden: bool = False, parent_cmd: GoldyBot.objects.command.Command = None)
31    def __init__(self, func, command_object:nextcord.BaseApplicationCommand | commands.Command=None, command_name=None, 
32        required_roles:list=[], slash_options:dict={}, help_des:str=None, hidden:bool=False, 
33        parent_cmd:Command=None
34    ):
35        """Generates goldy bot command object with command function object."""
36        self.func:function = func
37        self.command = command_object
38        """Nextcord command object."""
39
40        if command_name == None:
41            self.command_name = self.func.__name__
42        else:
43            self.command_name = command_name
44        self.required_roles_ = required_roles
45        self.slash_options_ = slash_options
46        self.params_ = list(self.func.__code__.co_varnames)
47        self.params_amount_ = self.func.__code__.co_argcount
48        self.help_des_ = help_des
49        self.is_hidden_ = hidden
50
51        self.parent_cmd_ = parent_cmd
52        
53        self.in_extension_ = False
54
55        if self.params[0] == "self":
56            self.in_extension_ = True
57            self.params_.pop(0)
58
59        # Add command to extension in cache.
60        if self.in_extension:
61            if self.module.is_internal_module:
62                GoldyBot.cache.main_cache_dict["internal_modules"][f"{self.module_name}"]["extensions"][f"{self.extension_name}"]["commands"].append(self)
63            else:
64                GoldyBot.cache.main_cache_dict["modules"][f"{self.module_name}"]["extensions"][f"{self.extension_name}"]["commands"].append(self)
65        else:
66            GoldyBot.cache.main_cache_dict["internal_modules"]["goldy"]["extensions"]["core"]["commands"].append(self)
67
68        super().__init__()

Generates goldy bot command object with command function object.

command

Nextcord command object.

code_name: str

Returns code name of command.

params: list

Returns list of function parameters.

extension_name: str | None

Returns extension's code name.

Finds and returns the object of the command's extension.

in_extension: bool

Returns true/false if the command is in a extension.

module_name: str

Returns name of module the command is located in.

is_hidden

Is the command hidden.

Returns the command object of the parent command if command has parent.

is_child

Returns if command is child or not.

def mention(self) -> str:
136    def mention(self) -> str:
137        """Returns mention of slash command, if registered as a slash command."""
138        #TODO: Perhaps have this return codeblock if this command is not a slash command.
139        try:
140            if self.is_child == False:
141                return f"</{self.code_name}:{self.command.command_ids[0]}>"
142            else:
143                return f"</{self.parent_cmd.code_name} {self.code_name}:{self.command.command_ids[0]}>"
144        except (TypeError, AttributeError):
145            GoldyBot.log("warn", f"[{MODULE_NAME}] Tried to mention slash command '{self.code_name}' but could not find it's command id so I'm returning the mention without it.")
146            
147            if self.is_child == False: return f"</{self.code_name}:0>"
148            else: return f"</{self.parent_cmd.code_name} {self.code_name}:0>"

Returns mention of slash command, if registered as a slash command.

def create_slash(self) -> nextcord.application_command.BaseApplicationCommand:
150    def create_slash(self) -> nextcord.BaseApplicationCommand:
151        """Creates slash command."""
152
153        GoldyBot.logging.log(f"[{MODULE_NAME}] [{self.command_name.upper()}] Creating slash command...")
154        slash_command_params = self.slash_commands_params_generator()
155
156        return_data = {}
157
158        #  Make slash command only visible to admins if it is hidden.
159        default_member_permissions = None
160        if self.is_hidden: default_member_permissions = nextcord.Permissions(permissions=8)
161        
162
163        if self.in_extension == True: # Run in EXTENSION!
164            exec(f"""
165@client.slash_command(name=command_name, description=help_des, guild_ids=guilds_allowed_in, default_member_permissions=default_member_permissions)
166async def slash_command_(interaction: Interaction{slash_command_params[0]}):
167    ctx = GoldyBot.objects.slash.InteractionToCtx(interaction)
168    try:
169        if self.allowed_to_run(ctx):
170            await func(class_, ctx{slash_command_params[1]})
171            GoldyBot.logging.log(f"[{MODULE_NAME}] The slash command '{self.code_name}' was executed.")
172
173    except GoldyBot.errors.MemberHasNoPermsForCommand:
174        if hidden == False:
175            no_perms_embed.description = GoldyBot.utility.msgs.bot.CommandNoPerms.Embed.des.format(ctx.author.mention)
176            message = await ctx.send(embed=no_perms_embed)
177            await asyncio.sleep(6)
178            await message.delete()
179
180    except GoldyBot.errors.GuildNotRegistered:
181        if hidden == False:
182            guild_not_registered_embed.description = GoldyBot.utility.msgs.bot.CommandGuildNotRegistered.Embed.des.format(ctx.author.mention)
183            message = await ctx.send(embed=guild_not_registered_embed)
184            await asyncio.sleep(15)
185            message.delete()
186
187    except Exception as e:
188        GoldyBot.logging.log("error", e)
189            """, 
190            
191            {"func":self.func, "client":GoldyBot.cache.main_cache_dict["client"], "command_name":self.command_name, "help_des":self.get_help_des(), "self":self,
192            "Interaction": GoldyBot.nextcord.Interaction, "GoldyBot": GoldyBot, "asyncio":asyncio, "nextcord":nextcord, "class_":self.extension, "no_perms_embed":self.no_perms_embed, 
193            "guild_not_registered_embed":self.guild_not_registered_embed, "hidden":self.is_hidden, "guilds_allowed_in":self.guilds_allowed_in, 
194            "default_member_permissions":default_member_permissions}, return_data)
195            
196        else: # Run as NORMAL command!
197            exec(f"""
198@client.slash_command(name=command_name, description=help_des, guild_ids=guilds_allowed_in, default_member_permissions=default_member_permissions)
199async def slash_command_(interaction: Interaction{slash_command_params[0]}):
200    ctx = GoldyBot.objects.slash.InteractionToCtx(interaction)
201    try:
202        if self.allowed_to_run(ctx):
203            await func(ctx{slash_command_params[1]})
204            GoldyBot.logging.log(f"[{MODULE_NAME}] The slash command '{self.code_name}' was executed.")
205
206    except GoldyBot.errors.MemberHasNoPermsForCommand:
207        if hidden == False:
208            no_perms_embed.description = GoldyBot.utility.msgs.bot.CommandNoPerms.Embed.des.format(ctx.author.mention)
209            message = await ctx.send(embed=no_perms_embed)
210            await asyncio.sleep(15)
211            await message.delete()
212
213    except GoldyBot.errors.GuildNotRegistered:
214        if hidden == False:
215            guild_not_registered_embed.description = GoldyBot.utility.msgs.bot.CommandGuildNotRegistered.Embed.des.format(ctx.author.mention)
216            message = await ctx.send(embed=guild_not_registered_embed)
217            await asyncio.sleep(15)
218            await message.delete()
219
220    except Exception as e:
221        GoldyBot.logging.log("error", e)
222            """, 
223            
224            {"func":self.func, "client":GoldyBot.cache.main_cache_dict["client"], "command_name":self.command_name, "help_des":self.get_help_des(), "self":self,
225            "Interaction": GoldyBot.nextcord.Interaction, "GoldyBot": GoldyBot, "asyncio":asyncio, "nextcord":nextcord, "no_perms_embed":self.no_perms_embed, 
226            "guild_not_registered_embed":self.guild_not_registered_embed, "hidden":self.is_hidden, "guilds_allowed_in":self.guilds_allowed_in, 
227            "default_member_permissions":default_member_permissions}, return_data)
228
229        GoldyBot.logging.log(f"[{MODULE_NAME}] [{self.command_name.upper()}] Slash command created!")
230
231        self.command = return_data["slash_command_"]
232        return return_data["slash_command_"]

Creates slash command.

def remove(self):
234    def remove(self):
235        """Removes command from nextcord."""
236        command_application_object = self.command
237
238        # Remove command from extension in cache.
239        if self.in_extension:
240            if self.module.is_internal_module:
241                GoldyBot.cache.main_cache_dict["internal_modules"][f"{self.module_name}"]["extensions"][f"{self.extension_name}"]["commands"].remove(self)
242            else:
243                GoldyBot.cache.main_cache_dict["modules"][f"{self.module_name}"]["extensions"][f"{self.extension_name}"]["commands"].remove(self)
244        else:
245            GoldyBot.cache.main_cache_dict["internal_modules"]["goldy"]["extensions"]["core"]["commands"].remove(self)
246
247        # Remove from nextcord.
248        client = GoldyBot.cache.client()
249        client.remove_command(name=self.code_name)
250
251        for guild_id in self.guilds_allowed_in:
252            print(command_application_object.command_ids) #TODO: Problem here!
253            GoldyBot.async_loop.run_until_complete(client.delete_application_commands(command_application_object, guild_id=guild_id))
254            
255            GoldyBot.cache.main_cache_dict["client"] = client
256
257        GoldyBot.logging.log("info_5", f"[{MODULE_NAME}] Removed the command '{self.code_name}'!")
258        return True

Removes command from nextcord.

def any_args_missing(self, command_executers_args: tuple) -> bool:
260    def any_args_missing(self, command_executers_args:tuple) -> bool:
261        """Checks if the args given by the command executer matches what parameters the command needs."""
262        if len(command_executers_args) == len(self.params[1:]):
263            return True
264        else:
265            return False

Checks if the args given by the command executer matches what parameters the command needs.

def slash_commands_params_generator(self):
267    def slash_commands_params_generator(self):
268        """Generates a string of params for slash commands. This is more of an in-house thing."""
269        params_amount = len(self.params[1:])
270        slash_command_params = ""
271        slash_command_args = ""
272
273        if params_amount >= 1:
274            slash_command_params = ", "
275            slash_command_args = ", "
276            count = 0
277            for param in self.params[1:]:
278                count += 1
279
280                try:
281                    # String
282                    if isinstance(self.slash_options_[param.lower()], str):
283                        default_value = f"='{self.slash_options_[param.lower()]}'"
284
285                    # Slash Option
286                    if isinstance(self.slash_options_[param.lower()], nextcord.SlashOption):
287                        slash_option:nextcord.SlashOption = self.slash_options_[param.lower()]
288
289                        if isinstance(slash_option.name, str):
290                            name = f"'{slash_option.name}'"
291                        else:
292                            name = None
293
294                        if isinstance(slash_option.description, str):
295                            description = f"'{slash_option.description}'"
296                        else:
297                            description = None
298
299                        if isinstance(slash_option.required, nextcord.utils._MissingSentinel):
300                            required = None
301                        else:
302                            required = slash_option.required
303
304                        if isinstance(slash_option.choices, nextcord.utils._MissingSentinel):
305                            choices = None
306                        else:
307                            choices = slash_option.choices
308
309                        """
310                        if isinstance(slash_option.min_value, nextcord.utils._MissingSentinel):
311                            min_value = None
312                        else:
313                            min_value = slash_option.min_value
314                            
315                        if isinstance(slash_option.max_value, nextcord.utils._MissingSentinel):
316                            max_value = None
317                        else:
318                            max_value = slash_option.max_value
319                        """
320
321                        if isinstance(slash_option.autocomplete, nextcord.utils._MissingSentinel):
322                            autocomplete = None
323                        else:
324                            autocomplete = slash_option.autocomplete
325
326                        if isinstance(slash_option.default, str):
327                            default = f"'{slash_option.default}'"
328                        else:
329                            default = None
330                        
331                        default_value = f"""=nextcord.SlashOption(name={name}, description={description}, required={required}, 
332                        choices={choices}, autocomplete={autocomplete}, default={default}, verify={slash_option._verify})"""
333                    
334                    else:
335                        default_value = f"={self.slash_options_[param.lower()]}"
336
337                except KeyError:
338                    default_value = ""
339
340                if count >= params_amount:
341                    slash_command_params += f"{param}{default_value}"
342                    slash_command_args += f"{param}"
343                else:
344                    slash_command_params += f"{param}{default_value}, "
345                    slash_command_args += f"{param}, "
346
347        return (slash_command_params, slash_command_args)

Generates a string of params for slash commands. This is more of an in-house thing.

def update_command_object(self, command_object: nextcord.ext.commands.core.Command):
349    def update_command_object(self, command_object:commands.Command):
350        """Adds/updates this command object to the class."""
351        self.command = command_object

Adds/updates this command object to the class.

def sub_command( self, command_name: str = None, required_roles: list = [], help_des: str = None, slash_options: Dict[str, nextcord.application_command.SlashOption] = {}, also_run_parent_CMD: bool = True):
353    def sub_command(self, command_name:str=None, required_roles:list=[], help_des:str=None, slash_options:Dict[str, nextcord.SlashOption]={}, also_run_parent_CMD:bool=True):
354        """Create a lovely sub command from this slash command. 😀 (Only works with slash commands.)"""
355
356        def decorate(func):
357            def inner(func, command_name, required_roles, help_des, slash_options):
358                parent_command = self.command
359
360                return_data = {}
361
362                goldy_sub_command = Command(func, command_name=command_name, required_roles=required_roles, slash_options=slash_options, help_des=help_des, parent_cmd=self)
363                slash_command_params = goldy_sub_command.slash_commands_params_generator()
364                parent_slash_command_params = self.slash_commands_params_generator()
365
366                if self.in_extension == True: # Run in EXTENSION!
367                    exec(f"""
368@parent_command.subcommand(name=command_name, description=help_des)
369async def slash_command_(interaction: Interaction{slash_command_params[0]}):
370    ctx = GoldyBot.objects.slash.InteractionToCtx(interaction)
371    try:
372        if self.allowed_to_run(ctx):
373            continue_onto_subcommand = None
374            if also_run_parent_CMD == True: continue_onto_subcommand = await goldy_parent_command.func(class_, ctx{parent_slash_command_params[1]})
375            if not continue_onto_subcommand == False:
376                await func(class_, ctx{slash_command_params[1]})
377                GoldyBot.logging.log(f"[{MODULE_NAME}] The slash subcommand '{goldy_sub_command.code_name}' was executed.")
378            else:
379                GoldyBot.logging.log(f"[{MODULE_NAME}] The slash subcommand '{goldy_sub_command.code_name}' never executed because parent command returned 'False'.")
380
381    except GoldyBot.errors.MemberHasNoPermsForCommand:
382        if hidden == False:
383            no_perms_embed.description = GoldyBot.utility.msgs.bot.CommandNoPerms.Embed.des.format(ctx.author.mention)
384            message = await ctx.send(embed=no_perms_embed)
385            await asyncio.sleep(6)
386            await message.delete()
387
388    except GoldyBot.errors.GuildNotRegistered:
389        if hidden == False:
390            guild_not_registered_embed.description = GoldyBot.utility.msgs.bot.CommandGuildNotRegistered.Embed.des.format(ctx.author.mention)
391            message = await ctx.send(embed=guild_not_registered_embed)
392            await asyncio.sleep(15)
393            message.delete()
394
395    except Exception as e:
396        GoldyBot.logging.log("error", e)
397                    """, 
398                                                                        # Lambda expression: Returns nc_co
399                    {"func":goldy_sub_command.func, "parent_command": parent_command, "command_name":goldy_sub_command.command_name, "help_des":goldy_sub_command.get_help_des(), 
400                    "self":goldy_sub_command, "Interaction": GoldyBot.nextcord.Interaction, "GoldyBot": GoldyBot, "asyncio":asyncio, "nextcord":nextcord, 
401                    "class_":goldy_sub_command.extension, "no_perms_embed":self.no_perms_embed, "guild_not_registered_embed":self.guild_not_registered_embed, 
402                    "hidden":goldy_sub_command.is_hidden, "also_run_parent_CMD":also_run_parent_CMD, "goldy_parent_command": self}, return_data)
403                    
404                else: # Run as NORMAL command!
405                    exec(f"""
406@parent_command.subcommand(name=command_name, description=help_des)
407async def slash_command_(interaction: Interaction{slash_command_params[0]}):
408    ctx = GoldyBot.objects.slash.InteractionToCtx(interaction)
409    try:
410        if self.allowed_to_run(ctx):
411            if also_run_parent_CMD == True: await goldy_parent_command.func(ctx{parent_slash_command_params[1]})
412            await func(ctx{slash_command_params[1]})
413            GoldyBot.logging.log(f"[{MODULE_NAME}] The slash command '{goldy_sub_command.code_name}' was executed.")
414
415    except GoldyBot.errors.MemberHasNoPermsForCommand:
416        if hidden == False:
417            no_perms_embed.description = GoldyBot.utility.msgs.bot.CommandNoPerms.Embed.des.format(ctx.author.mention)
418            message = await ctx.send(embed=no_perms_embed)
419            await asyncio.sleep(15)
420            await message.delete()
421
422    except GoldyBot.errors.GuildNotRegistered:
423        if hidden == False:
424            guild_not_registered_embed.description = GoldyBot.utility.msgs.bot.CommandGuildNotRegistered.Embed.des.format(ctx.author.mention)
425            message = await ctx.send(embed=guild_not_registered_embed)
426            await asyncio.sleep(15)
427            await message.delete()
428
429    except Exception as e:
430        GoldyBot.logging.log("error", e)
431                    """, 
432                    
433                    {"func":goldy_sub_command.func, "parent_command": parent_command, "command_name":goldy_sub_command.command_name, "help_des":goldy_sub_command.get_help_des(), 
434                    "self":goldy_sub_command, "Interaction": GoldyBot.nextcord.Interaction, "GoldyBot": GoldyBot, "asyncio":asyncio, "nextcord":nextcord, 
435                    "no_perms_embed":self.no_perms_embed, "guild_not_registered_embed":self.guild_not_registered_embed, "hidden":goldy_sub_command.is_hidden, 
436                    "also_run_parent_CMD":also_run_parent_CMD, "goldy_parent_command": self}, return_data)
437
438                goldy_sub_command.update_command_object(return_data["slash_command_"])
439
440                GoldyBot.logging.log("info_5", f"[{MODULE_NAME}] Sub Command '{goldy_sub_command.command_name}' of '{parent_command.name}' has been loaded.")
441                return goldy_sub_command
442
443            return inner(func, command_name, required_roles, help_des, slash_options)
444
445        return decorate

Create a lovely sub command from this slash command. 😀 (Only works with slash commands.)

def allowed_to_run(self, ctx):
447    def allowed_to_run(self, ctx):
448        """Checks if the command is allowed to run with current circumstances."""
449        goldy_config = GoldyBot.config.Config(GoldyBot.files.File(GoldyBot.paths.GOLDY_CONFIG_JSON))
450        
451        # Check if guild is registered.
452        #--------------------------------
453        if str(ctx.guild.id) in goldy_config.read("allowed_guilds"):
454            guild_code_name = GoldyBot.cache.FindGuilds(goldy_config).find_object_by_id(ctx.guild.id).code_name
455            guild_config = GoldyBot.utility.guilds.config.GuildConfig(GoldyBot.config.Config(GoldyBot.files.File(GoldyBot.paths.CONFIG + f"/{guild_code_name}/config.json")))
456            
457            if guild_config.is_extension_allowed(self.extension_name):
458
459                if not self.required_roles_ == []:
460                    # If the required roles contain 'bot_dev' and the bot dev is running the command allow the command to execute.
461                    #----------------------------------------------------------------------------------------------------------------
462                    if "bot_dev" in self.required_roles_:
463                        if str(ctx.author.id) in GoldyBot.settings.BOT_DEVS:
464                            return True
465
466                    # If the required roles contain 'bot_admin' and a bot admin is running the command allow the command to execute. (NEW)
467                    #----------------------------------------------------------------------------------------------------------------
468                    if "bot_admin" in self.required_roles_:
469                        if str(ctx.author.id) in goldy_config.read("admin_users"):
470                            return True
471
472                    # Check if member has any of the required roles.
473                    #----------------------------------------------------
474                    for role_code_name in self.required_roles_:
475                        if not role_code_name in ["bot_dev", "bot_admin"]:
476                            role = guild_config.get_role(ctx, role_code_name)
477                            if GoldyBot.objects.member.Member(ctx).has_role(role):
478                                return True
479
480                    raise GoldyBot.errors.MemberHasNoPermsForCommand(f"The member '{ctx.author.name}' does not have the right permissions to use this command.")
481
482                else:
483                    return True
484                
485        else:
486            raise GoldyBot.errors.GuildNotRegistered(f"The guild '{ctx.guild.name}' has not been registered.")

Checks if the command is allowed to run with current circumstances.

guilds_allowed_in: List[int]

Returns the ids of the guilds this command is allowed to function in.

def get_help_des(self) -> str:
511    def get_help_des(self) -> str:
512        """Returns the command's help description."""
513        if self.help_des_ == None:
514            try:
515                return (importlib.import_module(f'.{self.command_name}', package="GoldyBot.utility.msgs")).help_des
516
517            except ImportError:
518                GoldyBot.logging.log("info", f"[{MODULE_NAME}] The command '{self.command_name}' does not have a 'msg' module, so the help command will not display a help description for it.")
519                return "None"
520
521            except AttributeError:
522                GoldyBot.logging.log("info", f"[{MODULE_NAME}] The command '{self.command_name}' does not contain 'help_des' variable in it's 'msg' module, so the help command will not display a help description.")
523                return "None"
524        else:
525            return self.help_des_

Returns the command's help description.