Esempio n. 1
0
def build_id_list(user_id=None,
                  taxon=None,
                  state=None,
                  media="images") -> list:
    """Generates an ID list based on given arguments

    - `user_id`: User ID of custom list
    - `taxon`: taxon string/list
    - `state`: state string/list
    - `media`: images/songs
    """
    logger.info("building id list")
    if isinstance(taxon, str):
        taxon = taxon.split(" ")
    if isinstance(state, str):
        state = state.split(" ")

    state_roles = state if state else []
    if media in ("songs", "song", "s", "a"):
        state_list = "songBirds"
        default = songBirds
    elif media in ("images", "image", "i", "p"):
        state_list = "birdList"
        default = birdList
    else:
        raise GenericError("Invalid media type", code=990)

    custom_list = []
    if (user_id and "CUSTOM" in state_roles
            and database.exists(f"custom.list:{user_id}")
            and not database.exists(f"custom.confirm:{user_id}")):
        custom_list = [
            bird.decode("utf-8")
            for bird in database.smembers(f"custom.list:{user_id}")
        ]

    birds = []
    if taxon:
        birds_in_taxon = set(
            itertools.chain.from_iterable(taxons.get(o, []) for o in taxon))
        if state_roles:
            birds_in_state = set(
                itertools.chain(
                    *(states[state][state_list] for state in state_roles),
                    custom_list))
            birds = list(birds_in_taxon.intersection(birds_in_state))
        else:
            birds = list(birds_in_taxon.intersection(set(default)))
    elif state_roles:
        birds = list(
            set(
                itertools.chain(
                    *(states[state][state_list] for state in state_roles),
                    custom_list)))
    else:
        birds = default
    logger.info(f"number of birds: {len(birds)}")
    return birds
Esempio n. 2
0
    async def goatsucker(self, ctx):
        logger.info("command: goatsucker")

        await channel_setup(ctx)
        await user_setup(ctx)

        answered = int(database.hget(f"channel:{ctx.channel.id}",
                                     "gsAnswered"))
        # check to see if previous bird was answered
        if answered:  # if yes, give a new bird
            if database.exists(f"session.data:{ctx.author.id}"):
                logger.info("session active")
                session_increment(ctx, "total", 1)

            database.hset(f"channel:{ctx.channel.id}", "gsAnswered", "0")
            currentBird = random.choice(goatsuckers)
            database.hset(f"channel:{ctx.channel.id}", "goatsucker",
                          str(currentBird))
            logger.info("currentBird: " + str(currentBird))
            await send_bird(ctx,
                            currentBird,
                            on_error=error_skip_goat,
                            message=GS_MESSAGE)
        else:  # if no, give the same bird
            await send_bird(ctx,
                            database.hget(f"channel:{ctx.channel.id}",
                                          "goatsucker").decode("utf-8"),
                            on_error=error_skip_goat,
                            message=GS_MESSAGE)
Esempio n. 3
0
def incorrect_increment(ctx, bird: str, amount: int):
    """Increments the value of an incorrect bird by `amount`.

    `ctx` - Discord context object or user id\n
    `bird` - bird that was incorrect\n
    `amount` (int) - amount to increment by, usually 1
    """
    if isinstance(ctx, (str, int)):
        user_id = ctx
        guild = None
    else:
        user_id = ctx.author.id
        guild = ctx.guild

    logger.info(f"incrementing incorrect {bird} by {amount}")
    date = str(datetime.datetime.now(datetime.timezone.utc).date())
    database.zincrby("incorrect:global", amount, string.capwords(str(bird)))
    database.zincrby(f"incorrect.user:{user_id}", amount, string.capwords(str(bird)))
    database.zincrby(f"daily.incorrect:{date}", amount, string.capwords(str(bird)))
    if guild is not None:
        logger.info("no dm")
        database.zincrby(
            f"incorrect.server:{ctx.guild.id}", amount, string.capwords(str(bird))
        )
    else:
        logger.info("dm context")
    if database.exists(f"session.data:{user_id}"):
        logger.info("session in session")
        database.zincrby(
            f"session.incorrect:{user_id}", amount, string.capwords(str(bird))
        )
    else:
        logger.info("no session")
