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 "—"
def _format_urban_defn(definition: dict) -> discord.Embed: """ 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 bold phrases defn = defn.replace("[", "**").replace("]", "**") # Sanitise backticks and place in a code block if applicable. example = dots(definition["example"].replace("`", "’")) if example: example = f"`{string.trunc(example, 1000)}`" author = definition["author"] yes = definition["thumbs_up"] no = definition["thumbs_down"] permalink = definition["permalink"] embed = discord.Embed( title=title, description=string.trunc(defn), colour=0xFFFF00, url=permalink ) embed.set_author( name=f"{author} (\N{HEAVY BLACK HEART} {yes} " f"\N{PILE OF POO} {no})" ) 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 inspect_emoji(self, ctx, *, emoji: discord.Emoji = None): """ Note that this will only work for custom emojis. If no emoji name is provided, we list all emojis available in this guild. """ if emoji: desc = f'Created on {emoji.created_at.strftime("%c")}\n\n' if emoji.animated: desc += "Animated emoji\n" if emoji.require_colons: desc += "Must be wrapped in colons\n" if emoji.managed: desc += "Managed as part of a Twitch integration\n" if not emoji.roles: desc += "Emoji is usable by everyone here\n" rc = emoji.require_colons embed = discord.Embed( title=rc and f"`:{emoji.name}:`" or f"`{emoji}`", description=desc, url=emoji.url, colour=0xab19cf or emoji.animated and 0xd1851b, ) if emoji.roles: embed.add_field( name="Usable by", value=string.trunc(", ".join(map(str, emoji.roles)), 1024), ) embed.set_thumbnail(url=emoji.url) embed.set_author(name=f'Emoji in "{emoji.guild}"', icon_url=emoji.url) embed.set_footer(text=str(emoji.id), icon_url=emoji.url) await ctx.send(embed=embed) elif len(ctx.guild.emojis) == 0: await ctx.send("This server has no emojis yet...", delete_after=10) else: binder = bookbinding.StringBookBinder(ctx, max_lines=None) def key(e): return e.name for i, e in enumerate(sorted(ctx.guild.emojis, key=key)): binder.add_line(f"`{i + 1:03}`\t{e} \t `{e}`") binder.start()
async def fut(reply): resp = await destination.send(string.trunc(reply)) if not bot.debug: # Delete after 30s. await asyncio.sleep(30) futs = [resp.delete()] if ctx: futs.append(ctx.message.delete()) try: await asyncio.gather(*futs) except: pass
async def inspect_member(self, ctx, *, member: discord.Member = None): """ If no member is specified, then I will show your profile instead. """ if member is None: member = ctx.author embed = discord.Embed(title=f"`@{member}`", colour=member.colour) desc = "\n".join(( f"Display name: `{member.display_name}`", f'Joined here on: {member.joined_at.strftime("%c")}', f'Joined Discord on: {member.created_at.strftime("%c")}', f"Top role: {member.top_role}", f"Colour: `#{hex(member.colour.value)[2:].zfill(6)}`", "Status: " + { discord.Status.online: "Online", discord.Status.idle: "Away", discord.Status.dnd: "Busy", discord.Status.invisible: "Hiding", discord.Status.offline: "Offline", }.get(member.status, "On Mars"), f'Account type: {member.bot and "Bot" or "Human"}', )) embed.description = desc embed.set_thumbnail(url=member.avatar_url) embed.set_footer(text=str(member.id), icon_url=member.default_avatar_url) if member.roles: embed.add_field( name="Roles", value=string.trunc(", ".join(map(str, reversed(member.roles))), 1024), ) role_perms = Permissions.from_discord_type(member.guild_permissions) role_perms = {*role_perms.unmask()} chn_perms = member.permissions_in(ctx.channel) chn_perms = {*Permissions.from_discord_type(chn_perms).unmask()} # Calculate any extra perms granted for this channel only. chn_perms.difference_update(role_perms) if role_perms: role_perms = ", ".join(f"`{p}`" for p in role_perms) else: role_perms = "No role permissions granted (somehow)" embed.add_field(name="Role-granted permissions", value=role_perms) if member.activity: # This design is...not the best imho, but it is confusingly # defined in the API: # This attr can be a Game, Streaming or Activity, but Activity # itself can have a `playing` type which denotes a game, so... # how do we know which one to expect? Game is not a subtype # of activity nor vice versa! if isinstance(member.activity, discord.Activity): a = member.activity attrs = [] # Less verbose. z = attrs.append if a.start: z(f'Since: {a.start.strftime("%c")}') if a.end: z(f'Until: {a.end.strftime("%c")}') if a.details: z(f"Details: {a.details}") if a.small_image_text: z(f"Small tooltip: {a.small_image_text}") if a.large_image_text: z(f"Large tooltip: {a.large_image_text}") if not attrs: z(a.name) else: attrs.insert(0, f"Name: {a.name}") t = a.type t = "Activity" if t == discord.ActivityType.unknown else t.name.title( ) embed.add_field(name=t, value="\n".join(attrs)) elif isinstance(member.activity, discord.Game): embed.add_field(name="Playing", value=member.activity.name) elif isinstance(member.activity, discord.Streaming): a = member.activity embed.add_field( name="Streaming", value=f"[{a.twitch_name or a.name}]({a.url})\n" f'{a.details or ""}', ) if chn_perms: chn_perms = ", ".join(f"`{p}`" for p in chn_perms) embed.add_field(name="Additional permissions in this channel", value=chn_perms) await ctx.send("Member inspection", embed=embed)
async def inspect_channel(self, ctx, *, channel: GuildChannelConverter = None): """ Inspects a channel in this guild. If no channel is given, we check the current channel. """ channel = channel or ctx.channel category = channel.category category = category and category.name.upper() or None if isinstance(channel, discord.TextChannel): try: wh_count = len(await channel.webhooks()) except discord.Forbidden: wh_count = "I need `MANAGE_WEBHOOKS` first!" pin_count = len(await channel.pins()) try: invite_count = len(await channel.invites()) except discord.Forbidden: invite_count = "I need `MANAGE_CHANNELS` first!" embed = discord.Embed( title=f"`#{channel.name}`", colour=alg.rand_colour(), description="\n".join([ f"Type: Text channel", f'Created on: {channel.created_at.strftime("%c")}', f"Category: `{category}`", f"NSFW: {string.yn(channel.is_nsfw()).lower()}", f"Pin count: {pin_count}", f"Webhook count: {wh_count}", f"Invitations here: {invite_count}", ]), ) if channel.topic: embed.add_field(name="Topic", value=string.trunc(channel.topic, 1024), inline=False) if len(channel.members) == len(ctx.guild.members): embed.add_field( name="Accessible by", value="All " + string.plur_simple(len(channel.members), "member"), ) elif len(channel.members) > 10: embed.add_field(name="Accessible by", value=f"{len(channel.members)} members") elif channel.members: embed.add_field( name="Accessible by", value=", ".join(sorted(map(str, channel.members))), ) else: embed.add_field(name="Accessible by", value="No one has this role yet!") if channel.changed_roles: embed.add_field( name="Roles with custom permissions", value=", ".join( str(c) for c in sorted(channel.changed_roles, key=str)), ) else: channel: discord.VoiceChannel = channel embed = discord.Embed( title=f"`#{channel.name}`", colour=alg.rand_colour(), description="\n".join([ f"Type: Text channel", f'Created on: {channel.created_at.strftime("%c")}', f"Category: `{category}`", f"Bitrate: {channel.bitrate / 1000:,.2f}kbps", f"User limit: {channel.user_limit or None}", ]), ) if len(channel.members) == len(ctx.guild.members): embed.add_field( name="Members in this VC", value="All " + string.plur_simple(len(channel.members), "member"), ) elif len(channel.members) > 10: embed.add_field(name="Members in this VC", value=f"{len(channel.members)} members") elif channel.members: embed.add_field( name="Members in this VC", value=", ".join(sorted(map(str, channel.members))), ) else: embed.add_field(name="Members in this VC", value="No one is in this VC yet!") embed.set_author(name=f"Channel #{channel.position + 1}") embed.set_footer(text=str(channel.id)) await ctx.send("Channel inspection", embed=embed)
async def info(self, ctx, package): """ 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(): conn = await self.acquire_http() resp = await conn.get(url=url) 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", "Unknown") 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 = discord.Embed( title=name, description=string.trunc(summary, 2048), url=url, colour=alg.rand_colour(), ) 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)
async def tldrlegal_logic(self, ctx, query, verbose): """ Helper to prevent code duplication. """ http = await self.acquire_http() # Get search results async with http.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: page = await option_picker( ctx, *results, option_formatter=lambda o: o[0].replace("*", "∗")) if page is None: return await ctx.send("Took too long...") # Get the info into an object. async with http.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()) # Generate embed and send. embed = discord.Embed( title=license_info.name, description=string.trunc(license_info.brief), colour=alg.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)