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)
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)
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"))
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
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
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)
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()
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"))
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)
def embed_generator(pag, page, index): return theme.generic_embed(ctx, title="Random bash.org quote", description=page, url="http://bash.org")
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"))
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
def generator(_, page, __): return theme.generic_embed(ctx, description=page)
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"))
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)
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())
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