Beispiel #1
0
    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
Beispiel #2
0
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)
Beispiel #3
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!")
Beispiel #4
0
async def get_bird(
    request: Request,
    media: str = "images",
    addon: str = "",
    bw: int = 0,
):
    logger.info("endpoint: get bird")
    session_id = get_session_id(request)
    media_type = media

    filters = Filter.parse(addon)
    if bool(bw):
        filters.bw = True
    logger.info(f"args: media: {media_type}; filters: {filters};")

    logger.info(
        "bird: " +
        database.hget(f"web.session:{session_id}", "bird").decode("utf-8"))

    # tempScore = int(database.hget(f"web.session:{session_id}", "tempScore"))
    # if tempScore >= 10:
    #     logger.info("trial maxed")
    #     raise HTTPException(status_code=403, detail="Sign in to continue")

    if media_type not in ("images", "songs"):
        logger.error(f"invalid media type {media_type}")
        raise HTTPException(status_code=422, detail="Invalid media type")

    answered = int(database.hget(f"web.session:{session_id}", "answered"))
    logger.info(f"answered: {answered}")
    # check to see if previous bird was answered
    if answered:  # if yes, give a new bird
        id_list = songBirds if media_type == "songs" else birdList
        currentBird = random.choice(id_list)
        user_id = int(database.hget(f"web.session:{session_id}", "user_id"))
        if user_id != 0:
            increment_bird_frequency(currentBird, user_id)
        prevB = database.hget(f"web.session:{session_id}",
                              "prevB").decode("utf-8")
        while currentBird == prevB and len(id_list) > 1:
            currentBird = random.choice(id_list)
        database.hset(f"web.session:{session_id}", "prevB", str(currentBird))
        database.hset(f"web.session:{session_id}", "bird", str(currentBird))
        logger.info("currentBird: " + str(currentBird))
        database.hset(f"web.session:{session_id}", "answered", "0")
        file_object, ext, content_type = await send_bird(
            request, currentBird, media_type, filters)
    else:  # if no, give the same bird
        file_object, ext, content_type = await send_bird(
            request,
            database.hget(f"web.session:{session_id}", "bird").decode("utf-8"),
            media_type,
            filters,
        )

    logger.info(f"file_object: {file_object}")
    logger.info(f"extension: {ext}")
    return send_file(file_object, media_type=content_type)
Beispiel #5
0
def get_bird():
    logger.info("endpoint: get bird")
    session_id = get_session_id()
    media_type = flask.request.args.get("media", "images", str)

    filters = Filter.parse(flask.request.args.get("addon", "", str))
    if bool(flask.request.args.get("bw", 0, int)):
        filters.bw = True
    logger.info(f"args: media: {media_type}; filters: {filters};")

    logger.info(
        "bird: " + database.hget(f"web.session:{session_id}", "bird").decode("utf-8")
    )

    tempScore = int(database.hget(f"web.session:{session_id}", "tempScore"))
    if tempScore >= 10:
        logger.info("trial maxed")
        flask.abort(403, "Sign in to continue")

    if media_type not in ("images", "songs"):
        logger.error(f"invalid media type {media_type}")
        flask.abort(406, "Invalid media type")

    answered = int(database.hget(f"web.session:{session_id}", "answered"))
    logger.info(f"answered: {answered}")
    # check to see if previous bird was answered
    if answered:  # if yes, give a new bird
        id_list = songBirds if media_type == "songs" else birdList
        currentBird = random.choice(id_list)
        user_id = int(database.hget(f"web.session:{session_id}", "user_id"))
        if user_id != 0:
            session_increment(user_id, "total", 1)
            increment_bird_frequency(currentBird, user_id)
        prevB = database.hget(f"web.session:{session_id}", "prevB").decode("utf-8")
        while currentBird == prevB and len(id_list) > 1:
            currentBird = random.choice(id_list)
        database.hset(f"web.session:{session_id}", "prevB", str(currentBird))
        database.hset(f"web.session:{session_id}", "bird", str(currentBird))
        database.hset(f"web.session:{session_id}", "media_type", str(media_type))
        logger.info("currentBird: " + str(currentBird))
        database.hset(f"web.session:{session_id}", "answered", "0")
        file_object, ext = asyncio.run(send_bird(currentBird, media_type, filters))
    else:  # if no, give the same bird
        file_object, ext = asyncio.run(
            send_bird(
                database.hget(f"web.session:{session_id}", "bird").decode("utf-8"),
                database.hget(f"web.session:{session_id}", "media_type").decode(
                    "utf-8"
                ),
                filters,
            )
        )

    logger.info(f"file_object: {file_object}")
    logger.info(f"extension: {ext}")
    return flask.send_file(file_object, attachment_filename=f"bird.{ext}")
