Example #1
0
    async def append_to_bucket(bucket, user):
        bucket.members.add(user)

        if 1 < len(bucket.members) <= MAX_NAMED_MEMBERS:
            first = bucket.members[:-1]
            last = bucket.members[-1]

            first = list(map(str, first))
            last = str(last) if last else ""

            message = ", ".join(first) + f" and {last} paid their respects"
        elif len(bucket.members) > MAX_NAMED_MEMBERS:
            message = f"{len(bucket.members)} people paid their respects"
        else:
            assert len(
                bucket.members
            ) > 0, "Somehow appending first member, which is not allowed..."
            message = f"{bucket.members[0]} paid their respects"

        if bucket.reason:
            message += f" for {bucket.reason}"

        message = string.trunc(message)

        embed = embeds.Embed(description=message, colour=bucket.colour)

        # If the message is valid and exists, edit it. Otherwise,
        if bucket.message:
            await bucket.message.edit(embed=embed)
        else:
            bucket.message = await bucket.ctx.send(embed=embed)

        if ENABLE_REACT:
            await bucket.message.add_reaction(
                "\N{REGIONAL INDICATOR SYMBOL LETTER F}")
Example #2
0
    def build_page(self, paginator: Paginator, page: str,
                   page_index: int) -> discord.Embed:
        embed = embeds.Embed()

        embed.description = page
        embed.set_footer(text=f"p.{page_index + 1} of {len(paginator.pages)}")

        return embed
            def factory(_, page, __):
                if not incidents["unresolved"]:
                    color = status["color"]
                else:
                    color = get_impact_color(find_highest_impact(incidents["unresolved"]))

                e = embeds.Embed(
                    colour=color, title="API Status for discordapp.com", description=page, url=status["url"]
                )
                if footer_text != "None":
                    e.set_footer(text=footer_text[:2000])
                return e
Example #4
0
def generic_embed(ctx,
                  *args,
                  colour=random_colour,
                  avatar_injected=False,
                  **kwargs):
    if inspect.isfunction(colour):
        colour = colour()

    e = embeds.Embed(*args, colour=colour, **kwargs)
    author = __author__
    owner_user = discord.utils.get(ctx.bot.users, id=ctx.bot.owner_id)
    if owner_user:
        owner = f"(@{owner_user})"
        author_str = f"N³, by {author} {owner}"
        author_icon = str(owner_user.avatar_url)
        author_url = __repository__
        e.set_author(name=author_str, url=author_url, icon_url=author_icon)

    if avatar_injected:
        e.set_thumbnail(url=ctx.bot.user.avatar_url_as(static_format="png"))
    return e
Example #5
0
    async def tldr_command(self, ctx, *, page: str):
        """
        Similar to man pages, this shows information on how to use a command,
        the difference being that this is designed to be human readable.

        Usage:

        - tldr gcc
        - tldr gcc

        If unspecified, we check all platforms. This will take a little longer
        to respond.
        """
        supported_platforms = ("common", "linux", "osx", "sunos", "windows")

        if any(x in page for x in "#?/"):
            return await ctx.send("Invalid page name.", delete_after=10)

        url = "https://raw.githubusercontent.com/tldr-pages/tldr/master/pages/"

        async with aiohttp.ClientSession() as session:
            for platform in supported_platforms:
                async with session.get(f"{url}{platform}/{page}.md") as resp:
                    content = await resp.text()
                    if 200 <= resp.status < 300:
                        break

                if resp.status != 200:
                    return await ctx.send(f"Error: {resp.reason}.",
                                          delete_after=10)

            content = "".join(content).splitlines()

        if not content:
            raise RuntimeError("No response from GitHub. Is the page empty?")
        elif len(content) == 1:
            raise RuntimeError("No body, only a title. Is the page empty?")

        # First line is title if it starts with '#'
        if content[0].startswith("#"):
            title = content.pop(0)[1:].lstrip() + f" ({platform})"
        else:
            title = f"{page} ({platform.title()})"

        paginator = pagination.Paginator()
        last_line_was_bullet = False

        for line in content:
            # Removes the blank line between bullets and code examples.
            if last_line_was_bullet and not line.lstrip().startswith("- "):
                if not line.strip():
                    last_line_was_bullet = False
                    continue
            elif line.lstrip().startswith(" "):
                last_line_was_bullet = True

            paginator.add_line(line)

        pages = []
        for page in paginator.pages:
            page = scrub_tags(page)

            if page.strip():
                pages.append(embeds.Embed(title=title, description=page))

        booklet = pagination.EmbedNavigator(ctx=ctx, pages=pages)
        await booklet.start()
