async def stop_race_(self, ctx): first = database.zrevrange(f"race.scores:{ctx.channel.id}", 0, 0, True)[0] if ctx.guild is not None: user = ctx.guild.get_member(int(first[0])) else: user = None if user is None: user = self.bot.get_user(int(first[0])) if user is None: user = "******" else: user = f"{user.name}#{user.discriminator}" else: user = f"{user.name}#{user.discriminator} ({user.mention})" await ctx.send( f"**Congratulations, {user}!**\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}")
def test_suite_cleanup_thing(self): yield database.delete(f"channel:{self.ctx.channel.id}") database.zrem("score:global", str(self.ctx.channel.id)) database.zrem("users:global", str(self.ctx.author.id)) database.zrem("streak:global", str(self.ctx.author.id)) database.zrem("streak.max:global", str(self.ctx.author.id)) database.delete(f"incorrect.user:{self.ctx.author.id}") database.delete(f"correct.user:{self.ctx.author.id}") if self.ctx.guild is not None: database.delete(f"users.server:{self.ctx.guild.id}") database.delete(f"incorrect.server:{self.ctx.guild.id}")
async def stop(self, ctx): logger.info("command: stop session") await channel_setup(ctx) await user_setup(ctx) if database.exists(f"session.data:{ctx.author.id}"): database.hset(f"session.data:{ctx.author.id}", "stop", round(time.time())) await self._send_stats(ctx, "**Session stopped.**\n") database.delete(f"session.data:{ctx.author.id}") database.delete(f"session.incorrect:{ctx.author.id}") else: await ctx.send("**There is no session running.** *You can start one with `b!session start`*")
def setup(self, guild=False): self.bot = mock.Bot() self.cog = get_birds.Birds(self.bot) self.ctx = mock.Context() if guild: self.ctx.set_guild() database.delete(f"channel:{self.ctx.channel.id}") database.zrem("score:global", str(self.ctx.channel.id)) database.zrem("users:global", str(self.ctx.author.id)) database.zrem("streak:global", str(self.ctx.author.id)) database.zrem("streak.max:global", str(self.ctx.author.id)) if self.ctx.guild is not None: database.delete(f"users.server:{self.ctx.guild.id}")
def setup(self, guild=False): # pylint: disable=attribute-defined-outside-init self.bot = mock.Bot() self.cog = score.Score(self.bot) self.ctx = mock.Context(self.bot) if guild: self.ctx.set_guild() database.delete(f"channel:{self.ctx.channel.id}") database.zrem("score:global", str(self.ctx.channel.id)) database.zrem("users:global", str(self.ctx.author.id)) database.zrem("streak:global", str(self.ctx.author.id)) database.zrem("streak.max:global", str(self.ctx.author.id)) if self.ctx.guild is not None: database.delete(f"users.server:{self.ctx.guild.id}") asyncio.run(channel_setup(self.ctx)) asyncio.run(user_setup(self.ctx))
async def leave(self, ctx, confirm: typing.Optional[bool] = False): logger.info("command: leave") if database.exists(f"leave:{ctx.guild.id}"): logger.info("confirming") if confirm: logger.info(f"confirmed. Leaving {ctx.guild}") database.delete(f"leave:{ctx.guild.id}") await ctx.send("**Ok, bye!**") await ctx.guild.leave() return logger.info("confirm failed. leave canceled") database.delete(f"leave:{ctx.guild.id}") await ctx.send("**Leave canceled.**") return logger.info("not confirmed") database.set(f"leave:{ctx.guild.id}", 0, ex=60) await ctx.send( "**Are you sure you want to remove me from the guild?**\n" + "Use `b!leave yes` to confirm, `b!leave no` to cancel. " + "You have 60 seconds to confirm before it will automatically cancel." )
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 custom(self, ctx, *, args=""): logger.info("command: custom list set") args = args.lower().strip().split(" ") logger.info(f"parsed args: {args}") if ("replace" not in args and ctx.message.attachments and database.exists(f"custom.list:{ctx.author.id}")): await ctx.send( "Woah there. You already have a custom list. " + "To view its contents, use `b!custom view`. " + "If you want to replace your list, upload the file with `b!custom replace`." ) return if "delete" in args and database.exists( f"custom.list:{ctx.author.id}"): if (database.exists(f"custom.confirm:{ctx.author.id}") and database.get(f"custom.confirm:{ctx.author.id}").decode( "utf-8") == "delete"): database.delete(f"custom.list:{ctx.author.id}", f"custom.confirm:{ctx.author.id}") await ctx.send("Ok, your list was deleted.") return database.set(f"custom.confirm:{ctx.author.id}", "delete", ex=86400) await ctx.send( "Are you sure you want to permanently delete your list? " + "Use `b!custom delete` again within 24 hours to clear your custom list." ) return if ("confirm" in args and database.exists(f"custom.confirm:{ctx.author.id}") and database.get(f"custom.confirm:{ctx.author.id}").decode("utf-8") == "confirm"): # list was validated by server and user, making permanent logger.info("user confirmed") database.persist(f"custom.list:{ctx.author.id}") database.delete(f"custom.confirm:{ctx.author.id}") database.set(f"custom.cooldown:{ctx.author.id}", 0, ex=86400) await ctx.send( "Ok, your custom bird list is now available. Use `b!custom view` " + "to view your list. You can change your list again in 24 hours." ) return if ("validate" in args and database.exists(f"custom.confirm:{ctx.author.id}") and database.get(f"custom.confirm:{ctx.author.id}").decode("utf-8") == "valid"): # list was validated, now for user confirm logger.info("valid list, user needs to confirm") database.expire(f"custom.list:{ctx.author.id}", 86400) database.set(f"custom.confirm:{ctx.author.id}", "confirm", ex=86400) birdlist = "\n".join( bird.decode("utf-8") for bird in database.smembers(f"custom.list:{ctx.author.id}")) await ctx.send( f"**Please confirm the following list.** ({int(database.scard(f'custom.list:{ctx.author.id}'))} items)" ) await self.broken_send(ctx, birdlist, between="```\n") await ctx.send( "Once you have looked over the list and are sure you want to add it, " + "please use `b!custom confirm` to have this list added as a custom list. " + "You have another 24 hours to confirm. " + "To start over, upload a new list with the message `b!custom replace`." ) return if "view" in args: if not database.exists(f"custom.list:{ctx.author.id}"): await ctx.send( "You don't have a custom list. To add a custom list, " + "upload a txt file with a bird's name on each line to this DM " + "and put `b!custom` in the **Add a Comment** section.") return birdlist = "\n".join( bird.decode("utf-8") for bird in database.smembers(f"custom.list:{ctx.author.id}")) birdlist = f"{birdlist}" await ctx.send( f"**Your Custom Bird List** ({int(database.scard(f'custom.list:{ctx.author.id}'))} items)" ) await self.broken_send(ctx, birdlist, between="```\n") return if not database.exists( f"custom.list:{ctx.author.id}") or "replace" in args: # user inputted bird list, now validating start = time.perf_counter() if database.exists(f"custom.cooldown:{ctx.author.id}"): await ctx.send( "Sorry, you'll have to wait 24 hours between changing lists." ) return logger.info("reading received bird list") if not ctx.message.attachments: logger.info("no file detected") await ctx.send( "Sorry, no file was detected. Upload your txt file and put `b!custom` in the **Add a Comment** section." ) return decoded = await auto_decode(await ctx.message.attachments[0].read()) if not decoded: logger.info("invalid character encoding") await ctx.send( "Sorry, something went wrong. Are you sure this is a text file?" ) return parsed_birdlist = set( map(lambda x: x.strip(), decoded.strip().split("\n"))) parsed_birdlist.discard("") parsed_birdlist = list(parsed_birdlist) if len(parsed_birdlist) > 200: logger.info("parsed birdlist too long") await ctx.send( "Sorry, we're not supporting custom lists larger than 200 birds. Make sure there are no empty lines." ) return logger.info("checking for invalid characters") char = re.compile(r"[^A-Za-z '\-\xC0-\xD6\xD8-\xF6\xF8-\xFF]") for item in parsed_birdlist: if len(item) > 1000: logger.info("item too long") await ctx.send( f"Line starting with `{item[:100]}` exceeds 1000 characters." ) return search = char.search(item) if search: logger.info("invalid character detected") await ctx.send( f"An invalid character `{search.group()}` was detected. Only letters, spaces, hyphens, and apostrophes are allowed." ) await ctx.send( f"Error on line starting with `{item[:100]}`, position {search.span()[0]}" ) return database.delete(f"custom.list:{ctx.author.id}", f"custom.confirm:{ctx.author.id}") await self.validate(ctx, parsed_birdlist) elapsed = time.perf_counter() - start await ctx.send( f"**Finished validation in {round(elapsed//60)} minutes {round(elapsed%60, 4)} seconds.** {ctx.author.mention}" ) logger.info( f"Finished validation in {round(elapsed//60)} minutes {round(elapsed%60, 4)} seconds." ) return if database.exists(f"custom.confirm:{ctx.author.id}"): next_step = database.get(f"custom.confirm:{ctx.author.id}").decode( "utf-8") if next_step == "valid": await ctx.send( "You need to validate your list. Use `b!custom validate` to do so. " + "You can also delete or replace your list with `b!custom [delete|replace]`" ) return if next_step == "confirm": await ctx.send( "You need to confirm your list. Use `b!custom confirm` to do so. " + "You can also delete or replace your list with `b!custom [delete|replace]`" ) return if next_step == "delete": await ctx.send( "You're in the process of deleting your list. Use `b!custom delete` to do so. " + "You can also replace your list with `b!custom replace`") return capture_message( f"custom.confirm database invalid with {next_step}") await ctx.send( "Whoops, something went wrong. Please report this incident " + "in the support server below.\nhttps://discord.gg/2HbshwGjnm") return await ctx.send( "Use `b!custom view` to view your bird list or `b!custom replace` to replace your bird list." )