async def send_birdsong(ctx, bird: str, on_error=None, message=None): """Gets a bird sound and sends it to the user. `ctx` - Discord context object\n `bird` (str) - bird picture to send\n `on_error` (function) - function to run when an error occurs\n `message` (str) - text message to send before bird picture """ if bird == "": logger.error("error - bird is blank") await ctx.send( "**There was an error fetching birds.**\n*Please try again.*") if on_error is not None: on_error(ctx) return delete = await ctx.send("**Fetching.** This may take a while.") # trigger "typing" discord message await ctx.trigger_typing() try: response = await get_song(ctx, bird) except GenericError as e: await delete.delete() await ctx.send( f"**An error has occurred while fetching songs.**\n*Please try again.*\n**Reason:** {e}" ) logger.exception(e) if on_error is not None: on_error(ctx) return filename = str(response[0]) extension = str(response[1]) # remove spoilers in tag metadata audioFile = eyed3.load(filename) if audioFile is not None and audioFile.tag is not None: audioFile.tag.remove(filename) statInfo = os.stat(filename) if statInfo.st_size > 4000000: # another filesize check (4mb) await delete.delete() await ctx.send("**Oops! File too large :(**\n*Please try again.*") else: with open(filename, 'rb') as img: if message is not None: await ctx.send(message) # change filename to avoid spoilers await ctx.send(file=discord.File(img, filename="bird." + extension) ) await delete.delete()
async def remove_role(self, ctx, *, args): logger.info("command: remove role") logger.info(f"args: {args}") try: guild_id = int(args.split(' ')[0]) role_id = int(args.split(' ')[1]) guild = self.bot.get_guild(guild_id) role = guild.get_role(role_id) await guild.get_member(ctx.author.id).remove_roles(role) await ctx.send("Ok, done!") except Exception as e: capture_exception(e) logger.exception(e) await ctx.send(f"Error: {e}")
# Here we load our extensions(cogs) that are located in the cogs directory, each cog is a collection of commands core_extensions = [ 'bot.cogs.get_birds', 'bot.cogs.check', 'bot.cogs.skip', 'bot.cogs.hint', 'bot.cogs.score', 'bot.cogs.state', 'bot.cogs.sessions', 'bot.cogs.race', 'bot.cogs.other' ] extra_extensions = ['bot.cogs.covid'] for extension in core_extensions + extra_extensions: try: bot.load_extension(extension) except (discord.errors.ClientException, commands.errors.ExtensionNotFound, commands.errors.ExtensionFailed) as e: if extension in core_extensions: logger.exception(f'Failed to load extension {extension}.', e) capture_exception(e) raise e else: logger.error(f'Failed to load extension {extension}.', e) if sys.platform == 'win32': asyncio.set_event_loop(asyncio.ProactorEventLoop()) ###### # Global Command Checks ###### @bot.check def set_sentry_tag(ctx): """Tags sentry errors with current command."""
async def send_bird(ctx, bird: str, media_type: str, filters: Filter, on_error=None, message=None): """Gets bird media and sends it to the user. `ctx` - Discord context object\n `bird` (str) - bird to send\n `media_type` (str) - type of media (images/songs)\n `filters` (bot.filters Filter)\n `on_error` (function) - async function to run when an error occurs, passes error as argument\n `message` (str) - text message to send before bird\n """ if bird == "": logger.error("error - bird is blank") await ctx.send("**There was an error fetching birds.**") if on_error is not None: await on_error(GenericError("bird is blank", code=100)) else: await ctx.send("*Please try again.*") return # add special condition for screech owls # since screech owl is a genus and SciOly # doesn't specify a species if bird == "Screech Owl": logger.info("choosing specific Screech Owl") bird = random.choice(screech_owls) delete = await ctx.send("**Fetching.** This may take a while.") # trigger "typing" discord message await ctx.trigger_typing() try: filename, extension = await get_media(ctx, bird, media_type, filters) except GenericError as e: await delete.delete() if e.code == 100: await ctx.send( f"**This combination of filters has no valid {media_type} for the current bird.**" ) else: await ctx.send( f"**An error has occurred while fetching {media_type}.**\n**Reason:** {e}" ) logger.exception(e) if on_error is not None: await on_error(e) else: await ctx.send("*Please try again.*") return if os.stat( filename).st_size > MAX_FILESIZE: # another filesize check (4mb) await delete.delete() await ctx.send("**Oops! File too large :(**\n*Please try again.*") return if media_type == "images": if filters.bw: # prevent the black and white conversion from blocking loop = asyncio.get_running_loop() fn = functools.partial(_black_and_white, filename) filename = await loop.run_in_executor(None, fn) elif media_type == "songs": # remove spoilers in tag metadata audioFile = eyed3.load(filename) if audioFile is not None and audioFile.tag is not None: audioFile.tag.remove(filename) # change filename to avoid spoilers file_obj = discord.File(filename, filename=f"bird.{extension}") if message is not None: await ctx.send(message) await ctx.send(file=file_obj) await delete.delete()
and len(os.environ["SCIOLY_ID_BOT_EXTRA_COGS"].strip()) > 0): extra_extensions = os.environ["SCIOLY_ID_BOT_EXTRA_COGS"].strip( ).split(",") else: extra_extensions = [] for extension in core_extensions + extra_extensions: try: bot.load_extension(extension) except ( discord.errors.ClientException, commands.errors.ExtensionNotFound, commands.errors.ExtensionFailed, ) as e: if extension in core_extensions: logger.exception(f"Failed to load extension {extension}.", e) capture_exception(e) raise e logger.error(f"Failed to load extension {extension}.", e) if sys.platform == "win32": asyncio.set_event_loop(asyncio.ProactorEventLoop()) ###### # Global Command Checks ###### @bot.check def log_command_frequency(ctx): """Logs the command used to the database.""" logger.info("global check: logging command frequency")
async def send_bird(ctx, bird: str, on_error=None, message=None, addOn="", bw=False): """Gets a bird picture and sends it to the user. `ctx` - Discord context object\n `bird` (str) - bird picture to send\n `on_error` (function)- function to run when an error occurs\n `message` (str) - text message to send before bird picture\n `addOn` (str) - string to append to search for female/juvenile birds\n `bw` (bool) - whether the image should be black and white (converts with `_black_and_white()`) """ if bird == "": logger.error("error - bird is blank") await ctx.send( "**There was an error fetching birds.**\n*Please try again.*") if on_error is not None: on_error(ctx) return # add special condition for screech owls # since screech owl is a genus and SciOly # doesn't specify a species if bird == "Screech Owl": logger.info("choosing specific Screech Owl") bird = random.choice(screech_owls) delete = await ctx.send("**Fetching.** This may take a while.") # trigger "typing" discord message await ctx.trigger_typing() try: response = await get_image(ctx, bird, addOn) except GenericError as e: await delete.delete() await ctx.send( f"**An error has occurred while fetching images.**\n*Please try again.*\n**Reason:** {e}" ) logger.exception(e) if on_error is not None: on_error(ctx) return filename = str(response[0]) extension = str(response[1]) statInfo = os.stat(filename) if statInfo.st_size > 4000000: # another filesize check (4mb) await delete.delete() await ctx.send("**Oops! File too large :(**\n*Please try again.*") else: if bw: # prevent the black and white conversion from blocking loop = asyncio.get_running_loop() fn = partial(_black_and_white, filename) file_stream = await loop.run_in_executor(None, fn) else: file_stream = filename if message is not None: await ctx.send(message) # change filename to avoid spoilers file_obj = discord.File(file_stream, filename=f"bird.{extension}") await ctx.send(file=file_obj) await delete.delete()