Example #6
0
def make_colour_embed(r, g, b, a=255):
    """
    Generates an embed to describe the given RGB(A) colour, then returns it.
    """
    # % alpha
    pct_a = round(100.0 * a / 255.0, 2)

    embed = embeds.Embed(color=discord.Color.from_rgb(r, g, b))

    hex_str = utils.to_hex(r, g, b, a).upper()
    short_hex = utils.to_short_hex(r, g, b, a)

    if short_hex:
        short_hex = short_hex.upper()

    rf, gf, bf, af = utils.to_float(r, g, b, a)

    hsl_h, hsl_s, hsl_l = utils.to_hsl(r, g, b)
    hsl_h = f"{hsl_h:.0f}\N{DEGREE SIGN}"
    hsl_s = f"{hsl_s:.0f}%"
    hsl_l = f"{hsl_l:.0f}%"

    hsv_h, hsv_s, hsv_v = utils.to_hsv(r, g, b)
    hsv_h = f"{hsv_h:.0f}\N{DEGREE SIGN}"
    hsv_s = f"{hsv_s:.0f}%"
    hsv_v = f"{hsv_v:.0f}%"

    cmyk_c, cmyk_m, cmyk_y, cmyk_k = utils.to_cmyk(r, g, b)
    cmyk_c = round(cmyk_c, 2)
    cmyk_m = round(cmyk_m, 2)
    cmyk_y = round(cmyk_y, 2)
    cmyk_k = round(cmyk_k, 2)

    title = utils.ColourNames.from_value((r, g, b))
    if title:
        # Title case!
        title = title.title()

        if not short_hex:
            title += f" ({hex_str})"
        else:
            title += f" ({hex_str}, {short_hex})"
    else:
        title = hex_str

    embed.title = title

    if a < 255:
        embed.description = f"{pct_a:.0f}% opacity"

    embed.add_field(
        name="RGB and RGBA",
        value=f"RGBb\t{r, g, b}\nRGBAb {r, g, b, a}\n" f"RGBf \t{rf, gf, bf}\nRGBAf  {rf, gf, bf, af}",
    )

    embed.add_field(
        name="Other systems",
        value=f"CMYK   ({cmyk_c}, {cmyk_m}, {cmyk_y}, {cmyk_k})\n"
        f"HSL\t\t({hsl_h}, {hsl_s}, {hsl_l})\n"
        f"HSV\t\t({hsv_h}, {hsv_s}, {hsv_v})",
    )

    footer = f'This is{" " if utils.is_web_safe(r, g, b, a) else " not "}web-safe.'

    if a < 255:
        footer += " Embed colour does not take into account alpha. "

    footer = "Values may not directly match the input, as they are " "limited by the gamut of 32-bit RGBA colour-space."

    embed.set_footer(text=footer)

    return embed
