Esempio n. 1
0
        def mk_page(body):
            """
            Makes a new page with the current body. This is a template
            for embeds to ensure a consistent layout if we can't fit the
            commands list on one page.
            """
            page = theme.generic_embed(ctx,
                                       title="All commands",
                                       description=body,
                                       avatar_injected=True)

            page.set_footer(text="Commands proceeded by ellipses signify "
                            "command groups with sub-commands available.")
            page.add_field(
                name="Want more information?",
                value=f"Run `{ctx.bot.command_prefix}help <command>` "
                f"for more details on a specific command!",
                inline=False,
            )

            page.add_field(
                name="Want a more spammy embed?",
                value=
                "Try running with the `-m` flag for added brief descriptions!")
            page.set_thumbnail(url=ctx.bot.user.avatar_url)

            return page
    async def commit_command(self, ctx):
        async with ctx.typing():
            async with aiohttp.ClientSession() as session:
                async with session.get(
                        "http://www.commitlogsfromlastnight.com/") as resp:
                    resp.raise_for_status()
                    data = await resp.text()

            soup = bs4.BeautifulSoup(data, features="html5lib")

            posts: bs4.Tag = soup.find("tbody").find_all("tr")
            post: bs4.Tag = random.choice(list(posts))

            committer = post.find(attrs={"class": "commiter"}).text
            avatar_link = post.find(attrs={"class": "avatarlink"})["href"]
            avatar = post.find(attrs={"class": "avatar"})["src"]

            date = post.find(attrs={"class": "date"}).text
            date = datetime.datetime.strptime(date, "%d/%m/%y %I:%M %p")

            commit = post.find(attrs={"class": "commit"})
            message = commit.text
            link = commit["href"]

        embed = theme.generic_embed(ctx,
                                    title="Commit Logs From Last Night",
                                    description=message,
                                    url=link,
                                    timestamp=date)
        embed.set_author(name=committer, url=avatar_link)
        embed.set_thumbnail(url=avatar)
        embed.set_footer(
            text="because real hackers pivot two hours before their demo")

        await ctx.send(embed=embed)
Esempio n. 3
0
 async def search_emoji_command(self, ctx, emoji: discord.Emoji):
     """Gets an emoji I can see from any guild I am in..."""
     embed = theme.generic_embed(ctx,
                                 title=emoji.name,
                                 description=str(emoji),
                                 url=emoji.url)
     embed.set_footer(text=str(emoji.guild))
     embed.set_image(url=emoji.url)
     await ctx.send(embed=embed)
Esempio n. 4
0
    async def radar_base(self, ctx):
        async with ctx.typing():
            gif = await self._download(utils.RADAR_US)

        embed = theme.generic_embed(
            ctx,
            title=ctx.command.brief,
            description=f"{utils.OVERVIEW_BASE}\n\nRun with the `highres` argument for higher resolution!",
        )
        embed.set_image(url="attachment://image.gif")
        await ctx.send(embed=embed, file=discord.File(gif, "image.gif"))
Esempio n. 5
0
        def embed(**kwargs):
            if "image" in kwargs:
                image = kwargs.pop("image")
            else:
                image = None
            embed = theme.generic_embed(ctx, title=title, url=layers.web_page, **kwargs)
            embed.set_author(name=author)

            if image:
                embed.set_image(url=image)
            return embed