Esempio n. 4
0
async def channel_setup(ctx):
    """Sets up a new discord channel.

    `ctx` - Discord context object
    """
    logger.info("checking channel setup")
    if database.exists(f"channel:{ctx.channel.id}"):
        logger.info("channel data ok")
    else:
        database.hset(
            f"channel:{ctx.channel.id}",
            mapping={"bird": "", "answered": 1, "prevB": "", "prevJ": 20},
        )
        # true = 1, false = 0, index 0 is last arg, prevJ is 20 to define as integer
        logger.info("channel data added")
        await ctx.send("Ok, setup! I'm all ready to use!")

    if database.zscore("score:global", str(ctx.channel.id)) is not None:
        logger.info("channel score ok")
    else:
        database.zadd("score:global", {str(ctx.channel.id): 0})
        logger.info("channel score added")

    if ctx.guild is not None:
        if (
            database.zadd("channels:global", {f"{ctx.guild.id}:{ctx.channel.id}": 0})
            != 0
        ):
            logger.info("server lookup ok")
        else:
            logger.info("server lookup added")
Esempio n. 5
0
async def channel_setup(ctx):
    """Sets up a new discord channel.
    
    `ctx` - Discord context object
    """
    logger.info("checking channel setup")
    if database.exists(f"channel:{ctx.channel.id}"):
        logger.info("channel data ok")
    else:
        database.hmset(
            f"channel:{ctx.channel.id}", {
                "bird": "",
                "answered": 1,
                "sBird": "",
                "sAnswered": 1,
                "goatsucker": "",
                "gsAnswered": 1,
                "prevJ": 20,
                "prevB": "",
                "prevS": "",
                "prevK": 20
            })
        # true = 1, false = 0, index 0 is last arg, prevJ is 20 to define as integer
        logger.info("channel data added")
        await ctx.send("Ok, setup! I'm all ready to use!")

    if database.zscore("score:global", str(ctx.channel.id)) is not None:
        logger.info("channel score ok")
    else:
        database.zadd("score:global", {str(ctx.channel.id): 0})
        logger.info("channel score added")
Esempio n. 6
0
def score_increment(ctx, amount: int):
    """Increments the score of a user by `amount`.

    `ctx` - Discord context object\n
    `amount` (int) - amount to increment by, usually 1
    """
    if isinstance(ctx, (str, int)):
        user_id = str(ctx)
        guild = None
        channel_id = ""
    else:
        user_id = str(ctx.author.id)
        guild = ctx.guild
        channel_id = str(ctx.channel.id)

    logger.info(f"incrementing score by {amount}")
    date = str(datetime.datetime.now(datetime.timezone.utc).date())
    database.zincrby("score:global", amount, channel_id)
    database.zincrby("users:global", amount, user_id)
    database.zincrby(f"daily.score:{date}", amount, user_id)
    if guild is not None:
        logger.info("no dm")
        database.zincrby(f"users.server:{ctx.guild.id}", amount, user_id)
        if database.exists(f"race.data:{ctx.channel.id}"):
            logger.info("race in session")
            database.zincrby(f"race.scores:{ctx.channel.id}", amount, user_id)
    else:
        logger.info("dm context")
Esempio n. 7
0
    def __call__(self, ctx: commands.Context):
        if (ctx.command.name in (
                "bird",
                "song",
                "goatsucker",
                "check",
                "skip",
        ) and database.exists("cooldown:global")
                and int(database.get("cooldown:global")) > 1):
            bucket = self.rate_limit_mapping.get_bucket(ctx.message)

        elif not self.disable and ctx.guild is None:
            bucket = self.dm_mapping.get_bucket(ctx.message)

        elif ctx.channel.name.startswith(
                "racing") and ctx.command.name.startswith("check"):
            bucket = self.race_mapping.get_bucket(ctx.message)

        else:
            bucket = self.default_mapping.get_bucket(ctx.message)

        retry_after = bucket.update_rate_limit()
        if retry_after:
            raise commands.CommandOnCooldown(bucket, retry_after)
        return True