Example #7
0
    async def f_command(self, ctx: neko_commands.Context, *, reason=None):
        colour = discord.Colour.blurple().value

        await ctx.message.delete()
        bucket = self.buckets.get(ctx.channel)

        if bucket:
            msg = bucket.message.id

            # Get the last 10 recent messages. If the bucket message
            # is in there, then update, else, delete the old message if
            # possible and then resend the new one. If the bucket is too
            # old, start anew.
            most_recent = await ctx.channel.history(limit=REHOIST_AFTER
                                                    ).flatten()
            new_msg = algorithms.find(lambda m: m.id == msg, most_recent)

            if bucket.reason and reason:
                is_matching_reason = fuzzy_search.deep_ratio(
                    bucket.reason, reason)
                # if close match, e.g. there was a slight typo.
                is_matching_reason = is_matching_reason > FUZZY_SENSITIVITY_THRESH
            else:
                is_matching_reason = bucket.reason == reason or reason is None

            if new_msg:
                bucket.message = new_msg
            else:
                try:
                    await bucket.message.delete()
                    bucket.message = None
                except discord.NotFound:
                    if ctx.channel in self.buckets:
                        del self.buckets[ctx.channel]
                    bucket = None

            # If user gave a different reason, restart.
            if is_matching_reason:
                return await self.append_to_bucket(bucket, ctx.author)
            else:
                if ctx.channel in self.buckets:
                    try:
                        embed = bucket.message.embeds[0]
                        embed.colour = discord.Colour.greyple()
                        embed.set_footer(
                            text=f"{ctx.author} changed the subject! For shame!"
                        )
                        await bucket.message.edit(embed=embed)
                        await bucket.message.clear_reactions()
                        del self.buckets[ctx.channel]
                    except Exception:
                        pass
                    finally:
                        bucket = None

        message = None

        if not bucket:
            if reason is None:
                message = await ctx.send(embed=embeds.Embed(
                    description=f"{ctx.author} paid their respects",
                    colour=colour))
            else:
                # Replace square brackets with a full-width variant. This will
                # stop the link formatting being triggered. This prevents
                # dickheads from smuggling dodgy links into chat hidden by
                # a seemingly innocent string via this bot.
                reason = reason.replace("[", "[").replace("]", "]")

                message = await ctx.send(embed=embeds.Embed(
                    description=f"{ctx.author} paid their respects for"
                    f" {string.trunc(reason, 1980)}",
                    colour=colour,
                ))

        if ENABLE_REACT:
            await message.add_reaction("\N{REGIONAL INDICATOR SYMBOL LETTER F}"
                                       )

        f_bucket = RespectPaid(aggregates.MutableOrderedSet({ctx.author}),
                               message, colour, ctx, reason)

        self.buckets[ctx.channel] = f_bucket
        # noinspection PyAsyncCall
        asyncio.create_task(destroy_bucket_later(self, f_bucket))
Example #8
0
    async def tldr_legal_logic(self, ctx, query, verbose):
        """
        Helper to prevent code duplication.
        """
        async with aiohttp.ClientSession() as session:

            # Get search results
            async with session.get(f"{base_url}search",
                                   params={"q": query}) as resp:
                if resp.status != 200:
                    return await ctx.send(f"tldrlegal said {resp.reason!r}")

                results = self.get_results_from_html(await resp.text())

            count = len(results)

            if count == 0:
                return await ctx.send("Nothing was found.", delete_after=15)
            elif count == 1:
                # Get the URL
                page = results[0]
            else:
                string_results = [o[0].replace("*", "∗") for o in results]

                try:
                    page = await pagination.option_picker(*string_results,
                                                          ctx=ctx)

                    if page == pagination.NoOptionPicked():
                        return
                    else:
                        # Reverse index.
                        page = results[string_results.index(page)]
                except asyncio.TimeoutError:
                    return await ctx.send("Took too long...")

            # Get the info into an object.

            try:
                async with session.get(page[1]) as resp:
                    if resp.status != 200:
                        return await ctx.send(f"tldrlegal said {resp.reason!r}"
                                              )
                    license_info = self.get_license_info(
                        page[1], await resp.text())
            except ValueError as ex:
                return await ctx.send(ex)

        # Generate embed and send.
        embed = embeds.Embed(
            title=license_info.name,
            description=string.trunc(license_info.brief),
            colour=algorithms.rand_colour(),
            url=license_info.url,
        )
        embed.set_footer(text="Disclaimer: This is only a short summary of the"
                         " Full Text. No information on TLDRLegal is"
                         " legal advice.")

        def fmt(prs):
            if verbose:
                s = string.trunc("\n".join(f"**{n}** {d}" for n, d in prs),
                                 1024)
            else:
                s = string.trunc("\n".join(f"- {n}" for n, _ in prs), 1024)

            # Prevents errors for empty bodies.
            return s or "—"

        embed.add_field(name="__CAN__",
                        value=fmt(license_info.can),
                        inline=not verbose)
        embed.add_field(name="__CANNOT__",
                        value=fmt(license_info.cant),
                        inline=not verbose)
        embed.add_field(name="__MUST__",
                        value=fmt(license_info.must),
                        inline=not verbose)

        if not verbose:
            embed.add_field(name="\u200b",
                            value="_Run again using `tldrlegal more <query>` "
                            "to get a longer explanation!_")

        await ctx.send(embed=embed)