Esempio n. 6
0
    async def iss_command(self, ctx):
        """
        Calculates where above the Earth's surface the ISS is currently,
        and plots it on a small map.
        """

        with ctx.channel.typing():
            # Plot the first point
            with io.BytesIO() as b:
                async with self.acquire_http_session() as http:
                    res = await http.request(
                        "GET",
                        "https://api.wheretheiss.at/v1/satellites/25544")

                    data = await res.json()
                    image_fut = self.plot(data["latitude"], data["longitude"],
                                          b)

                    assert isinstance(data, dict), "I...I don't understand..."

                    long = data["longitude"]
                    lat = data["latitude"]
                    time = datetime.datetime.fromtimestamp(data["timestamp"])
                    altitude = data["altitude"]
                    velocity = data["velocity"]

                    is_day = data["visibility"] == "daylight"

                    desc = "\n".join([
                        f"**Longitude**: {long:.3f}°E",
                        f'**Latitude**: {abs(lat):.3f}°{"N" if lat >= 0 else "S"}',
                        f"**Altitude**: {altitude:.3f} km",
                        f"**Velocity**: {velocity:.3f} km/h",
                        f"**Timestamp**: {time} UTC",
                    ])

                    embed = theme.generic_embed(
                        ctx=ctx,
                        title="International space station location",
                        description=desc,
                        url="http://www.esa.int/Our_Activities/Human_Spaceflight"
                        "/International_Space_Station"
                        "/Where_is_the_International_Space_Station ",
                    )

                    await image_fut

                    b.seek(0)
                    file = discord.File(b, "iss.png")

                    await ctx.send(file=file, embed=embed)
    def format_urban_definition(self, ctx, definition: dict):
        """
        Takes an UrbanDictionary word response and formats an embed
        to represent the output, before returning it.
        """

        # Adds ellipses to the end and truncates if a string is too long.
        def dots(string, limit=1024):
            return string if len(string) < limit else string[:limit -
                                                             3] + "..."

        title = definition["word"].title()
        defn = dots(definition["definition"], 2000)

        # Remove embedded URLS to stop Discord following them.
        defn = defn.replace("https://", "").replace("http://", "")
        # [] signify phrases linking to elsewhere.
        defn = self.format_links(defn, 2000)

        # Sanitise backticks and place in a code block if applicable.
        example = dots(definition["example"].replace("`", "’"))
        if example:
            example = self.format_links(example, 1024)
            example = dots(example, 1024)

        author = definition["author"]
        yes = definition["thumbs_up"]
        no = definition["thumbs_down"]
        permalink = definition["permalink"]

        embed = theme.generic_embed(
            ctx=ctx,
            title=
            f"{title} -- {author} (\N{THUMBS UP SIGN} {yes} \N{THUMBS DOWN SIGN} {no})",
            description=string.trunc(defn),
            colour=0xFFFF00,
            url=permalink,
        )

        if example:
            embed.add_field(name="Example", value=example)

        if "tags" in definition and definition["tags"]:
            tags = ", ".join(sorted({*definition["tags"]}))
            embed.set_footer(text=string.trunc(tags))
        return embed
Esempio n. 8
0
    async def version_command(self, ctx):
        """Shows versioning information and some other useful statistics."""
        licence = neko3.__license__
        repo = neko3.__repository__
        version = neko3.__version__
        uptime = self.up_time
        docstring = inspect.getdoc(neko3)
        if docstring:
            # Ensures the license is not included in the description, as that
            # is rather long.
            docstring = "".join(docstring.split("===", maxsplit=1)[0:1])

            docstring = [
                string.remove_single_lines(inspect.cleandoc(docstring))
            ]
        else:
            docstring = []

        docstring.append(f"_Licensed under the **{licence}**_")

        embed = theme.generic_embed(ctx,
                                    description=f"v{version}\n" +
                                    f"\n".join(docstring),
                                    url=repo)

        # Most recent changes
        # Must do in multiple stages to allow the cached property to do
        # magic first.
        when, update, count = await self.get_commits()

        embed.add_field(name=f"Update #{count} ({when})",
                        value=string.trunc(update, 1024),
                        inline=False)
        embed.add_field(
            name="Dependencies",
            value=
            f"Run `{ctx.prefix}dependencies` to see what makes Nekozilla tick!"
        )

        embed.set_image(url=ctx.bot.user.avatar_url)

        embed.set_footer(text=f"Uptime: {uptime}")

        embed.set_thumbnail(url=ctx.bot.user.avatar_url)
        await ctx.send(embed=embed)
