async def on_command_error(self, ctx, exception): def human_join(strings): if len(strings) <= 2: return ' or '.join(strings) else: return ', '.join( strings[:len(strings) - 1]) + ' or ' + strings[-1] if isinstance(exception, commands.BadUnionArgument): msg = 'Could not find the specified ' + human_join( [c.__name__ for c in exception.converters]) await ctx.trigger_typing() await ctx.send( embed=discord.Embed(color=discord.Color.red(), description=msg) ) elif isinstance(exception, commands.BadArgument): await ctx.trigger_typing() await ctx.send(embed=discord.Embed(color=discord.Color.red(), description=str(exception))) elif isinstance(exception, commands.MissingRequiredArgument): await ctx.invoke(self.get_command('help'), command=str(ctx.command)) elif isinstance(exception, commands.CommandNotFound): logger.warning(error('CommandNotFound: ' + str(exception))) elif isinstance(exception, commands.CheckFailure): logger.warning(error('CheckFailure: ' + str(exception))) else: logger.error(error('Unexpected exception:'), exc_info=exception)
async def validate_api_token(self): try: self.config.modmail_api_token except KeyError: logger.critical(error(f'MODMAIL_API_TOKEN not found.')) logger.critical( error('Set a config variable called ' 'MODMAIL_API_TOKEN with a token from ' 'https://dashboard.modmail.tk.')) logger.critical( error('If you want to self-host logs, ' 'input a MONGO_URI config variable.')) logger.critical( error('A Modmail API token is not needed ' 'if you are self-hosting logs.')) return await self.logout() else: valid = await self.api.validate_token() if not valid: logger.critical( error('Invalid MODMAIL_API_TOKEN - get one ' 'from https://dashboard.modmail.tk')) return await self.logout() user = await self.api.get_user_info() username = user['user']['username'] logger.info(info('Validated token.')) logger.info(info('GitHub user: ' + username))
def run(self, *args, **kwargs): try: self.loop.run_until_complete(self.start(self.token)) except discord.LoginFailure: logger.critical(error("Invalid token")) except KeyboardInterrupt: pass except Exception: logger.critical(error("Fatal exception"), exc_info=True) finally: try: self.metadata_task.cancel() self.loop.run_until_complete(self.metadata_task) except asyncio.CancelledError: logger.debug(info("data_task has been cancelled.")) try: self.autoupdate_task.cancel() self.loop.run_until_complete(self.autoupdate_task) except asyncio.CancelledError: logger.debug(info("autoupdate_task has been cancelled.")) self.loop.run_until_complete(self.logout()) for task in asyncio.Task.all_tasks(): task.cancel() try: self.loop.run_until_complete( asyncio.gather(*asyncio.Task.all_tasks())) except asyncio.CancelledError: logger.debug(info("All pending tasks has been cancelled.")) finally: self.loop.run_until_complete(self.session.close()) self.loop.close() logger.info(error(" - Shutting down bot - "))
async def on_command_error(self, context, exception): if isinstance(exception, commands.BadUnionArgument): msg = "Could not find the specified " + human_join( [c.__name__ for c in exception.converters]) await context.trigger_typing() await context.send( embed=discord.Embed(color=discord.Color.red(), description=msg) ) elif isinstance(exception, commands.BadArgument): await context.trigger_typing() await context.send(embed=discord.Embed(color=discord.Color.red(), description=str(exception))) elif isinstance(exception, commands.CommandNotFound): logger.warning(error("CommandNotFound: " + str(exception))) elif isinstance(exception, commands.MissingRequiredArgument): await context.send_help(context.command) elif isinstance(exception, commands.CheckFailure): for check in context.command.checks: if not await check(context) and hasattr(check, "fail_msg"): await context.send(embed=discord.Embed( color=discord.Color.red(), description=check.fail_msg)) logger.warning(error("CheckFailure: " + str(exception))) else: logger.error(error("Unexpected exception:"), exc_info=exception)
async def validate_database_connection(self): try: await self.db.command("buildinfo") except Exception as exc: logger.critical( error("Something went wrong " "while connecting to the database.")) message = f"{type(exc).__name__}: {str(exc)}" logger.critical(error(message)) if "ServerSelectionTimeoutError" in message: logger.critical( error("This may have been caused by not whitelisting " "IPs correctly. Make sure to whitelist all " "IPs (0.0.0.0/0) https://i.imgur.com/mILuQ5U.png")) if "OperationFailure" in message: logger.critical( error( "This is due to having invalid credentials in your MONGO_URI." )) logger.critical( error( "Recheck the username/password and make sure to url encode them. " "https://www.urlencoder.io/")) return await self.logout() else: logger.info(info("Successfully connected to the database."))
async def on_command_error(self, context, exception): if isinstance(exception, (commands.MissingRequiredArgument, commands.UserInputError)): await context.invoke(self.get_command('help'), command=str(context.command)) elif isinstance(exception, commands.CommandNotFound): logger.warning(error('CommandNotFound: ' + str(exception))) else: logger.error(error('Unexpected exception:'), exc_info=exception)
async def validate_database_connection(self): try: await self.db.command('buildinfo') except Exception as exc: logger.critical(error('Something went wrong ' 'while connecting to the database.')) logger.critical(error(f'{type(exc).__name__}: {str(exc)}')) return await self.logout() else: logger.info(info('Successfully connected to the database.'))
async def load_plugin(self, username, repo, plugin_name, branch): ext = f"plugins.{username}-{repo}-{branch}.{plugin_name}.{plugin_name}" dirname = f"plugins/{username}-{repo}-{branch}/{plugin_name}" if "requirements.txt" in os.listdir(dirname): # Install PIP requirements venv = hasattr(sys, "real_prefix") # in a virtual env user_install = "--user" if not venv else "" try: if os.name == "nt": # Windows await self.bot.loop.run_in_executor( None, self._asubprocess_run, f"pip install -r {dirname}/requirements.txt {user_install} -q -q", ) else: await self.bot.loop.run_in_executor( None, self._asubprocess_run, f"python3 -m pip install -U -r {dirname}/" f"requirements.txt {user_install} -q -q", ) # -q -q (quiet) # so there's no terminal output unless there's an error except subprocess.CalledProcessError as exc: err = exc.stderr.decode("utf8").strip() if err: msg = f"Requirements Download Error: {username}/{repo}@{branch}[{plugin_name}]" logger.error(error(msg)) raise DownloadError( f"Unable to download requirements: ```\n{err}\n```" ) from exc else: if not os.path.exists(site.USER_SITE): os.makedirs(site.USER_SITE) sys.path.insert(0, site.USER_SITE) await asyncio.sleep(0.5) try: self.bot.load_extension(ext) except commands.ExtensionError as exc: msg = f"Plugin Load Failure: {username}/{repo}@{branch}[{plugin_name}]" logger.error(error(msg)) raise DownloadError("Invalid plugin") from exc else: msg = f"Loaded Plugin: {username}/{repo}@{branch}[{plugin_name}]" logger.info(info(msg))
def barcode(req, format, barcode=''): if format == 'svg': bc = Code39(barcode, add_checksum=False) mimetype = 'image/svg+xml' else: if ImageWriter is None: return utils.error(req, _('PIL is not installed.')) bc = Code39(barcode, ImageWriter(), add_checksum=False) mimetype = 'image/{0}'.format(format) response = HttpResponse(mimetype=mimetype) try: bc.write(response, options={'format': format.upper()}) except KeyError: return utils.error(req, _('Unsupported barcode format.')) return response
async def download_initial_plugins(self): await self.bot._connected.wait() for i in self.bot.config.plugins: parsed_plugin = self.parse_plugin(i) try: await self.download_plugin_repo(*parsed_plugin[:-1]) except DownloadError as exc: msg = f'{parsed_plugin[0]}/{parsed_plugin[1]} - {exc}' logger.error(error(msg)) else: try: await self.load_plugin(*parsed_plugin) except DownloadError as exc: msg = f'{parsed_plugin[0]}/{parsed_plugin[1]} - {exc}' logger.error(error(msg))
async def predicate(ctx): has_perm = await check_permissions(ctx, ctx.command.qualified_name, permission_level) if not has_perm and ctx.command.qualified_name != 'help': logger.error(error(f'You does not have permission to use this command: ' f'`{ctx.command.qualified_name}` ({permission_level.name}).')) return has_perm
async def send_error_message(self, msg): # pylint: disable=W0221 logger.warning(error(f"CommandNotFound: {msg}")) embed = Embed(color=Color.red()) embed.set_footer( text=f'Command/Category "{self.context.kwargs.get("command")}" not found.' ) choices = set() for name, cmd in self.context.bot.all_commands.items(): if not cmd.hidden: choices.add(name) command = self.context.kwargs.get("command") closest = get_close_matches(command, choices) if closest: embed.add_field( name=f"Perhaps you meant:", value="\n".join(f"`{x}`" for x in closest) ) else: embed.title = "Cannot find command or category" embed.set_footer( text=f'Type "{self.clean_prefix}{self.command_attrs["name"]}" ' "for a list of all available commands." ) await self.get_destination().send(embed=embed)
def export_group_excel(req, gid): if Workbook is None: return utils.error(u'Die Excelerweiterung ist nicht installiert.') group = StudentGroup.objects.select_related().get(id=int(gid)) wb = Workbook() ws = wb.get_active_sheet() ws.cell('A1').value = unicode(group) ws.cell('A3').value = u'Firma' ws.cell('B3').value = u'Name' ws.cell('C3').value = u'Vorname' row = 4 for s in group.students.filter(finished=False).order_by( 'company__short_name', 'lastname'): if s.company: comp = s.company.short_name else: comp = u'-' ws.cell('A{0}'.format(row)).value = comp ws.cell('B{0}'.format(row)).value = s.lastname ws.cell('C{0}'.format(row)).value = s.firstname row += 1 dest = os.path.join( settings.LATEX_BUILD_DIR, 'excel_exp_{0}.xlsx'.format(time.time()) ) wb.save(dest) with open(dest, 'rb') as fp: response = HttpResponse( fp.read(), content_type='application/vnd.' 'openxmlformats-officedocument.spreadsheetml.sheet' ) response['Content-Disposition'] = 'attachment; filename="{0}.xlsx"'.format( group.name()) os.remove(dest) return response
def _load_extensions(self): """Adds commands automatically""" self.remove_command('help') logger.info(LINE) logger.info(info(' _____ _ _ ______ _____ ')) logger.info(info('| __ \(_) | | | ____| __ \ ')) logger.info(info('| | | |_ ___ ___ ___ _ __ __| | | |__ | |__) |')) logger.info(info('| | | | / __|/ __/ _ \| __/ _` | | __| | _ / ')) logger.info(info('| |__| | \__ \ (_| (_) | | | (_| |_| | | | \ \ ')) logger.info( info('|_____/|_|___/\___\___/|_| \__,_(_)_| |_| \_\ ')) logger.info(info(f'v{__version__}')) logger.info(info('Authors: kyb3r, fourjr, Taaku18')) logger.info(LINE) for file in os.listdir('cogs'): if not file.endswith('.py'): continue cog = f'cogs.{file[:-3]}' logger.info(info(f'Loading {cog}')) try: self.load_extension(cog) except Exception: logger.exception(error(f'Failed to load {cog}'))
async def predicate(ctx): """ Parameters ---------- ctx : Context The current discord.py `Context`. Returns ------- bool `True` if the author is a bot owner, or has the ``administrator`` permission. Or if the author has all of the required permissions. Otherwise, `False`. """ if await ctx.bot.is_owner(ctx.author): return True resolved = ctx.channel.permissions_for(ctx.author) has_perm = resolved.administrator or all( getattr(resolved, name, None) == value for name, value in perms.items()) if not has_perm: restrictions = ', '.join( f'{name.replace("_", " ").title()} ({"yes" if value else "no"})' for name, value in perms.items()) logger.error( error(f'Command `{ctx.command.qualified_name}` processes ' f'the following restrictions(s): {restrictions}.')) return has_perm
def _configure_logging(self): level_text = self.config.log_level.upper() logging_levels = { "CRITICAL": logging.CRITICAL, "ERROR": logging.ERROR, "WARNING": logging.WARNING, "INFO": logging.INFO, "DEBUG": logging.DEBUG, } log_file_name = self.config.token.split(".")[0] ch_debug = logging.FileHandler(os.path.join(temp_dir, f"{log_file_name}.log"), mode="a+") ch_debug.setLevel(logging.DEBUG) formatter_debug = FileFormatter( "%(asctime)s %(filename)s - " "%(levelname)s: %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) ch_debug.setFormatter(formatter_debug) logger.addHandler(ch_debug) log_level = logging_levels.get(level_text) logger.info(LINE) if log_level is not None: logger.setLevel(log_level) ch.setLevel(log_level) logger.info(info("Logging level: " + level_text)) else: logger.info(error("Invalid logging level set. ")) logger.info(info("Using default logging level: INFO"))
async def download_initial_plugins(self): await self.bot._connected.wait() for i in self.bot.config.plugins: username, repo, name, branch = self.parse_plugin(i) try: await self.download_plugin_repo(username, repo, branch) except DownloadError as exc: msg = f"{username}/{repo}@{branch} - {exc}" logger.error(error(msg)) else: try: await self.load_plugin(username, repo, name, branch) except DownloadError as exc: msg = f"{username}/{repo}@{branch}[{name}] - {exc}" logger.error(error(msg))
async def reply(self, message: discord.Message, anonymous: bool = False) -> None: if not message.content and not message.attachments: raise MissingRequiredArgument(param(name='msg')) if all(not g.get_member(self.id) for g in self.bot.guilds): return await message.channel.send( embed=discord.Embed( color=discord.Color.red(), description='Your message could not be delivered since ' 'the recipient shares no servers with the bot.' )) tasks = [] try: await self.send(message, destination=self.recipient, from_mod=True, anonymous=anonymous) except Exception: logger.info(error('Message delivery failed:'), exc_info=True) tasks.append(message.channel.send( embed=discord.Embed( color=discord.Color.red(), description='Your message could not be delivered as ' 'the recipient is only accepting direct ' 'messages from friends, or the bot was ' 'blocked by the recipient.' ) )) else: # Send the same thing in the thread channel. tasks.append( self.send(message, destination=self.channel, from_mod=True, anonymous=anonymous) ) tasks.append( self.bot.api.append_log(message, self.channel.id, type_='anonymous' if anonymous else 'thread_message' )) if self.close_task is not None: # Cancel closing if a thread message is sent. await self.cancel_closure() tasks.append( self.channel.send( embed=discord.Embed( color=discord.Color.red(), description='Scheduled close has been cancelled.' ) ) ) await asyncio.gather(*tasks)
async def download_initial_plugins(self): await self.bot._connected.wait() for i in self.bot.config.plugins: parsed_plugin = self.parse_plugin(i) try: await self.download_plugin_repo(*parsed_plugin[:-1]) except DownloadError as exc: msg = f'{parsed_plugin[0]}/{parsed_plugin[1]} - {exc}' logger.error(error(msg)) else: try: await self.load_plugin(*parsed_plugin) except DownloadError as exc: msg = f'{parsed_plugin[0]}/{parsed_plugin[1]} - {exc}' logger.error(error(msg)) await async_all(env() for env in self.bot.extra_events.get('on_plugin_ready', [])) logger.debug(info('on_plugin_ready called.'))
async def reply(self, message: discord.Message, anonymous: bool = False) -> None: if not message.content and not message.attachments: raise MissingRequiredArgument(param(name="msg")) if all(not g.get_member(self.id) for g in self.bot.guilds): return await message.channel.send(embed=discord.Embed( color=discord.Color.red(), description="Votre message na pas pu être envoyé :" "le destinataire nest pas sur le serveur.", )) tasks = [] try: await self.send(message, destination=self.recipient, from_mod=True, anonymous=anonymous) except Exception: logger.info(error("Erreur :"), exc_info=True) tasks.append( message.channel.send(embed=discord.Embed( color=discord.Color.red(), description="Votre message na pas pu être envoyé :" "Erreur destinataire " "Erreur destinataire " "Le bot est bloqué par le destinataire.", ))) else: # Send the same thing in the thread channel. tasks.append( self.send( message, destination=self.channel, from_mod=True, anonymous=anonymous, )) tasks.append( self.bot.api.append_log( message, self.channel.id, type_="anonymous" if anonymous else "thread_message", )) # Cancel closing if a thread message is sent. if self.close_task is not None: await self.cancel_closure() tasks.append( self.channel.send(embed=discord.Embed( color=discord.Color.red(), description="Annulé", ))) await asyncio.gather(*tasks)
async def on_ready(self): """Bot startup, sets uptime.""" await self._connected.wait() logger.info(LINE) logger.info(info("Client ready.")) logger.info(LINE) logger.info(info(f"Logged in as: {self.user}")) logger.info(info(f"User ID: {self.user.id}")) logger.info(info(f"Prefix: {self.prefix}")) logger.info( info(f"Guild Name: {self.guild.name if self.guild else 'None'}")) logger.info(info(f"Guild ID: {self.guild.id if self.guild else 0}")) logger.info(LINE) if not self.guild: logger.error( error("WARNING - The GUILD_ID " "provided does not exist!")) else: await self.threads.populate_cache() # Wait until config cache is populated with stuff from db await self.config.wait_until_ready() # closures closures = self.config.closures.copy() logger.info( info(f"There are {len(closures)} thread(s) " "pending to be closed.")) for recipient_id, items in closures.items(): after = (datetime.fromisoformat(items["time"]) - datetime.utcnow()).total_seconds() if after < 0: after = 0 thread = await self.threads.find(recipient_id=int(recipient_id)) if not thread: # If the channel is deleted self.config.closures.pop(str(recipient_id)) await self.config.update() continue await thread.close( closer=self.get_user(items["closer_id"]), after=after, silent=items["silent"], delete_channel=items["delete_channel"], message=items["message"], auto_close=items.get("auto_close", False), ) logger.info(LINE)
def main_color(self) -> typing.Union[discord.Color, int]: color = self.config.get("main_color") if not color: return discord.Color.blurple() try: color = int(color.lstrip("#"), base=16) except ValueError: logger.error(error("Invalid main_color provided")) return discord.Color.blurple() else: return color
def mod_color(self) -> typing.Union[discord.Color, int]: color = self.config.get('mod_color') if not color: return discord.Color.green() try: color = int(color.lstrip('#'), base=16) except ValueError: logger.error(error('Invalid mod_color provided')) return discord.Color.green() else: return color
async def plugin_remove(self, ctx, *, plugin_name: str): """Remove a plugin.""" if plugin_name in self.registry: details = self.registry[plugin_name] plugin_name = ( details["repository"] + "/" + plugin_name + "@" + details["branch"] ) if plugin_name in self.bot.config.plugins: try: username, repo, name, branch = self.parse_plugin(plugin_name) self.bot.unload_extension( f"plugins.{username}-{repo}-{branch}.{name}.{name}" ) except Exception: pass self.bot.config.plugins.remove(plugin_name) try: # BUG: Local variables 'username' and 'repo' might be referenced before assignment if not any( i.startswith(f"{username}/{repo}") for i in self.bot.config.plugins ): # if there are no more of such repos, delete the folder def onerror(func, path, exc_info): # pylint: disable=W0613 if not os.access(path, os.W_OK): # Is the error an access error? os.chmod(path, stat.S_IWUSR) func(path) shutil.rmtree( f"plugins/{username}-{repo}-{branch}", onerror=onerror ) except Exception as exc: logger.error(str(exc)) self.bot.config.plugins.append(plugin_name) logger.error(error(exc)) raise exc await self.bot.config.update() embed = discord.Embed( description="The plugin is uninstalled and all its data is erased.", color=self.bot.main_color, ) await ctx.send(embed=embed) else: embed = discord.Embed( description="That plugin is not installed.", color=self.bot.main_color ) await ctx.send(embed=embed)
def main_color(self): color = self.config.get('main_color') if not color: return discord.Color.blurple() try: color = int(color.lstrip('#'), base=16) except ValueError: logger.error(error('Invalid main_color provided')) return discord.Color.blurple() else: return color
def recipient_color(self): color = self.config.get('recipient_color') if not color: return discord.Color.gold() try: color = int(color.lstrip('#'), base=16) except ValueError: logger.error(error('Invalid recipient_color provided')) return discord.Color.gold() else: return color
def validate(cls): # Check current machine can run this backup process or not. # We compare hostname in config.json with runtime hostname, # if they are equal, current machine has privilege to run this script. if utils.hostname() != Configure.get('hostname'): utils.error( 'Error: Hostname not match. \n' + 'Run command [configure] to redump hostname and try again.') # Validate required input required_keys = [ 'aws_access_key_id', 'aws_secret_access_key', 'region_name', 'bucket', 'backup_paths', 'keep_days', 'retry', ] for key in required_keys: if utils.is_empty(cls.__config[key]): utils.error('Error: Config item [%s] is empty' % key)
async def download_initial_plugins(self): await self.bot._connected.wait() for i in self.bot.config.plugins: username, repo, name, branch = self.parse_plugin(i) try: await self.download_plugin_repo(username, repo, branch) except DownloadError as exc: msg = f"{username}/{repo}@{branch} - {exc}" logger.error(error(msg)) else: try: await self.load_plugin(username, repo, name, branch) except DownloadError as exc: msg = f"{username}/{repo}@{branch}[{name}] - {exc}" logger.error(error(msg)) await async_all( env() for env in self.bot.extra_events.get("on_plugin_ready", [])) logger.debug(info("on_plugin_ready called."))
async def on_ready(self): """Bot startup, sets uptime.""" await self._connected.wait() logger.info(LINE) logger.info(info('Client ready.')) logger.info(LINE) logger.info(info(f'Logged in as: {self.user}')) logger.info(info(f'User ID: {self.user.id}')) logger.info(info(f'Guild ID: {self.guild.id if self.guild else 0}')) logger.info(LINE) if not self.guild: logger.error( error('WARNING - The GUILD_ID ' 'provided does not exist!')) else: await self.threads.populate_cache() # Wait until config cache is populated with stuff from db await self.config.wait_until_ready() # closures closures = self.config.closures.copy() logger.info( info(f'There are {len(closures)} thread(s) ' 'pending to be closed.')) for recipient_id, items in closures.items(): after = (datetime.fromisoformat(items['time']) - datetime.utcnow()).total_seconds() if after < 0: after = 0 recipient = self.get_user(int(recipient_id)) thread = await self.threads.find(recipient=recipient) if not thread: # If the recipient is gone or channel is deleted self.config.closures.pop(str(recipient_id)) await self.config.update() continue # TODO: Low priority, # Retrieve messages/replies when bot is down, from history? await thread.close(closer=self.get_user(items['closer_id']), after=after, silent=items['silent'], delete_channel=items['delete_channel'], message=items['message']) logger.info(LINE)
def _load_extensions(self): """Adds commands automatically""" logger.info(LINE) logger.info(info("┌┬┐┌─┐┌┬┐┌┬┐┌─┐┬┬")) logger.info(info("││││ │ │││││├─┤││")) logger.info(info("┴ ┴└─┘─┴┘┴ ┴┴ ┴┴┴─┘")) logger.info(info(f"v{__version__}")) logger.info(info("Authors: kyb3r, fourjr, Taaku18")) logger.info(LINE) for file in os.listdir("cogs"): if not file.endswith(".py"): continue cog = f"cogs.{file[:-3]}" logger.info(info(f"Loading {cog}")) try: self.load_extension(cog) except Exception: logger.exception(error(f"Failed to load {cog}"))