Beispiel #6
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"),
            )
Beispiel #7
0
    async def info(self, ctx, *, arg):
        logger.info("command: info")
        arg = arg.lower().strip()

        filters = Filter.parse(arg)
        if filters.vc:
            filters.vc = False
            await ctx.send("**The VC filter is not allowed here!**")

        options = filters.display()
        arg = arg.split(" ")

        bird = None

        if len(arg[0]) == 4:
            bird = alpha_codes.get(arg[0].upper())

        if not bird:
            for i in reversed(range(1, 6)):
                # try the first 5 words, then first 4, etc. looking for a match
                matches = get_close_matches(
                    string.capwords(" ".join(arg[:i]).replace("-", " ")),
                    birdListMaster + sciListMaster,
                    n=1,
                    cutoff=0.8,
                )
                if matches:
                    bird = matches[0]
                    break

        if not bird:
            await ctx.send("Bird not found. Are you sure it's on the list?")
            return

        delete = await ctx.send("Please wait a moment.")
        if options:
            await ctx.send(f"**Detected filters**: `{'`, `'.join(options)}`")

        an = "an" if bird.lower()[0] in ("a", "e", "i", "o", "u") else "a"
        await send_bird(ctx,
                        bird,
                        "images",
                        filters,
                        message=f"Here's {an} *{bird.lower()}* image!")
        await send_bird(ctx,
                        bird,
                        "songs",
                        filters,
                        message=f"Here's {an} *{bird.lower()}* song!")
        await delete.delete()
        return
Beispiel #8
0
 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
Beispiel #9
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,
            )
Beispiel #10
0
async def _get_urls(
    session: aiohttp.ClientSession,
    bird: str,
    media_type: str,
    filters: Filter,
    retries: int = 0,
):
    """Returns a list of urls to Macaulay Library media.

    The amount of urls returned is specified in `COUNT`.
    Media URLs are fetched using Macaulay Library's internal JSON API,
    with `CATALOG_URL`. Raises a `GenericError` if fails.\n
    Some urls may return an error code of 476 (because it is still being processed),
    if so, ignore that url.

    `session` (aiohttp ClientSession)\n
    `bird` (str) - can be either common name or scientific name\n
    `media_type` (str) - either `p` for pictures, `a` for audio, or `v` for video\n
    `filters` (bot.filters Filter)
    """
    logger.info(f"getting file urls for {bird}")
    taxon_code = (await get_taxon(bird, session))[0]
    catalog_url = filters.url(taxon_code, media_type)
    async with session.get(catalog_url) as catalog_response:
        if catalog_response.status != 200:
            if retries >= 3:
                logger.info("Retried more than 3 times. Aborting...")
                raise GenericError(
                    f"An http error code of {catalog_response.status} occurred "
                    +
                    f"while fetching {catalog_url} for a {'image'if media_type=='p' else 'song'} for {bird}",
                    code=201,
                )
            retries += 1
            logger.info(
                f"An HTTP error occurred; Retries: {retries}; Sleeping: {1.5**retries}"
            )
            await asyncio.sleep(1.5**retries)
            urls = await _get_urls(session, bird, media_type, filters, retries)
            return urls

        catalog_data = await catalog_response.json()
        content = catalog_data["results"]["content"]
        urls = ([data["mediaUrl"]
                 for data in content] if filters.large or media_type == "a"
                else [data["previewUrl"] for data in content])
        if not urls:
            raise GenericError("No urls found.", code=100)
        return urls
Beispiel #11
0
 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
Beispiel #12
0
 async def filters(self, ctx):
     logger.info("command: filters")
     filters = Filter.aliases()
     embed = discord.Embed(
         title="Media Filters",
         type="rich",
         description="Filters can be space-separated or comma-separated. " +
         "You can use any alias to set filters. " +
         "Please note media will only be shown if it " +
         "matches all the filters, so using filters can " +
         "greatly reduce the number of media returned.",
         color=discord.Color.green(),
     )
     embed.set_author(name="Bird ID - An Ornithology Bot")
     for title, subdict in filters.items():
         value = "".join((f"**{name.title()}**: `{'`, `'.join(aliases)}`\n"
                          for name, aliases in subdict.items()))
         embed.add_field(name=title.title(), value=value, inline=False)
     await ctx.send(embed=embed)