Esempio n. 9
0
    async def _new_dialog(self, ctx):
        embeds = []
        # Includes those that cannot be run.
        all_cmds = list(sorted(ctx.bot.commands, key=str))

        commands = []

        for potential_command in all_cmds:
            # noinspection PyUnresolvedReferences
            try:
                if await potential_command.can_run(ctx):
                    commands.append(potential_command)
            except Exception as ex:
                if self.logger.getEffectiveLevel() >= logging.DEBUG:
                    self.logger.exception(ex)
                continue

        items_per_page = 6

        # We only show 10 commands per page.
        for i in range(0, len(commands), items_per_page):
            embed_page = theme.generic_embed(ctx,
                                             title="All commands",
                                             avatar_injected=True)

            next_commands = commands[i:i + items_per_page]

            for command in next_commands:
                command: neko_commands.Command = command
                # Special space char
                name = command.name

                embed_page.add_field(
                    name=name,
                    # If we put a zero space char first, and follow with an
                    # EM QUAD, it won't strip the space.
                    value="\u200e\u2001" + (command.brief or "—"),
                    inline=False,
                )

            embeds.append(embed_page)

        pagination.EmbedNavigator(pages=embeds, ctx=ctx).start()
Esempio n. 10
0
 async def alaska_radar_command(self, ctx):
     async with ctx.typing():
         gif = await self._download(utils.get_wide_urls_radar_closest_match("alaska")[1])
     embed = theme.generic_embed(ctx, title=ctx.command.brief, description=utils.OVERVIEW_BASE)
     embed.set_image(url="attachment://image.gif")
     await ctx.send(embed=embed, file=discord.File(gif, "image.gif"))
Esempio n. 11
0
    async def info_command(self, ctx, package: commands.clean_content):
        """
        Shows a summary for the given package name on PyPI, if there is one.
        """
        url = f"https://pypi.org/pypi/{parse.quote(package)}/json"

        # Seems like aiohttp is screwed up and will not parse these URLS.
        # Requests is fine though. Guess I have to use that...
        with ctx.typing():
            async with self.acquire_http_session() as http:
                async with http.get(url=url) as resp:
                    result = (await resp.json()
                              ) if 200 <= resp.status < 300 else None

        if result:
            data = result["info"]

            name = f'{data["name"]} v{data["version"]}'
            url = data["package_url"]
            summary = data.get("summary", "_No summary was provided_")
            author = data.get("author")
            serial = result.get("last_serial", "No serial")
            if isinstance(serial, int):
                serial = f"Serial #{serial}"

            # Shortens the classifier strings.
            classifiers = data.get("classifiers", [])
            if classifiers:
                fixed_classifiers = []
                for classifier in classifiers:
                    print()
                    if "::" in classifier:
                        _, _, classifier = classifier.rpartition("::")
                    classifier = f"`{classifier.strip()}`"
                    fixed_classifiers.append(classifier)
                classifiers = ", ".join(sorted(fixed_classifiers))

            other_attrs = {
                "License": data.get("license"),
                "Platform": data.get("platform"),
                "Homepage": data.get("home_page"),
                "Requires Python version": data.get("requires_python"),
                "Classifiers": classifiers,
            }

            embed = theme.generic_embed(ctx,
                                        title=name,
                                        description=string.trunc(
                                            summary, 2048),
                                        url=url,
                                        colour=algorithms.rand_colour())

            if author:
                embed.set_author(name=f"{author}")
            embed.set_footer(text=f"{serial}")

            for attr, value in other_attrs.items():
                if not value:
                    continue

                embed.add_field(name=attr, value=value)

            await ctx.send(embed=embed)

        else:
            await ctx.send(f"PyPI said: {resp.reason}", delete_after=10)
Esempio n. 12
0
 def embed_generator(pag, page, index):
     return theme.generic_embed(ctx,
                                title="Random bash.org quote",
                                description=page,
                                url="http://bash.org")
Esempio n. 13
0
 async def high_res_us_radar_command(self, ctx):
     async with ctx.typing():
         gif = await self._download(utils.RADAR_FULL_US)
     embed = theme.generic_embed(ctx, title=ctx.command.brief, description=utils.OVERVIEW_BASE)
     embed.set_image(url="attachment://image.gif")
     await ctx.send(embed=embed, file=discord.File(gif, "image.gif"))
