async def skip(self, ctx): logger.info("command: skip") currentBird = database.hget(f"channel:{ctx.channel.id}", "bird").decode("utf-8") database.hset(f"channel:{ctx.channel.id}", "bird", "") database.hset(f"channel:{ctx.channel.id}", "answered", "1") if currentBird != "": # check if there is bird url = get_wiki_url(ctx, currentBird) await ctx.send(f"Ok, skipping {currentBird.lower()}") await ctx.send(url) # sends wiki page streak_increment(ctx, None) # reset streak if database.exists(f"race.data:{ctx.channel.id}"): if Filter.from_int( int( database.hget(f"race.data:{ctx.channel.id}", "filter"))).vc: await voice_functions.stop(ctx, silent=True) media = database.hget(f"race.data:{ctx.channel.id}", "media").decode("utf-8") limit = int( database.hget(f"race.data:{ctx.channel.id}", "limit")) first = database.zrevrange(f"race.scores:{ctx.channel.id}", 0, 0, True)[0] if int(first[1]) >= limit: logger.info("race ending") race = self.bot.get_cog("Race") await race.stop_race_(ctx) else: logger.info(f"auto sending next bird {media}") filter_int, taxon, state = database.hmget( f"race.data:{ctx.channel.id}", ["filter", "taxon", "state"]) birds = self.bot.get_cog("Birds") await birds.send_bird_( ctx, media, Filter.from_int(int(filter_int)), taxon.decode("utf-8"), state.decode("utf-8"), ) else: await ctx.send("You need to ask for a bird first!")
async def _send_next_race_media(self, ctx): if database.exists(f"race.data:{ctx.channel.id}"): if Filter.from_int( int(database.hget(f"race.data:{ctx.channel.id}", "filter"))).vc: await voice_functions.stop(ctx, silent=True) media = database.hget(f"race.data:{ctx.channel.id}", "media").decode("utf-8") logger.info(f"auto sending next bird {media}") filter_int, taxon, state = database.hmget( f"race.data:{ctx.channel.id}", ["filter", "taxon", "state"]) await self.send_bird_( ctx, media, Filter.from_int(int(filter_int)), taxon.decode("utf-8"), state.decode("utf-8"), )
async def _get_options(self, ctx): filter_int, state, taxon, wiki, strict = database.hmget( f"session.data:{ctx.author.id}", ["filter", "state", "taxon", "wiki", "strict"], ) filters = Filter.from_int(int(filter_int)) options = textwrap.dedent(f"""\ **Active Filters:** `{'`, `'.join(filters.display())}` **State bird list:** {state.decode('utf-8') if state else 'None'} **Bird taxon:** {taxon.decode('utf-8') if taxon else 'None'} **Wiki Embeds**: {wiki==b'wiki'} **Strict Spelling**: {strict==b'strict'} """) return options
async def _get_options(self, ctx): filter_int, state, media, limit, taxon, strict, alpha = database.hmget( f"race.data:{ctx.channel.id}", ["filter", "state", "media", "limit", "taxon", "strict", "alpha"], ) filters = Filter.from_int(int(filter_int)) options = ( f"**Active Filters:** `{'`, `'.join(filters.display())}`\n" + f"**Special bird list:** {state.decode('utf-8') if state else 'None'}\n" + f"**Taxons:** {taxon.decode('utf-8') if taxon else 'None'}\n" + f"**Media Type:** {media.decode('utf-8')}\n" + f"**Amount to Win:** {limit.decode('utf-8')}\n" + f"**Strict Spelling:** {strict == b'strict'}\n" + f"**Alpha Codes:** {'Enabled' if alpha == b'alpha' else 'Disabled'}" ) return options
async def stop_race_(self, ctx): if Filter.from_int( int(database.hget(f"race.data:{ctx.channel.id}", "filter"))).vc: await voice_functions.disconnect(ctx, silent=True) database.delete(f"voice.server:{ctx.guild.id}") first = database.zrevrange(f"race.scores:{ctx.channel.id}", 0, 0, True)[0] if ctx.guild is not None: user = await fetch_get_user(int(first[0]), ctx=ctx, member=True) else: user = None if user is None: user = await fetch_get_user(int(first[0]), ctx=ctx, member=False) if user is None: user_info = "Deleted" else: user_info = f"{esc(user.name)}#{user.discriminator}" else: user_info = f"{esc(user.name)}#{user.discriminator} ({user.mention})" await ctx.send( f"**Congratulations, {user_info}!**\n" + f"You have won the race by correctly identifying `{int(first[1])}` birds. " + "*Way to go!*") database.hset(f"race.data:{ctx.channel.id}", "stop", round(time.time())) await self._send_stats(ctx, "**Race stopped.**") database.delete(f"race.data:{ctx.channel.id}") database.delete(f"race.scores:{ctx.channel.id}") logger.info("race end: skipping last bird") database.hset(f"channel:{ctx.channel.id}", "bird", "") database.hset(f"channel:{ctx.channel.id}", "answered", "1")
async def check(self, ctx, *, arg): logger.info("command: check") currentBird = database.hget(f"channel:{ctx.channel.id}", "bird").decode("utf-8") if currentBird == "": # no bird await ctx.send("You must ask for a bird first!") return # if there is a bird, it checks answer sciBird = (await get_sciname(currentBird)).lower().replace("-", " ") arg = arg.lower().replace("-", " ") currentBird = currentBird.lower().replace("-", " ") alpha_code = alpha_codes.get(string.capwords(currentBird)) logger.info("currentBird: " + currentBird) logger.info("arg: " + arg) bird_setup(ctx, currentBird) race_in_session = bool(database.exists(f"race.data:{ctx.channel.id}")) if race_in_session: logger.info("race in session") if database.hget(f"race.data:{ctx.channel.id}", "strict"): logger.info("strict spelling") correct = arg in (currentBird, sciBird) else: logger.info("spelling leniency") correct = spellcheck(arg, currentBird) or spellcheck( arg, sciBird) if not correct and database.hget(f"race.data:{ctx.channel.id}", "alpha"): logger.info("checking alpha codes") correct = arg.upper() == alpha_code else: logger.info("no race") if database.hget(f"session.data:{ctx.author.id}", "strict"): logger.info("strict spelling") correct = arg in (currentBird, sciBird) else: logger.info("spelling leniency") correct = (spellcheck(arg, currentBird) or spellcheck(arg, sciBird) or arg.upper() == alpha_code) if correct: logger.info("correct") database.hset(f"channel:{ctx.channel.id}", "bird", "") database.hset(f"channel:{ctx.channel.id}", "answered", "1") session_increment(ctx, "correct", 1) streak_increment(ctx, 1) database.zincrby(f"correct.user:{ctx.author.id}", 1, string.capwords(str(currentBird))) if (race_in_session and Filter.from_int( int(database.hget(f"race.data:{ctx.channel.id}", "filter"))).vc): await voice_functions.stop(ctx, silent=True) await ctx.send( f"Correct! Good job! The bird was **{currentBird}**." if not race_in_session else f"**{ctx.author.mention}**, you are correct! The bird was **{currentBird}**." ) url = get_wiki_url(ctx, currentBird) await ctx.send(url) score_increment(ctx, 1) if int(database.zscore("users:global", str(ctx.author.id))) in achievement: number = str( int(database.zscore("users:global", str(ctx.author.id)))) await ctx.send( f"Wow! You have answered {number} birds correctly!") filename = f"bot/media/achievements/{number}.PNG" with open(filename, "rb") as img: await ctx.send(file=discord.File(img, filename="award.png") ) if race_in_session: media = database.hget(f"race.data:{ctx.channel.id}", "media").decode("utf-8") limit = int( database.hget(f"race.data:{ctx.channel.id}", "limit")) first = database.zrevrange(f"race.scores:{ctx.channel.id}", 0, 0, True)[0] if int(first[1]) >= limit: logger.info("race ending") race = self.bot.get_cog("Race") await race.stop_race_(ctx) else: logger.info(f"auto sending next bird {media}") filter_int, taxon, state = database.hmget( f"race.data:{ctx.channel.id}", ["filter", "taxon", "state"]) birds = self.bot.get_cog("Birds") await birds.send_bird_( ctx, media, Filter.from_int(int(filter_int)), taxon.decode("utf-8"), state.decode("utf-8"), ) else: logger.info("incorrect") streak_increment(ctx, None) # reset streak session_increment(ctx, "incorrect", 1) incorrect_increment(ctx, str(currentBird), 1) if race_in_session: await ctx.send("Sorry, that wasn't the right answer.") else: database.hset(f"channel:{ctx.channel.id}", "bird", "") database.hset(f"channel:{ctx.channel.id}", "answered", "1") await ctx.send("Sorry, the bird was actually **" + currentBird + "**.") url = get_wiki_url(ctx, currentBird) await ctx.send(url)
async def start(self, ctx, *, args_str: str = ""): logger.info("command: start race") if not str(ctx.channel.name).startswith("racing"): logger.info("not race channel") await ctx.send( "**Sorry, racing is not available in this channel.**\n" + "*Set the channel name to start with `racing` to enable it.*" ) return if database.exists(f"race.data:{ctx.channel.id}"): logger.info("already race") await ctx.send( "**There is already a race in session.** *Change settings/view stats with `b!race view`*" ) return filters = Filter.parse(args_str, use_numbers=False) if filters.vc: if database.get(f"voice.server:{ctx.guild.id}") is not None: logger.info("already vc race") await ctx.send( "**There is already a VC race in session in this server!**" ) return client = await voice_functions.get_voice_client(ctx, connect=True) if client is None: return database.set(f"voice.server:{ctx.guild.id}", str(ctx.channel.id)) args = args_str.split(" ") logger.info(f"args: {args}") taxon_args = set(taxons.keys()).intersection({arg.lower() for arg in args}) if taxon_args: taxon = " ".join(taxon_args).strip() else: taxon = "" if "strict" in args: strict = "strict" else: strict = "" if "alpha" in args: alpha = "alpha" else: alpha = "" states_args = set(states.keys()).intersection({arg.upper() for arg in args}) if states_args: if {"CUSTOM"}.issubset(states_args): if database.exists( f"custom.list:{ctx.author.id}" ) and not database.exists(f"custom.confirm:{ctx.author.id}"): states_args.discard("CUSTOM") states_args.add(f"CUSTOM:{ctx.author.id}") else: states_args.discard("CUSTOM") await ctx.send( "**You don't have a custom list set.**\n*Ignoring the argument.*" ) state = " ".join(states_args).strip() else: state = "" song = "song" in args or "songs" in args or "s" in args or filters.vc image = ( "image" in args or "images" in args or "i" in args or "picture" in args or "pictures" in args or "p" in args ) if song and image: await ctx.send( "**Songs and images are not yet supported.**\n*Please try again*" ) return if song: media = "song" elif image: media = "image" else: media = "image" ints = [] for n in args: try: ints.append(int(n)) except ValueError: continue if ints: limit = int(ints[0]) else: limit = 10 if limit > 1000000: await ctx.send("**Sorry, the maximum amount to win is 1 million.**") limit = 1000000 logger.info( f"adding filters: {filters}; state: {state}; media: {media}; limit: {limit}" ) database.hset( f"race.data:{ctx.channel.id}", mapping={ "start": round(time.time()), "stop": 0, "limit": limit, "filter": str(filters.to_int()), "state": state, "media": media, "taxon": taxon, "strict": strict, "alpha": alpha, }, ) database.zadd(f"race.scores:{ctx.channel.id}", {str(ctx.author.id): 0}) await ctx.send( f"**Race started with options:**\n{await self._get_options(ctx)}" ) media = database.hget(f"race.data:{ctx.channel.id}", "media").decode("utf-8") logger.info("clearing previous bird") database.hset(f"channel:{ctx.channel.id}", "bird", "") database.hset(f"channel:{ctx.channel.id}", "answered", "1") logger.info(f"auto sending next bird {media}") filter_int, taxon, state = database.hmget( f"race.data:{ctx.channel.id}", ["filter", "taxon", "state"] ) birds = self.bot.get_cog("Birds") await birds.send_bird_( ctx, media, Filter.from_int(int(filter_int)), # type: ignore taxon.decode("utf-8"), # type: ignore state.decode("utf-8"), # type: ignore )
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)
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)