Esempio n. 8
0
    async def song(self, ctx, *, args_str: str = ""):
        logger.info("command: song")

        filters, taxon, state = await self.parse(ctx, args_str)
        media = "songs"
        if database.exists(f"race.data:{ctx.channel.id}"):
            media = database.hget(f"race.data:{ctx.channel.id}",
                                  "media").decode("utf-8")
        await self.send_bird_(ctx, media, filters, taxon, state)
Esempio n. 9
0
    async def stop(self, ctx):
        logger.info("command: stop race")

        if database.exists(f"race.data:{ctx.channel.id}"):
            await self.stop_race_(ctx)
        else:
            await ctx.send(
                "**There is no race in session.** *You can start one with `b!race start`*"
            )
Esempio n. 10
0
    async def view(self, ctx):
        logger.info("command: view race")

        if database.exists(f"race.data:{ctx.channel.id}"):
            await self._send_stats(ctx, "**Race In Progress**")
        else:
            await ctx.send(
                "**There is no race in session.** *You can start one with `b!race start`*"
            )
Esempio n. 11
0
def start_session() -> int:
    logger.info("creating session id")
    session_id = 0
    session_id = random.randint(420000000, 420999999)
    while database.exists(f"web.session:{session_id}") and session_id == 0:
        session_id = random.randint(420000000, 420999999)
    logger.info(f"session_id: {session_id}")
    web_session_setup(session_id)
    logger.info(f"created session id: {session_id}")
    return session_id
Esempio n. 12
0
    async def skip(self, ctx):
        logger.info("command: skip")

        await channel_setup(ctx)
        await user_setup(ctx)

        currentBird = str(database.hget(f"channel:{ctx.channel.id}",
                                        "bird"))[2:-1]
        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(currentBird)
            await ctx.send(f"Ok, skipping {currentBird.lower()}")
            await ctx.send(
                url if not database.exists(f"race.data:{ctx.channel.id}") else
                f"<{url}>")  # sends wiki page
            database.zadd("streak:global",
                          {str(ctx.author.id): 0})  # end streak
            if database.exists(
                    f"race.data:{ctx.channel.id}") and database.hget(
                        f"race.data:{ctx.channel.id}",
                        "media").decode("utf-8") == "image":

                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("auto sending next bird image")
                    addon, bw, taxon = database.hmget(
                        f"race.data:{ctx.channel.id}",
                        ["addon", "bw", "taxon"])
                    birds = self.bot.get_cog("Birds")
                    await birds.send_bird_(ctx, addon.decode("utf-8"),
                                           bw.decode("utf-8"),
                                           taxon.decode("utf-8"))
        else:
            await ctx.send("You need to ask for a bird first!")
Esempio n. 13
0
def verify_session(session_id) -> Union[int, bool]:
    session_id = str(session_id)
    logger.info(f"verifying session id: {session_id}")
    if not database.exists(f"web.session:{session_id}"):
        logger.info("doesn't exist")
        return False
    if int(database.hget(f"web.session:{session_id}", "user_id")) == 0:
        logger.info("exists, no user id")
        return True
    logger.info("exists with user id")
    return int(database.hget(f"web.session:{session_id}", "user_id"))