Esempio n. 14
0
        def embed_generator(_, page, __):
            next_page = theme.generic_embed(
                ctx,
                title=f"Help for {ctx.bot.command_prefix}"
                f"{command.qualified_name}",
                avatar_injected=True)

            brief = command.brief
            examples = getattr(command, "examples", [])
            usage = f"{ctx.prefix}{command.qualified_name} {command.signature}"
            description = []
            cog = command.cog_name or ""
            module = ctx.bot.get_cog(cog)
            module = module.__module__ if module else "No cog!"

            parent = command.full_parent_name
            cooldown = getattr(command, "_buckets")

            if cooldown:
                cooldown = getattr(cooldown, "_cooldown")

            if not real_match:
                description.insert(0, f"Closest match for `{query}`")

            description.append(f"```asciidoc\n{usage}\n```\n")

            if brief:
                description.append(brief)
            next_page.description = "\n".join(description)

            next_page.add_field(name="Details", value=page, inline=False)

            if examples:
                examples = "\n".join(
                    f"- `{ctx.bot.command_prefix}{command.qualified_name} "
                    f"{ex}`" for ex in examples)
                next_page.add_field(name="Examples",
                                    value=examples,
                                    inline=True)

            if cog and module and ctx.author.id == ctx.bot.owner_id:
                next_page.add_field(name="Defined in",
                                    value=" ".join(
                                        (module,
                                         cog)).strip().replace(" ", "."),
                                    inline=True)

            if children:
                children_str = ", ".join(f"`{child.name}`"
                                         for child in children)
                next_page.add_field(name="Child commands",
                                    value=children_str,
                                    inline=True)

            if parent:
                next_page.add_field(name="Parent",
                                    value=f"`{parent}`",
                                    inline=True)

            if cooldown:
                timeout = cooldown.per
                if timeout.is_integer():
                    timeout = int(timeout)

                next_page.add_field(
                    name="Cooldown policy",
                    value=(f"{cooldown.type.name.title()}-scoped "
                           f"per {cooldown.rate} "
                           f'request{"s" if cooldown.rate - 1 else ""} '
                           f"with a timeout of {timeout} "
                           f'second{"s" if timeout - 1 else ""}'),
                    inline=True,
                )

            # pages[-1].set_thumbnail(url=ctx.bot.user.avatar_url)

            if hasattr(command.callback, "_probably_broken"):
                next_page.add_field(name="In active development",
                                    value="Expect voodoo-type behaviour!")

            return next_page
Esempio n. 15
0
 def generator(_, page, __):
     return theme.generic_embed(ctx, description=page)
Esempio n. 16
0
 async def hawaii_overview_command(self, ctx):
     async with ctx.typing():
         png = await self._download(utils.OVERVIEW_MAP_HI)
     embed = theme.generic_embed(ctx, title=ctx.command.brief, description=utils.OVERVIEW_BASE)
     embed.set_image(url="attachment://image.png")
     await ctx.send(utils.OVERVIEW_BASE, file=discord.File(png, "image.png"))
