async def is_holiday(ctx): """Sends a picture of a turkey on Thanksgiving. Can be extended to other holidays as well. """ logger.info("global check: checking holiday") now = datetime.now(tz=timezone(-timedelta(hours=4))).date() us = holidays.US() if now in us: if us.get(now) == "Thanksgiving": await send_bird( ctx, "Wild Turkey", "images", Filter(), message= "**It's Thanksgiving!**\nGo celebrate with your family.", ) raise GenericError(code=666) if us.get(now) == "Independence Day": await send_bird( ctx, "Bald Eagle", "images", Filter(), message= "**It's Independence Day!**\nEnjoy this birb responsibly.", ) raise GenericError(code=666) elif now == date(now.year, 4, 1): return await drone_attack(ctx) return True
async def valid_bird(bird: str, session=None) -> Tuple[str, bool, str, str]: """Checks if a bird is valid. This checks first if Macaulay has a valid taxon code for the bird, then if the bird is already in one of our lists. If not, then it checks if Macaulay has valid images for the bird. Returns a tuple: `(input bird, valid bool, reason, detected name (may be empty string))`. """ bird_ = string.capwords(bird.strip().replace("-", " ")) logger.info(f"checking if {bird} is valid") async with contextlib.AsyncExitStack() as stack: if session is None: session = await stack.enter_async_context(aiohttp.ClientSession()) try: name = (await get_taxon(bird_, session))[1] except GenericError as e: if e.code in (111, 201): return (bird, False, "No taxon code found", "") raise e if bird_ not in birdListMaster: try: urls = await _get_urls(session, bird_, "p", Filter()) except GenericError as e: if e.code in (100, 201): return (bird, False, "One or less images found", name) raise e if len(urls) < 2: return (bird, False, "One or less images found", name) return (bird, True, "All checks passed", name)
async def goatsucker(self, ctx): logger.info("command: goatsucker") if database.exists(f"race.data:{ctx.channel.id}"): await ctx.send("This command is disabled during races.") return answered = int(database.hget(f"channel:{ctx.channel.id}", "answered")) # check to see if previous bird was answered if answered: # if yes, give a new bird session_increment(ctx, "total", 1) database.hset(f"channel:{ctx.channel.id}", "answered", "0") currentBird = random.choice(goatsuckers) self.increment_bird_frequency(ctx, currentBird) database.hset(f"channel:{ctx.channel.id}", "bird", str(currentBird)) logger.info("currentBird: " + str(currentBird)) await send_bird( ctx, currentBird, "images", Filter(), on_error=self.error_skip(ctx), message=GS_MESSAGE, ) else: # if no, give the same bird await send_bird( ctx, database.hget(f"channel:{ctx.channel.id}", "bird").decode("utf-8"), "images", Filter(), on_error=self.error_skip(ctx), message=GS_MESSAGE, )
async def valid_bird(bird: str, session=None) -> Tuple[str, bool, str, str]: """Checks if a bird is valid. This checks first if Macaulay has a valid taxon code for the bird, then if Macaulay has valid media for the bird based on the requested media type. Media can be `p` for pictures, `a` for audio, or `v` for video. Returns a tuple: `(input bird, valid bool, reason, detected name (may be empty string))`. """ bird = string.capwords(bird) logger.info(f"checking if {bird} is valid") async with contextlib.AsyncExitStack() as stack: if session is None: session = await stack.enter_async_context(aiohttp.ClientSession()) try: name = (await get_taxon(bird, session))[1] except GenericError as e: if e.code in (111, 201): return (bird, False, "No taxon code found", "") raise e urls = await _get_urls(session, bird, "p", Filter()) if len(urls) < 2: return (bird, False, "One or less images found", name) return (bird, True, "All checks passed", name)
async def parse(ctx, args_str: str): """Parse arguments for options.""" args = args_str.split(" ") logger.info(f"args: {args}") if not database.exists(f"race.data:{ctx.channel.id}"): roles = check_state_role(ctx) taxon_args = set(taxons.keys()).intersection( {arg.lower() for arg in args}) if taxon_args: taxon = " ".join(taxon_args).strip() else: taxon = "" state_args = set(states.keys()).intersection( {arg.upper() for arg in args}) if state_args: state = " ".join(state_args).strip() else: state = "" if database.exists(f"session.data:{ctx.author.id}"): logger.info("session parameters") if taxon_args: current_taxons = set( database.hget(f"session.data:{ctx.author.id}", "taxon").decode("utf-8").split(" ")) logger.info(f"toggle taxons: {taxon_args}") logger.info(f"current taxons: {current_taxons}") taxon_args.symmetric_difference_update(current_taxons) taxon_args.discard("") logger.info(f"new taxons: {taxon_args}") taxon = " ".join(taxon_args).strip() else: taxon = database.hget(f"session.data:{ctx.author.id}", "taxon").decode("utf-8") roles = (database.hget(f"session.data:{ctx.author.id}", "state").decode("utf-8").split(" ")) if roles[0] == "": roles = [] if not roles: logger.info("no session lists") roles = check_state_role(ctx) session_filter = int( database.hget(f"session.data:{ctx.author.id}", "filter")) filters = Filter.parse(args_str, defaults=False) if filters.vc: filters.vc = False await ctx.send("**The VC filter is not allowed inline!**") default_quality = Filter().quality if (Filter.from_int(session_filter).quality == default_quality and filters.quality and filters.quality != default_quality): filters ^= Filter() # clear defaults filters ^= session_filter else: filters = Filter.parse(args_str) if filters.vc: filters.vc = False await ctx.send("**The VC filter is not allowed inline!**") if state_args: logger.info(f"toggle states: {state_args}") logger.info(f"current states: {roles}") state_args.symmetric_difference_update(set(roles)) state_args.discard("") logger.info(f"new states: {state_args}") state = " ".join(state_args).strip() else: state = " ".join(roles).strip() if "CUSTOM" in state.upper().split(" "): if not database.exists(f"custom.list:{ctx.author.id}"): await ctx.send("**You don't have a custom list set!**") state_list = state.split(" ") state_list.remove("CUSTOM") state = " ".join(state_list) elif database.exists(f"custom.confirm:{ctx.author.id}"): await ctx.send( "**Please verify or confirm your custom list before using!**" ) state_list = state.split(" ") state_list.remove("CUSTOM") state = " ".join(state_list) else: logger.info("race parameters") race_filter = int( database.hget(f"race.data:{ctx.channel.id}", "filter")) filters = Filter.parse(args_str, defaults=False) if filters.vc: filters.vc = False await ctx.send("**The VC filter is not allowed inline!**") default_quality = Filter().quality if (Filter.from_int(race_filter).quality == default_quality and filters.quality and filters.quality != default_quality): filters ^= Filter() # clear defaults filters ^= race_filter taxon = database.hget(f"race.data:{ctx.channel.id}", "taxon").decode("utf-8") state = database.hget(f"race.data:{ctx.channel.id}", "state").decode("utf-8") logger.info( f"args: filters: {filters}; taxon: {taxon}; state: {state}") return (filters, taxon, state)
async def bird_song(request: Request, bird: str): info = await send_bird(request, bird, "songs", Filter()) return send_file(info[0], media_type=info[2])
def parse(ctx, args_str: str): """Parse arguments for options.""" args = args_str.split(" ") logger.info(f"args: {args}") if not database.exists(f"race.data:{ctx.channel.id}"): roles = check_state_role(ctx) taxon_args = set(taxons.keys()).intersection( {arg.lower() for arg in args}) if taxon_args: taxon = " ".join(taxon_args).strip() else: taxon = "" state_args = set(states.keys()).intersection( {arg.upper() for arg in args}) if state_args: state = " ".join(state_args).strip() else: state = "" if database.exists(f"session.data:{ctx.author.id}"): logger.info("session parameters") if taxon_args: current_taxons = set( database.hget(f"session.data:{ctx.author.id}", "taxon").decode("utf-8").split(" ")) logger.info(f"toggle taxons: {taxon_args}") logger.info(f"current taxons: {current_taxons}") taxon_args.symmetric_difference_update(current_taxons) logger.info(f"new taxons: {taxon_args}") taxon = " ".join(taxon_args).strip() else: taxon = database.hget(f"session.data:{ctx.author.id}", "taxon").decode("utf-8") roles = (database.hget(f"session.data:{ctx.author.id}", "state").decode("utf-8").split(" ")) if roles[0] == "": roles = [] if not roles: logger.info("no session lists") roles = check_state_role(ctx) session_filter = int( database.hget(f"session.data:{ctx.author.id}", "filter")) filters = Filter.parse(args_str, defaults=False) default_quality = Filter().quality if (Filter.from_int(session_filter).quality == default_quality and filters.quality and filters.quality != default_quality): filters ^= Filter() # clear defaults filters ^= session_filter else: filters = Filter.parse(args_str) if state_args: logger.info(f"toggle states: {state_args}") logger.info(f"current states: {roles}") state_args.symmetric_difference_update(set(roles)) logger.info(f"new states: {state_args}") state = " ".join(state_args).strip() else: state = " ".join(roles).strip() else: logger.info("race parameters") race_filter = int( database.hget(f"race.data:{ctx.channel.id}", "filter")) filters = Filter.parse(args_str, defaults=False) default_quality = Filter().quality if (Filter.from_int(race_filter).quality == default_quality and filters.quality and filters.quality != default_quality): filters ^= Filter() # clear defaults filters ^= race_filter taxon = database.hget(f"race.data:{ctx.channel.id}", "taxon").decode("utf-8") state = database.hget(f"race.data:{ctx.channel.id}", "state").decode("utf-8") logger.info( f"args: filters: {filters}; taxon: {taxon}; state: {state}") return (filters, taxon, state)
def bird_song(bird): path = asyncio.run(get_media(bird, "songs", Filter())) return flask.send_file(f"../{path[0]}")