def __init__(self, bot, command, plugin_info, command_info): self.bot = bot self.db = self.bot.db self.cd = CommandRateLimiter(self) self.command = command self.plugin_info = plugin_info self.command_info = command_info self.name = self.command_info.get('name') self.path = self.command_info.get('path') self.category = self.plugin_info.get('category') self.subcategory = self.plugin_info.get('subcategory') self.log = create_logger(self.name.upper()) self.nsfw = False self.cfg = {} self.cache = {} if self.bot.cfg.pref.raw.get('elastic'): self.stats = ElasticHandler(self.bot.cfg.pref.raw.get('elastic'), 'sigma-command') else: self.stats = None self.owner = False self.partner = False self.dmable = False self.requirements = ['send_messages', 'embed_links'] self.alts = [] self.usage = '{pfx}{cmd}' self.desc = 'No description provided.' self.insert_command_info() self.load_command_config()
def __init__(self, bot, command, module_info, command_info): """ :type bot: sigma.core.sigma.ApexSigma :type command: function :type module_info: dict :type command_info: dict """ self.bot = bot self.db = self.bot.db self.cd = CommandRateLimiter(self) self.command = command self.module_info = module_info self.command_info = command_info self.name = self.command_info.get('name') self.path = self.command_info.get('path') self.category = self.module_info.get('category') self.subcategory = self.module_info.get('subcategory') self.log = create_logger(self.name.upper(), shards=self.bot.cfg.dsc.shards) self.nsfw = False self.cfg = ModuleConfig(self) self.owner = False self.partner = False self.dmable = False self.requirements = ['send_messages', 'embed_links'] self.alts = [] self.usage = '{pfx}{cmd}' self.desc = 'No description provided.' self.insert_command_info() self.load_command_config()
def __init__(self, bot, command, plugin_info: dict, command_info: dict): self.bot = bot self.db: Database = self.bot.db self.cd = CommandRateLimiter(self) self.command = command self.plugin_info = plugin_info self.command_info = command_info self.name = self.command_info.get('name') self.path = self.command_info.get('path') self.category = self.plugin_info.get('category') self.subcategory = self.plugin_info.get('subcategory') self.log = create_logger(self.name.upper()) self.nsfw = False self.cfg = {} self.cache = {} self.owner = False self.partner = False self.dmable = False self.requirements = ['send_messages', 'embed_links'] self.alts = [] self.usage = '{pfx}{cmd}' self.desc = 'No description provided.' self.insert_command_info() self.load_command_config()
class SigmaCommand(object): """ The main command object that envelops all processes that should be checked before a command executes and finally execute the command. """ __slots__ = ("bot", "db", "cd", "command", "module_info", "command_info", "name", "path", "category", "subcategory", "log", "nsfw", "cfg", "owner", "partner", "dmable", "requirements", "alts", "usage", "desc") def __init__(self, bot, command, module_info, command_info): """ :type bot: sigma.core.sigma.ApexSigma :type command: function :type module_info: dict :type command_info: dict """ self.bot = bot self.db = self.bot.db self.cd = CommandRateLimiter(self) self.command = command self.module_info = module_info self.command_info = command_info self.name = self.command_info.get('name') self.path = self.command_info.get('path') self.category = self.module_info.get('category') self.subcategory = self.module_info.get('subcategory') self.log = create_logger(self.name.upper(), shards=self.bot.cfg.dsc.shards) self.nsfw = False self.cfg = ModuleConfig(self) self.owner = False self.partner = False self.dmable = False self.requirements = ['send_messages', 'embed_links'] self.alts = [] self.usage = '{pfx}{cmd}' self.desc = 'No description provided.' self.insert_command_info() self.load_command_config() def insert_command_info(self): """ Inserts the command details into the class. """ self.alts = self.command_info.get('alts', []) self.usage = self.command_info.get('usage', '{pfx}{cmd}') self.desc = self.command_info.get('description', 'No description provided.') self.requirements += self.command_info.get('requirements', []) permissions = self.command_info.get('permissions', {}) if permissions: self.nsfw = bool(permissions.get('nsfw')) self.owner = bool(permissions.get('owner')) self.partner = bool(permissions.get('partner')) self.dmable = bool(permissions.get('dmable')) if self.owner: self.desc += '\n(Bot Owner Only)' def load_command_config(self): """ Grabs any details the command should contain such as API keys. """ config_path = f'config/modules/{self.name}.yml' if os.path.exists(config_path): with open(config_path) as config_file: self.cfg.load(yaml.safe_load(config_file)) def resource(self, res_path): """ The from-root path to the resource files of the command. :type res_path: str :rtype: str """ module_path = self.path res_path = f'{module_path}/res/{res_path}' res_path = res_path.replace('\\', '/') return res_path def get_exception(self): """ Gets the exception to catch/except based on the mode that the bot is running in, dev or not dev. :rtype: DummyException or Exception """ return DummyException if self.bot.cfg.pref.dev_mode else Exception def log_command_usage(self, message, args, extime): """ Adds a log line when a command is used. :type message: discord.Message :type args: list[str] :type extime: float """ crst = arrow.get(message.created_at).float_timestamp exdiff = round(extime - crst, 3) if message.guild: cmd_location = f'SRV: {message.guild.name} [{message.guild.id}] | ' cmd_location += f'CHN: #{message.channel.name} [{message.channel.id}]' else: cmd_location = 'DIRECT MESSAGE' author_full = f'{message.author.name}#{message.author.discriminator} [{message.author.id}]' log_text = f'USR: {author_full} | {cmd_location}' if args: log_text += f' | ARGS: {" ".join(args)}' log_text += f' | EX: {exdiff}' self.log.info(log_text) async def add_usage_sumarum(self, message): """ Adds a special currency type when a command is executed. :type message: discord.Message """ trigger = f'usage_{self.name}' if message.guild and not await self.bot.cool_down.on_cooldown( trigger, message.author): await self.bot.cool_down.set_cooldown(trigger, message.author, 450) award = secrets.randbelow(3) if award: await self.db.add_resource(message.author.id, 'sumarum', award, trigger, message, True) @staticmethod async def respond_with_emote(message, icon): """ Responds to a message with an emote reaction. :type message: discord.Message :type icon: discord.Emoji or str """ try: await message.add_reaction(icon) except discord.DiscordException: pass async def update_cooldown(self, author): """ Updates the cooldown timer of the command usage. :type author: discord.User """ cdfile = await self.db[self.db.db_nam].CommandCooldowns.find_one( {'command': self.name}) or {} cooldown = cdfile.get('cooldown') if cooldown: await self.bot.cool_down.set_cooldown(f'{self.name}_core', author, cooldown) @staticmethod async def check_black_args(settings, args): """ Check the command request for any blacklisted arguments. :type settings: dict :type args list[str] :rtype: bool """ black_args = settings.get('blocked_args', []) trigg_args = [arg for arg in args if arg in black_args] return any(trigg_args) async def check_permissions(self, payload): """ Runs all permission checks and initializers. :type payload: sigma.core.mechanics.payload.CommandPayload :rtype: GlobalCommandPermissions, ServerCommandPermissions """ perms = GlobalCommandPermissions(self, payload) await perms.check_black_usr() await perms.check_black_srv() perms.generate_response() perms.check_final() guild_perms = ServerCommandPermissions(self, payload.msg) await guild_perms.check_perms() return perms, guild_perms async def send_requirements_error(self, requirements, payload): """ Sends an error that the command requires additional permissions to execute. :type requirements: sigma.core.mechanics.requirements.CommandRequirements :type payload: sigma.core.mechanics.payload.CommandPayload """ reqs_embed = discord.Embed(color=0xBE1931) reqs_error_title = f'❗ {self.bot.user.name} is missing permissions!' reqs_error_list = '' for req in requirements.missing_list: req = req.replace('_', ' ').title() reqs_error_list += f'\n- {req}' prefix = self.db.get_prefix(payload.settings) reqs_embed.add_field(name=reqs_error_title, value=f'```\n{reqs_error_list}\n```') reqs_embed.set_footer(text=f'{prefix}{self.name} could not execute.') try: await payload.msg.channel.send(embed=reqs_embed) except (discord.Forbidden, discord.NotFound): pass async def add_detailed_stats(self, pld, exec_timestamp): """ Adds detailed statistics for command usage. :param pld: :type pld: sigma.core.mechanics.payload.CommandPayload :type exec_timestamp: float """ cmd_stat = CommandStatistic(self.db, self, pld) cmd_stat.exec_timestamp = exec_timestamp cmd_stat.exec_time = exec_timestamp - arrow.get( pld.msg.created_at).float_timestamp await cmd_stat.save() async def execute(self, payload): """ Runs necessary checks and executes the command function. :type payload: sigma.core.mechanics.payload.CommandPayload """ if self.bot.ready: if payload.msg.guild: delete_command_message = payload.settings.get( 'delete_commands') if delete_command_message: try: await payload.msg.delete() except (discord.Forbidden, discord.NotFound): pass override = check_filter_perms(payload.msg, payload.settings, 'arguments') if await self.check_black_args(payload.settings, payload.args): if not any([ payload.msg.author.guild_permissions.administrator, override ]): await self.respond_with_emote(payload.msg, '🛡') return if not self.bot.cfg.dsc.bot and payload.msg.author.id != self.bot.user.id: self.log.warning(f'{payload.msg.author.name} tried using me.') return if not self.cd.is_cooling(payload.msg): if not await self.bot.cool_down.on_cooldown( f'{self.name}_core', payload.msg.author): await self.update_cooldown(payload.msg.author) perms, guild_perms = await self.check_permissions(payload) exec_timestamp = arrow.utcnow().float_timestamp self.log_command_usage(payload.msg, payload.args, exec_timestamp) self.cd.set_cooling(payload.msg) if perms.permitted: if guild_perms.permitted: requirements = CommandRequirements( self, payload.msg) if requirements.reqs_met: try: executed = False last_error = None client_os_broken_tries = 0 while client_os_broken_tries < 3 and not executed: try: await self.add_detailed_stats( payload, exec_timestamp) await getattr( self.command, self.name)(self, payload) executed = True except CancelledError: pass except aiohttp.ClientOSError as err: last_error = err client_os_broken_tries += 1 await asyncio.sleep(1) if not executed: raise last_error await add_cmd_stat(self) await self.add_usage_sumarum(payload.msg) self.bot.command_count += 1 cmd_ev_pld = CommandEventPayload( self.bot, self, payload) event_task = self.bot.queue.event_runner( 'command', cmd_ev_pld) self.bot.loop.create_task(event_task) except self.get_exception() as e: error = SigmaError(self, e) await error.error_handler(payload) else: await self.respond_with_emote(payload.msg, '📝') await self.send_requirements_error( requirements, payload) else: self.log.warning( 'ACCESS DENIED: This module or command is not allowed in this location.' ) await self.respond_with_emote(payload.msg, '🔒') else: perms.log_unpermitted() await self.respond_with_emote(payload.msg, '⛔') if perms.response: try: await payload.msg.channel.send( embed=perms.response) except (discord.Forbidden, discord.NotFound): pass else: await self.respond_with_emote(payload.msg, '❄') else: await self.respond_with_emote(payload.msg, '🕙')
class SigmaCommand(object): def __init__(self, bot, command, plugin_info: dict, command_info: dict): self.bot = bot self.db: Database = self.bot.db self.cd = CommandRateLimiter(self) self.command = command self.plugin_info = plugin_info self.command_info = command_info self.name = self.command_info.get('name') self.path = self.command_info.get('path') self.category = self.plugin_info.get('category') self.subcategory = self.plugin_info.get('subcategory') self.log = create_logger(self.name.upper()) self.nsfw = False self.cfg = {} self.cache = {} self.owner = False self.partner = False self.dmable = False self.requirements = ['send_messages', 'embed_links'] self.alts = [] self.usage = '{pfx}{cmd}' self.desc = 'No description provided.' self.insert_command_info() self.load_command_config() @staticmethod def get_usr_data(usr: discord.User): usr_data = { 'color': str(usr.color) if isinstance(usr, discord.Member) else '#000000', 'created': str(usr.created_at), 'discriminator': usr.discriminator, 'display_name': usr.display_name, 'game': (usr.activity.name if usr.activity else None) if isinstance(usr, discord.Member) else None, 'id': usr.id, 'name': usr.name, 'status': str(usr.status) if isinstance(usr, discord.Member) else None } return usr_data def insert_command_info(self): self.alts = self.command_info.get('alts', []) self.usage = self.command_info.get('usage', '{pfx}{cmd}') self.desc = self.command_info.get('description', 'No description provided.') self.requirements += self.command_info.get('requirements', []) permissions = self.command_info.get('permissions', {}) if permissions: self.nsfw = bool(permissions.get('nsfw')) self.owner = bool(permissions.get('owner')) self.partner = bool(permissions.get('partner')) self.dmable = bool(permissions.get('dmable')) if self.owner: self.desc += '\n(Bot Owner Only)' def load_command_config(self): config_path = f'config/plugins/{self.name}.yml' if os.path.exists(config_path): with open(config_path) as config_file: self.cfg = yaml.safe_load(config_file) def resource(self, res_path: str): module_path = self.path res_path = f'{module_path}/res/{res_path}' res_path = res_path.replace('\\', '/') return res_path def get_exception(self): if self.bot.cfg.pref.dev_mode: cmd_exception = DummyException else: cmd_exception = Exception return cmd_exception def log_command_usage(self, message: discord.Message, args: list, extime: int): crst = arrow.get(message.created_at).float_timestamp exdiff = round(extime - crst, 3) if message.guild: cmd_location = f'SRV: {message.guild.name} [{message.guild.id}] | ' cmd_location += f'CHN: #{message.channel.name} [{message.channel.id}]' else: cmd_location = 'DIRECT MESSAGE' author_full = f'{message.author.name}#{message.author.discriminator} [{message.author.id}]' log_text = f'USR: {author_full} | {cmd_location}' if args: log_text += f' | ARGS: {" ".join(args)}' log_text += f' | EX: {exdiff}' self.log.info(log_text) # async def add_usage_exp(self, message: discord.Message): # trigger = 'usage_experience' # if message.guild and not await self.bot.cool_down.on_cooldown(trigger, message.author): # award_xp = (600 if message.guild.large else 500) + secrets.randbelow(100) # await self.db.add_resource(message.author.id, 'experience', award_xp, trigger, message, True) # await self.bot.cool_down.set_cooldown(trigger, message.author, 450) @staticmethod async def respond_with_icon(message: discord.Message, icon: str or discord.Emoji): try: await message.add_reaction(icon) except discord.DiscordException: pass async def update_cooldown(self, author): cdfile = await self.db[self.db.db_nam].CommandCooldowns.find_one( {'command': self.name}) or {} cooldown = cdfile.get('cooldown') if cooldown: await self.bot.cool_down.set_cooldown(f'{self.name}_core', author, cooldown) async def check_black_args(self, guild: discord.Guild, args: list): black_args = await self.db.get_guild_settings(guild.id, 'blocked_args') or [] trigg_args = [arg for arg in args if arg in black_args] return any(trigg_args) async def execute(self, message: discord.Message, args: list): if self.bot.ready: if message.guild: delete_command_message = await self.db.get_guild_settings( message.guild.id, 'delete_commands') if delete_command_message: try: await message.delete() except (discord.Forbidden, discord.NotFound): pass if await self.check_black_args(message.guild, args): await self.respond_with_icon(message, '🛡') return if not self.bot.cfg.dsc.bot and message.author.id != self.bot.user.id: self.log.warning(f'{message.author.name} tried using me.') return if not self.cd.is_cooling(message): if not await self.bot.cool_down.on_cooldown( f'{self.name}_core', message.author): await self.update_cooldown(message.author) perms = GlobalCommandPermissions(self, message) await perms.check_black_usr() await perms.check_black_srv() await perms.generate_response() perms.check_final() guild_allowed = ServerCommandPermissions(self, message) await guild_allowed.check_perms() self.log_command_usage(message, args, arrow.utcnow().float_timestamp) self.cd.set_cooling(message) if perms.permitted: if guild_allowed.permitted: requirements = CommandRequirements(self, message) if requirements.reqs_met: try: await getattr(self.command, self.name)(self, message, args) await add_cmd_stat(self) # await self.add_usage_exp(message) self.bot.command_count += 1 event_task = self.bot.queue.event_runner( 'command', self, message, args) self.bot.loop.create_task(event_task) except self.get_exception() as e: error = SigmaError(self, e) await error.error_handler(message, args) else: await self.respond_with_icon(message, '📝') reqs_embed = discord.Embed(color=0xBE1931) reqs_error_title = f'❗ {self.bot.user.name} is missing permissions!' reqs_error_list = '' for req in requirements.missing_list: req = req.replace('_', ' ').title() reqs_error_list += f'\n- {req}' prefix = await self.db.get_prefix(message) reqs_embed.add_field( name=reqs_error_title, value=f'```\n{reqs_error_list}\n```') reqs_embed.set_footer( text= f'{prefix}{self.name} could not execute.') try: await message.channel.send(embed=reqs_embed ) except (discord.Forbidden, discord.NotFound): pass else: self.log.warning( 'ACCESS DENIED: This module or command is not allowed in this location.' ) await self.respond_with_icon(message, '🔒') else: perms.log_unpermitted() await self.respond_with_icon(message, '⛔') if perms.response: try: await message.channel.send(embed=perms.response ) except (discord.Forbidden, discord.NotFound): pass else: await self.respond_with_icon(message, '❄') else: await self.respond_with_icon(message, '🕙')
class SigmaCommand(object): def __init__(self, bot, command, plugin_info, command_info): self.bot = bot self.db = self.bot.db self.cd = CommandRateLimiter(self) self.command = command self.plugin_info = plugin_info self.command_info = command_info self.name = self.command_info.get('name') self.path = self.command_info.get('path') self.category = self.plugin_info.get('category') self.subcategory = self.plugin_info.get('subcategory') self.log = create_logger(self.name.upper()) self.nsfw = False self.cfg = {} self.cache = {} if self.bot.cfg.pref.raw.get('elastic'): self.stats = ElasticHandler(self.bot.cfg.pref.raw.get('elastic'), 'sigma-command') else: self.stats = None self.owner = False self.partner = False self.dmable = False self.requirements = ['send_messages', 'embed_links'] self.alts = [] self.usage = '{pfx}{cmd}' self.desc = 'No description provided.' self.insert_command_info() self.load_command_config() @staticmethod def get_usr_data(usr: discord.User): usr_data = { 'color': str(usr.color) if isinstance(usr, discord.Member) else '#000000', 'created': str(usr.created_at), 'discriminator': usr.discriminator, 'display_name': usr.display_name, 'game': (usr.activity.name if usr.activity else None) if isinstance(usr, discord.Member) else None, 'id': usr.id, 'name': usr.name, 'status': str(usr.status) if isinstance(usr, discord.Member) else None } return usr_data async def add_elastic_stats(self, message: discord.Message, args: list): ath = message.author chn = message.channel if message.channel else None gld = message.guild if message.guild else None ath_data = self.get_usr_data(ath) if chn: chn_data = { 'id': chn.id, 'nsfw': chn.is_nsfw() if not isinstance(chn, discord.DMChannel) else False, 'name': chn.name if not isinstance(chn, discord.DMChannel) else f'{ath.name}#{ath.discriminator}', } else: chn_data = None if gld: gld_data = { 'channels': len(gld.channels), 'created': str(gld.created_at), 'id': gld.id, 'name': gld.name, 'large': gld.large, 'members': { 'users': len([x for x in gld.members if not x.bot]), 'bots': len([x for x in gld.members if x.bot]), 'total': len(gld.members) }, 'region': str(gld.region), 'roles': len(gld.roles) } else: gld_data = None stat_data = { 'command': { 'name': self.name, 'category': self.category, 'nsfw': self.nsfw }, 'arguments': args, 'origin': { 'author': ath_data, 'channel': chn_data, 'guild': gld_data }, 'time': { 'executed': { 'date': arrow.utcnow().format('YYYY-MM-DD'), 'stamp': int(arrow.utcnow().float_timestamp * 1000) }, 'created': { 'date': arrow.get(message.created_at).format('YYYY-MM-DD'), 'stamp': int(arrow.utcnow().float_timestamp * 1000) } } } await self.stats.post(stat_data) def insert_command_info(self): self.alts = self.command_info.get('alts') or [] self.usage = self.command_info.get('usage') or '{pfx}{cmd}' self.desc = self.command_info.get('description') or 'No description provided.' self.requirements += self.command_info.get('requirements') or [] permissions = self.command_info.get('permissions') if permissions: self.nsfw = bool(permissions.get('nsfw')) self.owner = bool(permissions.get('owner')) self.partner = bool(permissions.get('partner')) self.dmable = bool(permissions.get('dmable')) if self.owner: self.desc += '\n(Bot Owner Only)' def load_command_config(self): config_path = f'config/plugins/{self.name}.yml' if os.path.exists(config_path): with open(config_path) as config_file: self.cfg = yaml.safe_load(config_file) def resource(self, res_path: str): module_path = self.path res_path = f'{module_path}/res/{res_path}' res_path = res_path.replace('\\', '/') return res_path def get_exception(self): if self.bot.cfg.pref.dev_mode: cmd_exception = DummyException else: cmd_exception = Exception return cmd_exception def log_command_usage(self, message: discord.Message, args: list): if message.guild: cmd_location = f'SRV: {message.guild.name} [{message.guild.id}] | ' cmd_location += f'CHN: #{message.channel.name} [{message.channel.id}]' else: cmd_location = 'DIRECT MESSAGE' author_full = f'{message.author.name}#{message.author.discriminator} [{message.author.id}]' log_text = f'USR: {author_full} | {cmd_location}' if args: log_text += f' | ARGS: {" ".join(args)}' self.log.info(log_text) async def add_usage_exp(self, message: discord.Message): if message.guild: if not await self.bot.cool_down.on_cooldown('UsageExperience', message.author): award_xp = (600 if message.guild.large else 500) + secrets.randbelow(100) await self.db.add_experience(message.author, message.guild, award_xp) await self.bot.cool_down.set_cooldown('UsageExperience', message.author, 450) @staticmethod async def respond_with_icon(message: discord.Message, icon: str or discord.Emoji): try: await message.add_reaction(icon) except discord.DiscordException: pass async def log_error(self, message: discord.Message, args: list, exception: Exception, error_token: str): if message.guild: gnam = message.guild.name gid = message.guild.id cnam = message.channel.name cid = message.channel.id else: gnam = None gid = None cnam = None cid = None err_file_data = { 'Token': error_token, 'Error': f'{exception}', 'TraceBack': { 'Class': f'{exception.with_traceback}', 'Details': traceback.format_exc() }, 'Message': { 'Command': self.name, 'Arguments': args, 'ID': message.id }, 'Author': { 'Name': f'{message.author.name}#{message.author.discriminator}', 'ID': message.author.id }, 'Guild': { 'Name': gnam, 'ID': gid }, 'Channel': { 'Name': cnam, 'ID': cid } } await self.db[self.bot.cfg.db.database].Errors.insert_one(err_file_data) log_text = f'ERROR: {exception} | TOKEN: {error_token} | TRACE: {exception.with_traceback}' self.log.error(log_text) async def execute(self, message: discord.Message, args: list): if self.bot.ready: if message.guild: delete_command_message = await self.db.get_guild_settings(message.guild.id, 'DeleteCommands') if delete_command_message: try: await message.delete() except discord.Forbidden: pass except discord.NotFound: pass if not self.bot.cfg.dsc.bot and message.author.id != self.bot.user.id: self.log.warning(f'{message.author.name} tried using me.') return if not self.cd.is_cooling(message): perms = GlobalCommandPermissions(self, message) await perms.check_black_usr() await perms.check_black_srv() await perms.generate_response() perms.check_final() guild_allowed = ServerCommandPermissions(self, message) await guild_allowed.check_perms() self.log_command_usage(message, args) self.cd.set_cooling(message) if perms.permitted: if guild_allowed.permitted: requirements = CommandRequirements(self, message) if requirements.reqs_met: try: await getattr(self.command, self.name)(self, message, args) await add_cmd_stat(self) if self.stats: await self.add_elastic_stats(message, args) await self.add_usage_exp(message) self.bot.command_count += 1 self.bot.loop.create_task(self.bot.queue.event_runner('command', self, message, args)) except self.get_exception() as e: await self.respond_with_icon(message, '❗') err_token = secrets.token_hex(16) await self.log_error(message, args, e, err_token) prefix = await self.db.get_prefix(message) title = '❗ An Error Occurred!' err_text = 'Something seems to have gone wrong.' err_text += '\nPlease send this token to our support server.' err_text += f'\nThe invite link is in the **{prefix}help** command.' err_text += f'\nToken: **{err_token}**' error_embed = discord.Embed(color=0xBE1931) error_embed.add_field(name=title, value=err_text) try: await message.channel.send(embed=error_embed) except discord.Forbidden: pass else: await self.respond_with_icon(message, '❗') reqs_embed = discord.Embed(color=0xBE1931) reqs_error_title = f'❗ Sigma is missing permissions!' reqs_error_list = '' for req in requirements.missing_list: req = req.replace('_', ' ').title() reqs_error_list += f'\n- {req}' prefix = await self.db.get_prefix(message) reqs_embed.add_field(name=reqs_error_title, value=f'```\n{reqs_error_list}\n```') reqs_embed.set_footer(text=f'{prefix}{self.name} could not execute.') try: await message.channel.send(embed=reqs_embed) except discord.Forbidden: pass else: self.log.warning('ACCESS DENIED: This module or command is not allowed in this location.') await self.respond_with_icon(message, '⛔') else: perms.log_unpermitted() await self.respond_with_icon(message, '⛔') if perms.response: try: await message.channel.send(embed=perms.response) except discord.Forbidden: pass else: await self.respond_with_icon(message, '🕙')
class SigmaCommand(object): def __init__(self, bot, command, plugin_info, command_info): self.bot = bot self.db = self.bot.db self.cd = CommandRateLimiter(self) self.command = command self.plugin_info = plugin_info self.command_info = command_info self.name = self.command_info.get('name') self.path = self.command_info.get('path') self.category = self.plugin_info.get('category') self.subcategory = self.plugin_info.get('subcategory') self.log = create_logger(self.name.upper()) self.nsfw = False self.cfg = {} self.cache = {} self.owner = False self.partner = False self.dmable = False self.requirements = ['send_messages', 'embed_links'] self.alts = [] self.usage = '{pfx}{cmd}' self.desc = 'No description provided.' self.insert_command_info() self.load_command_config() @staticmethod def get_usr_data(usr: discord.User): usr_data = { 'color': str(usr.color) if isinstance(usr, discord.Member) else '#000000', 'created': str(usr.created_at), 'discriminator': usr.discriminator, 'display_name': usr.display_name, 'game': (usr.activity.name if usr.activity else None) if isinstance(usr, discord.Member) else None, 'id': usr.id, 'name': usr.name, 'status': str(usr.status) if isinstance(usr, discord.Member) else None } return usr_data def insert_command_info(self): self.alts = self.command_info.get('alts', []) self.usage = self.command_info.get('usage', '{pfx}{cmd}') self.desc = self.command_info.get('description', 'No description provided.') self.requirements += self.command_info.get('requirements', []) permissions = self.command_info.get('permissions', {}) if permissions: self.nsfw = bool(permissions.get('nsfw')) self.owner = bool(permissions.get('owner')) self.partner = bool(permissions.get('partner')) self.dmable = bool(permissions.get('dmable')) if self.owner: self.desc += '\n(Bot Owner Only)' def load_command_config(self): config_path = f'config/plugins/{self.name}.yml' if os.path.exists(config_path): with open(config_path) as config_file: self.cfg = yaml.safe_load(config_file) def resource(self, res_path: str): module_path = self.path res_path = f'{module_path}/res/{res_path}' res_path = res_path.replace('\\', '/') return res_path def get_exception(self): if self.bot.cfg.pref.dev_mode: cmd_exception = DummyException else: cmd_exception = Exception return cmd_exception def log_command_usage(self, message: discord.Message, args: list, extime: int): crst = arrow.get(message.created_at).float_timestamp exdiff = round(extime - crst, 3) if message.guild: cmd_location = f'SRV: {message.guild.name} [{message.guild.id}] | ' cmd_location += f'CHN: #{message.channel.name} [{message.channel.id}]' else: cmd_location = 'DIRECT MESSAGE' author_full = f'{message.author.name}#{message.author.discriminator} [{message.author.id}]' log_text = f'USR: {author_full} | {cmd_location}' if args: log_text += f' | ARGS: {" ".join(args)}' log_text += f' | EX: {exdiff}' self.log.info(log_text) async def add_usage_exp(self, message: discord.Message): if message.guild: if not await self.bot.cool_down.on_cooldown( 'UsageExperience', message.author): award_xp = (600 if message.guild.large else 500) + secrets.randbelow(100) await self.db.add_experience(message.author, message.guild, award_xp) await self.bot.cool_down.set_cooldown('UsageExperience', message.author, 450) @staticmethod async def respond_with_icon(message: discord.Message, icon: str or discord.Emoji): try: await message.add_reaction(icon) except discord.DiscordException: pass async def log_error(self, message: discord.Message, args: list, exception: Exception, error_token: str): if message.guild: gnam = message.guild.name gid = message.guild.id cnam = message.channel.name cid = message.channel.id else: gnam = None gid = None cnam = None cid = None err_file_data = { 'Token': error_token, 'Error': f'{exception}', 'TraceBack': { 'Class': f'{exception.with_traceback}', 'Details': traceback.format_exc() }, 'Message': { 'Command': self.name, 'Arguments': args, 'ID': message.id }, 'Author': { 'Name': f'{message.author.name}#{message.author.discriminator}', 'ID': message.author.id }, 'Guild': { 'Name': gnam, 'ID': gid }, 'Channel': { 'Name': cnam, 'ID': cid } } if self.bot.cfg.pref.errorlog_channel: err_chn_id = self.bot.cfg.pref.errorlog_channel error_chn = discord.utils.find(lambda x: x.id == err_chn_id, self.bot.get_all_channels()) await send_error_embed(error_chn, err_file_data) await self.db[self.bot.cfg.db.database ].Errors.insert_one(err_file_data) log_text = f'ERROR: {exception} | TOKEN: {error_token} | TRACE: {exception.with_traceback}' self.log.error(log_text) async def send_error_message(self, message: discord.Message, args: list, e: Exception): await self.respond_with_icon(message, '❗') err_token = secrets.token_hex(16) await self.log_error(message, args, e, err_token) prefix = await self.db.get_prefix(message) name = self.bot.user.name if isinstance(e, discord.Forbidden): title = '❗ Error: Forbidden!' err_text = f'It seems that you tried running something that {name} isn\'t allowed to do.' err_text += f' This is something when {name} is missing permissions for stuff like' err_text += ' sending messages, adding reactions, uploading files, etc.' err_text += ' The error has been relayed to the developers. If you feel like dropping by' err_text += f' and asking about it, the invite link is in the **{prefix}help** command.' elif isinstance(e, discord.NotFound): title = '❗ Error: Not Found!' err_text = 'It might have been a target that got removed while the command was executing,' err_text += f' whatever it was, {name} couldn\'t find it and encountered an error.' err_text += ' The error has been relayed to the developers. If you feel like dropping by' err_text += f' and asking about it, the invite link is in the **{prefix}help** command.' else: title = '❗ An Unhandled Error Occurred!' err_text = 'Something seems to have gone wrong.' err_text += '\nPlease be patient while we work on fixing the issue.' err_text += '\nThe error has been relayed to the developers.' err_text += f'\nIf you feel like dropping by and asking about it,' err_text += f'\nthe invite link is in the **{prefix}help** command.' error_embed = discord.Embed(color=0xBE1931) error_embed.add_field(name=title, value=err_text) error_embed.set_footer(text=f'Token: {err_token}') try: await message.channel.send(embed=error_embed) except discord.Forbidden: pass async def update_cooldown(self, author): cdfile = await self.db[self.db.db_nam].CommandCooldowns.find_one( {'Command': self.name}) or {} cooldown = cdfile.get('Cooldown') if cooldown: await self.bot.cool_down.set_cooldown(f'{self.name}_core', author, cooldown) async def execute(self, message: discord.Message, args: list): if self.bot.ready: if message.guild: delete_command_message = await self.db.get_guild_settings( message.guild.id, 'DeleteCommands') if delete_command_message: try: await message.delete() except discord.Forbidden: pass except discord.NotFound: pass if not self.bot.cfg.dsc.bot and message.author.id != self.bot.user.id: self.log.warning(f'{message.author.name} tried using me.') return if not self.cd.is_cooling(message): if not await self.bot.cool_down.on_cooldown( f'{self.name}_core', message.author): await self.update_cooldown(message.author) perms = GlobalCommandPermissions(self, message) await perms.check_black_usr() await perms.check_black_srv() await perms.generate_response() perms.check_final() guild_allowed = ServerCommandPermissions(self, message) await guild_allowed.check_perms() self.log_command_usage(message, args, arrow.utcnow().float_timestamp) self.cd.set_cooling(message) if perms.permitted: if guild_allowed.permitted: requirements = CommandRequirements(self, message) if requirements.reqs_met: try: await getattr(self.command, self.name)(self, message, args) await add_cmd_stat(self) await self.add_usage_exp(message) self.bot.command_count += 1 event_task = self.bot.queue.event_runner( 'command', self, message, args) self.bot.loop.create_task(event_task) except self.get_exception() as e: await self.send_error_message( message, args, e) else: await self.respond_with_icon(message, '❗') reqs_embed = discord.Embed(color=0xBE1931) reqs_error_title = f'❗ {self.bot.user.name} is missing permissions!' reqs_error_list = '' for req in requirements.missing_list: req = req.replace('_', ' ').title() reqs_error_list += f'\n- {req}' prefix = await self.db.get_prefix(message) reqs_embed.add_field( name=reqs_error_title, value=f'```\n{reqs_error_list}\n```') reqs_embed.set_footer( text= f'{prefix}{self.name} could not execute.') try: await message.channel.send(embed=reqs_embed ) except discord.Forbidden: pass else: self.log.warning( 'ACCESS DENIED: This module or command is not allowed in this location.' ) await self.respond_with_icon(message, '⛔') else: perms.log_unpermitted() await self.respond_with_icon(message, '⛔') if perms.response: try: await message.channel.send(embed=perms.response ) except discord.Forbidden: pass else: await self.respond_with_icon(message, '❄') else: await self.respond_with_icon(message, '🕙')