Esempio n. 17
0
    async def ping_command(self, ctx):
        # Calculate the message-send time. This is the time taken to the response.
        message_send_time = time.perf_counter()

        pong_or_ping = "PING" if ctx.invoked_with == "pong" else "PONG"

        msg = await ctx.send(f"{pong_or_ping}...")
        message_send_time = time.perf_counter() - message_send_time

        heartbeat_latency = ctx.bot.latency

        # Calculate the event loop latency. This is a good representation of how
        # slow the loop is running. We spin the processor up first on the
        # current core to get an accurate measurement of speed when the CPU core
        # is under full load.

        # Time to do a round trip on the event loop, and time to callback.
        end_sync, end_async, end_fn = 0, 0, 0
        sync_latency, async_latency, function_latency = 0, 0, 0

        # Used to measure latency of a task.
        async def coro():
            """
            Empty coroutine that is used to determine the rough waiting time
            in the event loop.
            """
            pass

        # Measures time between the task starting and the callback being hit.
        def sync_callback(_):
            """
            Callback invoked once a coroutine has been ensured as a future.
            This measures the rough time needed to invoke a callback.
            """
            nonlocal end_sync
            end_sync = time.perf_counter()

        def fn_callback():
            """
            Makes a guesstimate on how long a function takes to invoke relatively.
            """
            nonlocal end_fn
            end_fn = time.perf_counter()

        for _ in range(0, 200):
            pass  # Dummy work to spin the CPU up

        for i in range(0, PING_CPU_PASSES):
            start = time.perf_counter()
            async_call = ctx.bot.loop.create_task(coro())
            async_call.add_done_callback(sync_callback)
            await async_call
            end_async = time.perf_counter()

            sync_latency += end_sync - start
            async_latency += end_async - start

            start = time.perf_counter()
            fn_callback()
            function_latency += end_fn - start

        function_latency /= PING_CPU_PASSES
        async_latency /= PING_CPU_PASSES
        sync_latency /= PING_CPU_PASSES

        # We match the latencies with respect to the total time taken out of all
        # of them
        total_ping = 1.05 * (message_send_time + heartbeat_latency)
        total_loop = 1.05 * (async_latency + sync_latency + function_latency)

        message_send_time_pct = message_send_time * 100 / total_ping
        heartbeat_latency_pct = heartbeat_latency * 100 / total_ping
        async_latency_pct = async_latency * 100 / total_loop
        sync_latency_pct = sync_latency * 100 / total_loop
        function_latency_pct = function_latency * 100 / total_loop

        joiner = lambda *a: "\n".join(a)

        pong = joiner(
            "```diff",
            f"+ GATEW: {self.make_progress_bar(heartbeat_latency_pct)} {heartbeat_latency * 1_000: .2f}ms",
            f"-  REST: {self.make_progress_bar(message_send_time_pct)} {message_send_time * 1_000: .2f}ms",
            f"+ STACK: {self.make_progress_bar(function_latency_pct)} {function_latency * 1_000_000: .2f}µs",
            f"- CALLB: {self.make_progress_bar(sync_latency_pct)} {sync_latency * 1_000_000: .2f}µs",
            f"+   AIO: {self.make_progress_bar(async_latency_pct)} {async_latency * 1_000_000: .2f}µs",
            "```",
        )

        embed = theme.generic_embed(ctx,
                                    avatar_injected=True,
                                    title=pong_or_ping,
                                    description=pong)
        embed.set_footer(
            text=
            f"{string.plur_simple(ctx.bot.command_invoke_count, 'command')} run since startup"
        )

        await msg.edit(content="", embed=embed)