Esempio n. 14
0
    async def start(self, ctx, *, args_str: str = ""):
        logger.info("command: start session")

        await channel_setup(ctx)
        await user_setup(ctx)

        if database.exists(f"session.data:{ctx.author.id}"):
            logger.info("already session")
            await ctx.send("**There is already a session running.** *Change settings/view stats with `b!session edit`*")
            return
        else:
            args = args_str.split(" ")
            logger.info(f"args: {args}")
            if "bw" in args:
                bw = "bw"
            else:
                bw = ""
            states_args = set(states.keys()).intersection({arg.upper() for arg in args})
            if states_args:
                state = " ".join(states_args).strip()
            else:
                state = " ".join(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 = ""
            female = "female" in args or "f" in args
            juvenile = "juvenile" in args or "j" in args
            if female and juvenile:
                await ctx.send("**Juvenile females are not yet supported.**\n*Please try again*")
                return
            elif female:
                addon = "female"
            elif juvenile:
                addon = "juvenile"
            else:
                addon = ""
            logger.info(f"adding bw: {bw}; addon: {addon}; state: {state}")

            database.hmset(
                f"session.data:{ctx.author.id}", {
                    "start": round(time.time()),
                    "stop": 0,
                    "correct": 0,
                    "incorrect": 0,
                    "total": 0,
                    "bw": bw,
                    "state": state,
                    "addon": addon,
                    "taxon": taxon
                }
            )
            await ctx.send(f"**Session started with options:**\n{await self._get_options(ctx)}")
Esempio n. 15
0
    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`*")
Esempio n. 16
0
    async def send_song_(self, ctx):
        songAnswered = int(
            database.hget(f"channel:{ctx.channel.id}", "sAnswered"))
        # check to see if previous bird was answered
        if songAnswered:  # if yes, give a new bird
            roles = check_state_role(ctx)
            if database.exists(f"session.data:{ctx.author.id}"):
                logger.info("session active")
                session_increment(ctx, "total", 1)

                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)
                logger.info(f"roles: {roles}")

            if roles:
                birds = list(
                    itertools.chain.from_iterable(states[state]["songBirds"]
                                                  for state in roles))
            else:
                birds = songBirds
            logger.info(f"number of birds: {len(birds)}")

            currentSongBird = random.choice(birds)
            prevS = database.hget(f"channel:{ctx.channel.id}",
                                  "prevS").decode("utf-8")
            while currentSongBird == prevS and len(birds) > 1:
                currentSongBird = random.choice(birds)
            database.hset(f"channel:{ctx.channel.id}", "prevS",
                          str(currentSongBird))
            database.hset(f"channel:{ctx.channel.id}", "sBird",
                          str(currentSongBird))
            logger.info("currentSongBird: " + str(currentSongBird))
            database.hset(f"channel:{ctx.channel.id}", "sAnswered", "0")
            await send_birdsong(ctx,
                                currentSongBird,
                                on_error=error_skip_song,
                                message=SONG_MESSAGE)
        else:
            await send_birdsong(ctx,
                                database.hget(f"channel:{ctx.channel.id}",
                                              "sBird").decode("utf-8"),
                                on_error=error_skip_song,
                                message=SONG_MESSAGE)
Esempio n. 17
0
async def user_setup(ctx):
    """Sets up a new discord user for score tracking.

    `ctx` - Discord context object or user id
    """
    if isinstance(ctx, (str, int)):
        user_id = str(ctx)
        guild = None
        ctx = None
    else:
        user_id = str(ctx.author.id)
        guild = ctx.guild

    logger.info("checking user data")
    if database.zscore("users:global", user_id) is None:
        database.zadd("users:global", {user_id: 0})
        logger.info("user global added")
        if ctx is not None:
            await ctx.send("Welcome <@" + user_id + ">!")

    date = str(datetime.datetime.now(datetime.timezone.utc).date())
    if database.zscore(f"daily.score:{date}", user_id) is None:
        database.zadd(f"daily.score:{date}", {user_id: 0})
        logger.info("user daily added")

    # Add streak
    if (database.zscore("streak:global", user_id) is None) or (database.zscore(
            "streak.max:global", user_id) is None):
        database.zadd("streak:global", {user_id: 0})
        database.zadd("streak.max:global", {user_id: 0})
        logger.info("added streak")

    if guild is not None:
        global_score = database.zscore("users:global", str(ctx.author.id))
        database.zadd(f"users.server:{ctx.guild.id}",
                      {str(ctx.author.id): global_score})
        logger.info("synced scores")

        if not database.exists(f"custom.list:{ctx.author.id}"):
            role_ids = [role.id for role in ctx.author.roles]
            role_names = [role.name.lower() for role in ctx.author.roles]
            if set(role_names).intersection(set(states["CUSTOM"]["aliases"])):
                index = role_names.index(
                    states["CUSTOM"]["aliases"][0].lower())
                role = ctx.guild.get_role(role_ids[index])
                await ctx.author.remove_roles(
                    role, reason="Remove state role for bird list")
                logger.info("synced roles")
Esempio n. 18
0
    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!")
Esempio n. 19
0
async def bird_setup(ctx, bird: str):
    """Sets up a new bird for incorrect tracking.
    
    `ctx` - Discord context object
    `bird` - bird to setup
    """
    logger.info("checking bird data")
    if database.zscore("incorrect:global", string.capwords(bird)) is not None:
        logger.info("bird global ok")
    else:
        database.zadd("incorrect:global", {string.capwords(bird): 0})
        logger.info("bird global added")

    if database.zscore(f"incorrect.user:{ctx.author.id}",
                       string.capwords(bird)) is not None:
        logger.info("bird user ok")
    else:
        database.zadd(f"incorrect.user:{ctx.author.id}",
                      {string.capwords(bird): 0})
        logger.info("bird user added")

    if ctx.guild is not None:
        logger.info("no dm")
        if database.zscore(f"incorrect.server:{ctx.guild.id}",
                           string.capwords(bird)) is not None:
            logger.info("bird server ok")
        else:
            database.zadd(f"incorrect.server:{ctx.guild.id}",
                          {string.capwords(bird): 0})
            logger.info("bird server added")
    else:
        logger.info("dm context")

    if database.exists(f"session.data:{ctx.author.id}"):
        logger.info("session in session")
        if database.zscore(f"session.incorrect:{ctx.author.id}",
                           string.capwords(bird)) is not None:
            logger.info("bird session ok")
        else:
            database.zadd(f"session.incorrect:{ctx.author.id}",
                          {string.capwords(bird): 0})
            logger.info("bird session added")
    else:
        logger.info("no session")
Esempio n. 20
0
def score_increment(ctx, amount: int):
    """Increments the score of a user by `amount`.

    `ctx` - Discord context object\n
    `amount` (int) - amount to increment by, usually 1
    """
    logger.info(f"incrementing score by {amount}")
    database.zincrby("score:global", amount, str(ctx.channel.id))
    database.zincrby("users:global", amount, str(ctx.author.id))
    if ctx.guild is not None:
        logger.info("no dm")
        database.zincrby(f"users.server:{ctx.guild.id}", amount,
                         str(ctx.author.id))
    else:
        logger.info("dm context")
    if database.exists(f"race.data:{ctx.channel.id}"):
        logger.info("race in session")
        database.zincrby(f"race.scores:{ctx.channel.id}", amount,
                         str(ctx.author.id))
Esempio n. 21
0
def web_session_setup(session_id):
    logger.info("setting up session")
    session_id = str(session_id)
    if database.exists(f"web.session:{session_id}"):
        logger.info("session data ok")
    else:
        database.hset(
            f"web.session:{session_id}",
            mapping={
                "bird": "",
                "answered": 1,  # true = 1, false = 0
                "prevB": "",
                "prevJ": 20,
                "tempScore": 0,  # not used = -1
                "user_id": 0,  # not set = 0
            },
        )
        database.expire(f"web.session:{session_id}", DATABASE_SESSION_EXPIRE)
        logger.info("session set up")
Esempio n. 22
0
def session_increment(ctx, item: str, amount: int):
    """Increments the value of a database hash field by `amount`.

    `ctx` - Discord context object or user id\n
    `item` - hash field to increment (see data.py for details,
    possible values include correct, incorrect, total)\n
    `amount` (int) - amount to increment by, usually 1
    """
    if isinstance(ctx, (str, int)):
        user_id = ctx
    else:
        user_id = ctx.author.id

    if database.exists(f"session.data:{user_id}"):
        logger.info("session active")
        logger.info(f"incrementing {item} by {amount}")
        value = int(database.hget(f"session.data:{user_id}", item))
        value += int(amount)
        database.hset(f"session.data:{user_id}", item, str(value))
    else:
        logger.info("session not active")
Esempio n. 23
0
    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"),
            )
Esempio n. 24
0
def incorrect_increment(ctx, bird: str, amount: int):
    """Increments the value of an incorrect bird by `amount`.

    `ctx` - Discord context object\n
    `bird` - bird that was incorrect\n
    `amount` (int) - amount to increment by, usually 1
    """
    logger.info(f"incrementing incorrect {bird} by {amount}")
    database.zincrby("incorrect:global", amount, string.capwords(str(bird)))
    database.zincrby(f"incorrect.user:{ctx.author.id}", amount,
                     string.capwords(str(bird)))
    if ctx.guild is not None:
        logger.info("no dm")
        database.zincrby(f"incorrect.server:{ctx.guild.id}", amount,
                         string.capwords(str(bird)))
    else:
        logger.info("dm context")
    if database.exists(f"session.data:{ctx.author.id}"):
        logger.info("session in session")
        database.zincrby(f"session.incorrect:{ctx.author.id}", amount,
                         string.capwords(str(bird)))
    else:
        logger.info("no session")
Esempio n. 25
0
    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."
        )
Esempio n. 26
0
    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,
            )
Esempio n. 27
0
    async def check(self, ctx, *, arg):
        logger.info("command: check")

        await channel_setup(ctx)
        await user_setup(ctx)

        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!")
        else:  # if there is a bird, it checks answer
            logger.info("currentBird: " +
                        str(currentBird.lower().replace("-", " ")))
            logger.info("args: " + str(arg.lower().replace("-", " ")))

            await bird_setup(ctx, currentBird)
            sciBird = await get_sciname(currentBird)
            if spellcheck(arg, currentBird) or spellcheck(arg, sciBird):
                logger.info("correct")

                database.hset(f"channel:{ctx.channel.id}", "bird", "")
                database.hset(f"channel:{ctx.channel.id}", "answered", "1")

                if database.exists(f"session.data:{ctx.author.id}"):
                    logger.info("session active")
                    session_increment(ctx, "correct", 1)

                database.zincrby("streak:global", 1, str(ctx.author.id))
                # check if streak is greater than max, if so, increases max
                if database.zscore("streak:global", str(
                        ctx.author.id)) > database.zscore(
                            "streak.max:global", str(ctx.author.id)):
                    database.zadd(
                        "streak.max:global", {
                            str(ctx.author.id):
                            database.zscore("streak:global", str(
                                ctx.author.id))
                        })

                await ctx.send("Correct! Good job!" if not database.
                               exists(f"race.data:{ctx.channel.id}") else
                               f"**{ctx.author.mention}**, you are correct!")
                url = get_wiki_url(currentBird)
                await ctx.send(url if not database.exists(
                    f"race.data:{ctx.channel.id}") else f"<{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 database.exists(f"race.data:{ctx.channel.id}") and str(
                        database.hget(f"race.data:{ctx.channel.id}",
                                      "media"))[2:-1] == "image":

                    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("auto sending next bird image")
                        addon, bw, taxon = database.hmget(
                            f"race.data:{ctx.channel.id}",
                            ["addon", "bw", "taxon"])
                        birds = self.bot.get_cog("Birds")
                        await birds.send_bird_(ctx, addon.decode("utf-8"),
                                               bw.decode("utf-8"),
                                               taxon.decode("utf-8"))

            else:
                logger.info("incorrect")

                database.zadd("streak:global", {str(ctx.author.id): 0})

                if database.exists(f"session.data:{ctx.author.id}"):
                    logger.info("session active")
                    session_increment(ctx, "incorrect", 1)

                incorrect_increment(ctx, str(currentBird), 1)

                if database.exists(f"race.data:{ctx.channel.id}"):
                    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.lower() + ".")
                    url = get_wiki_url(currentBird)
                    await ctx.send(url)
Esempio n. 28
0
    async def checkgoat(self, ctx, *, arg):
        logger.info("command: checkgoat")

        await channel_setup(ctx)
        await user_setup(ctx)

        currentBird = database.hget(f"channel:{ctx.channel.id}",
                                    "goatsucker").decode("utf-8")
        if currentBird == "":  # no bird
            await ctx.send("You must ask for a bird first!")
        else:  # if there is a bird, it checks answer
            await bird_setup(ctx, currentBird)
            index = goatsuckers.index(currentBird)
            sciBird = sciGoat[index]
            database.hset(f"channel:{ctx.channel.id}", "gsAnswered", "1")
            database.hset(f"channel:{ctx.channel.id}", "goatsucker", "")
            if spellcheck(arg, currentBird) or spellcheck(arg, sciBird):
                logger.info("correct")

                if database.exists(f"session.data:{ctx.author.id}"):
                    logger.info("session active")
                    session_increment(ctx, "correct", 1)

                # increment streak and update max
                database.zincrby("streak:global", 1, str(ctx.author.id))
                if database.zscore("streak:global", str(
                        ctx.author.id)) > database.zscore(
                            "streak.max:global", str(ctx.author.id)):
                    database.zadd(
                        "streak.max:global", {
                            str(ctx.author.id):
                            database.zscore("streak:global", str(
                                ctx.author.id))
                        })

                await ctx.send("Correct! Good job!")
                url = get_wiki_url(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"))

            else:
                logger.info("incorrect")

                database.zadd("streak:global", {str(ctx.author.id): 0})

                if database.exists(f"session.data:{ctx.author.id}"):
                    logger.info("session active")
                    session_increment(ctx, "incorrect", 1)

                incorrect_increment(ctx, str(currentBird), 1)
                await ctx.send("Sorry, the bird was actually " +
                               currentBird.lower() + ".")
                url = get_wiki_url(currentBird)
                await ctx.send(url)
            logger.info("currentBird: " +
                        str(currentBird.lower().replace("-", " ")))
            logger.info("args: " + str(arg.lower().replace("-", " ")))
Esempio n. 29
0
    async def on_command_error(ctx, error):
        """Handles errors for all commands without local error handlers."""
        logger.info("Error: " + str(error))

        # don't handle errors with local handlers
        if hasattr(ctx.command, 'on_error'):
            return

        if isinstance(error, commands.CommandOnCooldown):  # send cooldown
            await ctx.send("**Cooldown.** Try again after " +
                           str(round(error.retry_after)) + " s.",
                           delete_after=5.0)

        elif isinstance(error, commands.CommandNotFound):
            capture_exception(error)
            await ctx.send("Sorry, the command was not found.")

        elif isinstance(error, commands.MissingRequiredArgument):
            await ctx.send("This command requires an argument!")

        elif isinstance(error, commands.BadArgument):
            await ctx.send("The argument passed was invalid. Please try again."
                           )

        elif isinstance(error, commands.ArgumentParsingError):
            await ctx.send(
                "An invalid character was detected. Please try again.")

        elif isinstance(error, commands.BotMissingPermissions):
            await ctx.send(
                "**The bot does not have enough permissions to fully function.**\n"
                +
                f"**Permissions Missing:** `{', '.join(map(str, error.missing_perms))}`\n"
                + "*Please try again once the correct permissions are set.*")

        elif isinstance(error, commands.NoPrivateMessage):
            capture_exception(error)
            await ctx.send("**This command is unavaliable in DMs!**")

        elif isinstance(error, GenericError):
            if error.code == 842:
                await ctx.send("**Sorry, you cannot use this command.**")
            elif error.code == 666:
                logger.info("GenericError 666")
            elif error.code == 201:
                logger.info("HTTP Error")
                capture_exception(error)
                await ctx.send(
                    "**An unexpected HTTP Error has occurred.**\n *Please try again.*"
                )
            else:
                logger.info("uncaught generic error")
                capture_exception(error)
                await ctx.send(
                    "**An uncaught generic error has occurred.**\n" +
                    "*Please log this message in #support in the support server below, or try again.*\n"
                    + "**Error:** " + str(error))
                await ctx.send("https://discord.gg/fXxYyDJ")
                raise error

        elif isinstance(error, commands.CommandInvokeError):
            if isinstance(error.original, redis.exceptions.ResponseError):
                capture_exception(error.original)
                if database.exists(f"channel:{ctx.channel.id}"):
                    await ctx.send(
                        "**An unexpected ResponseError has occurred.**\n"
                        "*Please log this message in #support in the support server below, or try again.*\n"
                        "**Error:** " + str(error))
                    await ctx.send("https://discord.gg/fXxYyDJ")
                else:
                    await channel_setup(ctx)
                    await ctx.send("Please run that command again.")

            elif isinstance(error.original,
                            wikipedia.exceptions.DisambiguationError):
                await ctx.send(
                    "Wikipedia page not found. (Disambiguation Error)")

            elif isinstance(error.original, wikipedia.exceptions.PageError):
                await ctx.send("Wikipedia page not found. (Page Error)")

            elif isinstance(error.original,
                            wikipedia.exceptions.WikipediaException):
                capture_exception(error.original)
                await ctx.send("Wikipedia page unavaliable. Try again later.")

            elif isinstance(error.original, discord.Forbidden):
                if error.original.code == 50007:
                    await ctx.send(
                        "I was unable to DM you. Check if I was blocked and try again."
                    )
                else:
                    capture_exception(error)
                    await ctx.send(
                        "**An unexpected Forbidden error has occurred.**\n"
                        "*Please log this message in #support in the support server below, or try again.*\n"
                        "**Error:** " + str(error))

            elif isinstance(error.original, discord.HTTPException):
                if error.original.status == 502:
                    await ctx.send(
                        "**An error has occured with discord. :(**\n*Please try again.*"
                    )
                else:
                    capture_exception(error.original)
                    await ctx.send(
                        "**An unexpected HTTPException has occurred.**\n" +
                        "*Please log this message in #support in the support server below, or try again*\n"
                        + "**Error:** " + str(error.original))
                    await ctx.send("https://discord.gg/fXxYyDJ")

            elif isinstance(error.original, aiohttp.ClientOSError):
                if error.original.errno == errno.ECONNRESET:
                    await ctx.send(
                        "**An error has occured with discord. :(**\n*Please try again.*"
                    )
                else:
                    capture_exception(error.original)
                    await ctx.send(
                        "**An unexpected ClientOSError has occurred.**\n" +
                        "*Please log this message in #support in the support server below, or try again.*\n"
                        + "**Error:** " + str(error.original))
                    await ctx.send("https://discord.gg/fXxYyDJ")

            else:
                logger.info("uncaught command error")
                capture_exception(error.original)
                await ctx.send(
                    "**An uncaught command error has occurred.**\n" +
                    "*Please log this message in #support in the support server below, or try again.*\n"
                    + "**Error:**  " + str(error))
                await ctx.send("https://discord.gg/fXxYyDJ")
                raise error

        else:
            logger.info("uncaught non-command")
            capture_exception(error)
            await ctx.send(
                "**An uncaught non-command error has occurred.**\n" +
                "*Please log this message in #support in the support server below, or try again.*\n"
                + "**Error:** " + str(error))
            await ctx.send("https://discord.gg/fXxYyDJ")
            raise error
Esempio n. 30
0
    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)