Beispiel #13
0
    async def info(self, ctx, *, arg):
        logger.info("command: info")
        arg = arg.lower().strip()

        filters = Filter.parse(arg)
        options = filters.display()
        arg = arg.split(" ")

        bird = None

        if len(arg[0]) == 4:
            bird = alpha_codes.get(arg[0].upper())

        if not bird:
            for i in reversed(range(1, 6)):
                # try the first 6 words, then first 5, etc. looking for a match
                matches = get_close_matches(" ".join(arg[:i]),
                                            birdListMaster + sciListMaster,
                                            n=1)
                if matches:
                    bird = matches[0]
                    break

        if not bird:
            await ctx.send("Bird not found. Are you sure it's on the list?")
            return

        delete = await ctx.send("Please wait a moment.")
        if options:
            await ctx.send(f"**Detected filters**: `{'`, `'.join(options)}`")
        await send_bird(ctx,
                        bird,
                        "images",
                        filters,
                        message=f"Here's a *{bird.lower()}* image!")
        await send_bird(ctx,
                        bird,
                        "songs",
                        filters,
                        message=f"Here's a *{bird.lower()}* song!")
        await delete.delete()
        return
Beispiel #14
0
    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")
Beispiel #15
0
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)
Beispiel #16
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)
Beispiel #17
0
def bird_song(bird):
    path = asyncio.run(get_media(bird, "songs", Filter()))
    return flask.send_file(f"../{path[0]}")
Beispiel #18
0
    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
        )
Beispiel #19
0
    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)
Beispiel #20
0
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])
Beispiel #21
0
    async def start(self, ctx, *, args_str: str = ""):
        logger.info("command: start session")

        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

        filters = Filter.parse(args_str)

        args = args_str.lower().split(" ")
        logger.info(f"args: {args}")

        if "wiki" in args:
            wiki = ""
        else:
            wiki = "wiki"

        if "strict" in args:
            strict = "strict"
        else:
            strict = ""

        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 = ""

        logger.info(
            f"adding filters: {filters}; state: {state}; wiki: {wiki}; strict: {strict}"
        )

        database.hset(
            f"session.data:{ctx.author.id}",
            mapping={
                "start": round(time.time()),
                "stop": 0,
                "correct": 0,
                "incorrect": 0,
                "total": 0,
                "filter": str(filters.to_int()),
                "state": state,
                "taxon": taxon,
                "wiki": wiki,
                "strict": strict,
            },
        )
        await ctx.send(
            f"**Session started with options:**\n{await self._get_options(ctx)}"
        )
Beispiel #22
0
    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)
Beispiel #23
0
    async def edit(self, ctx, *, args_str: str = ""):
        logger.info("command: view session")

        if database.exists(f"session.data:{ctx.author.id}"):
            new_filter = Filter.parse(args_str, defaults=False)

            args = args_str.lower().split(" ")
            logger.info(f"args: {args}")

            new_filter ^= int(
                database.hget(f"session.data:{ctx.author.id}", "filter"))
            database.hset(f"session.data:{ctx.author.id}", "filter",
                          str(new_filter.to_int()))

            if "wiki" in args:
                if database.hget(f"session.data:{ctx.author.id}", "wiki"):
                    logger.info("enabling wiki embeds")
                    database.hset(f"session.data:{ctx.author.id}", "wiki", "")
                else:
                    logger.info("disabling wiki embeds")
                    database.hset(f"session.data:{ctx.author.id}", "wiki",
                                  "wiki")

            if "strict" in args:
                if database.hget(f"session.data:{ctx.author.id}", "strict"):
                    logger.info("disabling strict spelling")
                    database.hset(f"session.data:{ctx.author.id}", "strict",
                                  "")
                else:
                    logger.info("enabling strict spelling")
                    database.hset(f"session.data:{ctx.author.id}", "strict",
                                  "strict")

            states_args = set(states.keys()).intersection(
                {arg.upper()
                 for arg in args})
            if states_args:
                current_states = set(
                    database.hget(f"session.data:{ctx.author.id}",
                                  "state").decode("utf-8").split(" "))
                logger.info(f"toggle states: {states_args}")
                logger.info(f"current states: {current_states}")
                states_args.symmetric_difference_update(current_states)
                logger.info(f"new states: {states_args}")
                database.hset(
                    f"session.data:{ctx.author.id}",
                    "state",
                    " ".join(states_args).strip(),
                )

            taxon_args = set(taxons.keys()).intersection(
                {arg.lower()
                 for arg in args})
            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}")
                database.hset(
                    f"session.data:{ctx.author.id}",
                    "taxon",
                    " ".join(taxon_args).strip(),
                )

            await self._send_stats(ctx, "**Session started previously.**\n")
        else:
            await ctx.send(
                "**There is no session running.** *You can start one with `b!session start`*"
            )