Esempio n. 18
0
    async def stats_command(self, ctx):
        import threading
        from datetime import timedelta
        import platform

        # Calculates the ping, and will store our message response a little
        # later
        ack_time = 0

        def callback(*_, **__):
            nonlocal ack_time
            ack_time = time.perf_counter()

        start_ack = time.perf_counter()
        future = ctx.bot.loop.create_task(ctx.send("Getting ping!"))
        future.add_done_callback(callback)

        message = await future

        event_loop_latency = time.perf_counter() - start_ack
        ack_time -= start_ack
        event_loop_latency -= ack_time

        users = max(len(ctx.bot.users), len(list(ctx.bot.get_all_members())))
        tasks = len(asyncio.Task.all_tasks(loop=asyncio.get_event_loop()))
        procs = 1 + len(psutil.Process().children(recursive=True))

        tasks_threads_procs = f"{tasks}/{threading.active_count()}/{procs}"

        stats = collections.OrderedDict({
            "Commands invoked":
            f"{ctx.bot.command_invoke_count}",
            "Users":
            f"{users:,}",
            "Guilds/channels":
            f"{len(ctx.bot.guilds):,}/"
            f"{len(list(ctx.bot.get_all_channels())):,}",
            "Commands/aliases":
            f"{len(frozenset(ctx.bot.walk_commands())):,}"
            f"/{len(ctx.bot.all_commands):,}",
            "Futures/Threads/Processes":
            tasks_threads_procs,
            "Cogs/extensions":
            f"{len(ctx.bot.cogs):,}/{len(ctx.bot.extensions):,}",
            "Bot uptime":
            str(timedelta(seconds=ctx.bot.up_time)),
            "System uptime":
            str(timedelta(seconds=time.perf_counter())),
            "Heartbeat latency":
            f"∼{ctx.bot.latency * 1000:,.2f}ms",
            "`ACK` latency":
            f"∼{ack_time * 1000:,.2f}ms",
            "Event loop latency":
            f"{event_loop_latency * 1e6:,.2f}µs",
            "Architecture":
            f"{platform.machine()} "
            f'{" ".join(platform.architecture())}',
            "Python":
            f"{platform.python_implementation()} "
            f"{platform.python_version()}\n"
            f'{" ".join(platform.python_build()).title()}\n'
            f"{platform.python_compiler()}",
        })

        if ctx.bot.shard_count and ctx.bot_shard_count > 1:
            stats["Shards"] = f"{ctx.bot.shard_count}"

        embed = theme.generic_embed(ctx)

        # embed.set_thumbnail(url=ctx.bot.user.avatar_url)

        embed.set_footer(text=platform.platform())

        for name, value in stats.items():
            embed.add_field(name=name,
                            value=value,
                            inline=len(str(value)) < 100)

        await message.edit(content="", embed=embed)

        em = "\N{REGIONAL INDICATOR SYMBOL LETTER X}"

        async def later():
            try:
                await message.add_reaction(em)
                await ctx.bot.wait_for(
                    "reaction_add",
                    timeout=300,
                    check=lambda r, u: r.emoji == em and not u.bot and r.
                    message.id == message.id and u.id == ctx.message.author.id,
                )
            except asyncio.TimeoutError:
                try:
                    await message.clear_reactions()
                finally:
                    return
            else:
                try:
                    await neko_commands.try_delete(message)
                    await neko_commands.try_delete(ctx)
                finally:
                    return

        # noinspection PyAsyncCall
        asyncio.create_task(later())
Esempio n. 19
0
    def worker(ctx, message):
        """Calculates all conversions on a separate thread."""
        # Parse potential matches by pattern matching.
        tokens = list(tokenizer.tokenize(message))

        if not tokens:
            raise ValueError("No potential unit matches found.")

        # Parse real unit measurements that we can convert.
        quantities = list(parser.parse(*tokens))

        if not quantities:
            raise ValueError("No actual unit matches found.")

        # Get any conversions
        equivalents = collections.OrderedDict()

        for quantity in quantities:
            compatible = conversions.get_compatible_models(quantity.unit,
                                                           ignore_self=True)

            # Convert to SI first.
            si = quantity.unit.to_si(quantity.value)

            this_equivalents = tuple(
                models.ValueModel(c.from_si(si), c) for c in compatible)

            equivalents[quantity] = this_equivalents

        embed = theme.generic_embed(ctx)

        mass_msg_added = False

        for original, equivalents in list(equivalents.items())[:MAX]:
            equiv_str = []
            for equivalent in equivalents:
                equivalent = models.pretty_print(
                    equivalent.value,
                    equivalent.name,
                    use_long_suffix=True,
                    use_std_form=not original.unit.never_use_std_form,
                    none_if_rounds_to_zero=True,
                )
                equiv_str.append(equivalent)

            equiv_str = list(filter(bool, equiv_str))

            if not equiv_str:
                continue

            embed.add_field(
                name=models.pretty_print(
                    original.value,
                    original.name,
                    use_long_suffix=True,
                    use_std_form=not original.unit.never_use_std_form,
                    none_if_rounds_to_zero=False,
                ),
                value="\n".join(equiv_str),
                inline=True,
            )

            if original.unit.unit_type == models.UnitCategoryModel.FORCE_MASS:
                if not mass_msg_added:
                    mass_msg_added = True
                    embed.set_footer(
                        text="This example assumes that mass measurements are "
                        "accelerating at 1G. Likewise, acceleration "
                        "assumes that it applies to 1kg mass.")

        if not len(embed.fields):
            del embed
            raise ValueError("No valid or non-zero conversions found.")

        return embed