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}")
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
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
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()
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
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))
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)