async def profile(self, ctx: Context, *, member: Member = None): """User's profile""" if not member: member = ctx.author user, _ = await User.get_or_create(id=member.id) # Calculate current level progress: # (exp - curr lvl req) * 100 / (curr lvl req - next lvl req) current_level_exp = (user.level * 4)**2 next_level_exp = ((user.level + 1) * 4)**2 progress = round((user.exp - current_level_exp) * 100 / (next_level_exp - current_level_exp)) # Find position of profile in global user ranking rank = (await User.all().order_by("-exp")).index(user) embed = Embed(ctx, title=f"{member.name}'s profile", color=member.color) embed.set_thumbnail(url=member.avatar_url) embed.add_fields( ("Rank", str(rank + 1)), ("Level", f"{user.level}"), ("Experience", f"{user.exp}/{next_level_exp} ({progress}%)"), ("Balance", f"{user.balance} coins"), ) if mutes := await Mute.filter(guild__id=ctx.guild.id, user__id=member.id).count(): embed.add_field(name="Mutes", value=str(mutes))
async def role(self, ctx: Context, *, role: Role): """Shows info about a role""" embed = Embed(ctx, title=t(ctx, "title", role=role.name), color=role.color) embed.add_field(name=t(ctx, "id"), value=role.id) if len(role.members) > 1: embed.add_field(name=t(ctx, "members"), value=str(len(role.members))) embed.add_field( name=t(ctx, "mentionable"), value=t(ctx, "mentionable_yes") if role.mentionable else t(ctx, "mentionable_no"), ) if role.color != Color.default(): embed.add_field( name=t(ctx, "color"), value=t( ctx, "color_value", hex=str(role.color), rgb=str(role.color.to_rgb()), ), ) embed.add_field(name=t(ctx, "created_at"), value=role.created_at) await ctx.send(embed=embed)
async def mutes_active(self, ctx: Context): """See active mutes""" embed = Embed(ctx, title="Active mutes") await ctx.trigger_typing() mutes = Mute.filter(guild__id=ctx.guild.id, active=True) if not await mutes.count(): return await ctx.send( f"There are no active mutes in **{ctx.guild.name}**.") # TODO: Split active mutes into different embeds when more than 10 # and add scrolling (◀️ ▶️) async for i in mutes.prefetch_related("user"): moderator = ctx.guild.get_member(i.moderator) user = ctx.guild.get_member(i.user.id) description = ( f"**Given at**: {str(i.start.time())[:-10]} {str(i.start.date())[5:]}\n" f"**Duration**: {str(i.end - i.start)[:-7]}\n" f"**Moderator**: {moderator.mention}") if i.reason: description += f"\n**Reason**: *{i.reason}*" embed.add_field(name=f"{user} [{i.id}]", value=description, inline=False) await ctx.send(embed=embed)
async def character(self, ctx: Context, *, name: str): """Character info from AniList""" query = """ query ($name: String) { Character (search: $name) { name {full} image {large} description (asHtml: false) siteUrl favourites media (perPage: 10) { edges { node { title {romaji} siteUrl } characterRole } } } } """ character = (await anilist(query, {"name": name}))["data"]["Character"] embed = Embed(ctx, title=character["name"]["full"], description="", url=character["siteUrl"], footer="Via AniList", color=Color.blue()) embed.set_thumbnail(url=character["image"]["large"]) if character["favourites"]: embed.description += f"❤️ {character['favourites']} favorites \n\n" if character["description"]: description = clean_description(character["description"]) embed.description += f"Description: ||{description[:250]}...||" \ if len(description) >= 250 \ else f"Description: ||{description}||" appears_in = ["Main 🌕 Supporting 🌗 Background 🌑"] for i in character["media"]["edges"]: role = i["characterRole"] \ .replace("MAIN", "🌕") \ .replace("SUPPORTING", "🌗") \ .replace("BACKGROUND", "🌑") appears_in.append(f"{role} [{i['node']['title']['romaji']}]" f"({i['node']['siteUrl']})") embed.add_field(name="Appears in", value="\n".join(appears_in)) await ctx.send(embed=embed)
async def studio(self, ctx: Context, *, name: str): """Studio info from AniList""" query = """ query ($name: String) { Studio (search: $name, sort: SEARCH_MATCH) { name siteUrl isAnimationStudio media (sort: POPULARITY_DESC, perPage: 10) { nodes { title {romaji} coverImage {extraLarge} siteUrl popularity favourites } } } } """ studio = (await anilist(query, {"name": name}))["data"]["Studio"] embed = Embed( ctx, title=studio["name"], url=studio["siteUrl"], footer="Via AniList" ) embed.set_thumbnail(url=studio["media"]["nodes"][0]["coverImage"]["extraLarge"]) if studio["isAnimationStudio"]: embed.description = t(ctx, "animation_studio") # TODO: Observe, if this breaks when isAnimationStudio=False. most_popular = [t(ctx, "most_popular_header")] for i in studio["media"]["nodes"]: most_popular.append( t( ctx, "most_popular_item", popularity=i["popularity"], favorites=i["favourites"], title=i["title"]["romaji"], url=i["siteUrl"], ) ) embed.add_field( name=t(ctx, "most_popular_title"), value="\n".join(most_popular) ) await ctx.send(embed=embed)
async def send_group_help(self, group): ctx = self.context embed = Embed( ctx, title=f"`{self.get_command_signature(group)}`", description=group.help, color=Color.blue(), ) commands = self.get_formatted_commands(await self.filter_commands( group.commands)) embed.add_field(name="Commands", value="\n".join(commands)) await ctx.send(embed=embed)
async def send_cog_help(self, cog): ctx = self.context embed = Embed( ctx, title=f"{cog.qualified_name} Commands", description=cog.description, color=Color.blue(), ) commands = self.get_formatted_commands(await self.filter_commands( cog.get_commands())) embed.add_field(name="Commands", value="\n".join(commands)) await ctx.send(embed=embed)
async def studio(self, ctx: Context, *, name: str): """Studio info from AniList""" query = """ query ($name: String) { Studio (search: $name, sort: SEARCH_MATCH) { name siteUrl isAnimationStudio media (sort: POPULARITY_DESC, perPage: 10) { nodes { title {romaji} coverImage {extraLarge} siteUrl popularity favourites } } } } """ studio = (await anilist(query, {"name": name}))["data"]["Studio"] embed = Embed(ctx, title=studio["name"], url=studio["siteUrl"], footer="Via AniList") embed.set_thumbnail( url=studio["media"]["nodes"][0]["coverImage"]["extraLarge"]) if studio["isAnimationStudio"]: embed.description = "Animation Studio" # TODO: Observe, if this breaks when isAnimationStudio=False. most_popular = ["Popularity ⭐ Favorites ❤"] for i in studio["media"]["nodes"]: most_popular.append(f"{i['popularity']} ⭐ {i['favourites']} ❤️ " f"[{i['title']['romaji']}]({i['siteUrl']}) ") embed.add_field(name="Most popular productions", value="\n".join(most_popular)) await ctx.send(embed=embed)
async def role(self, ctx: Context, *, role: Role): """Shows info about a role""" embed = Embed(ctx, title=role.name, color=role.color) embed.add_field(name="ID", value=role.id) if len(role.members) > 1: embed.add_field(name="Members", value=str(len(role.members))) embed.add_field(name="Mentionable", value="Yes" if role.mentionable else "No") if role.color != Color.default(): embed.add_field(name="Color", value=f"{role.color}, rgb{role.color.to_rgb()}") embed.add_field(name="Created at", value=role.created_at) await ctx.send(embed=embed)
async def mutes_active(self, ctx: Context): """See active mutes""" embed = Embed(ctx, title=t(ctx, "title")) await ctx.trigger_typing() mutes = Mute.filter(guild__id=ctx.guild.id, active=True) if not await mutes.count(): return await ctx.send(t(ctx, "no_mutes", guild=ctx.guild)) # TODO: Split active mutes into different embeds when more than 10 # and add scrolling (◀️ ▶️) async for i in mutes.prefetch_related("user"): moderator = ctx.guild.get_member(i.moderator) user = ctx.guild.get_member(i.user.id) if i.reason: description = t( ctx, "entry_with_reason", given_at= f"{str(i.start.time())[:-10]} {str(i.start.date())[5:]}", duration=i.end - i.start, moderator=moderator.mention, reason=i.reason, ) else: description = t( ctx, "entry", given_at= f"{str(i.start.time())[:-10]} {str(i.start.date())[5:]}", duration=i.end - i.start, moderator=moderator.mention, ) embed.add_field(name=f"{user} [{i.id}]", value=description, inline=False) await ctx.send(embed=embed)
async def send_bot_help(self, mapping): ctx = self.context embed = Embed( ctx, title="Commands", description=self.get_opening_note(), color=Color.blue(), ) for cog, commands in mapping.items(): if not cog: # Don't show commands without a cog (e.g. the help command) continue category_name = cog.qualified_name filtered_commands = await self.filter_commands(commands) embed.add_field( name=category_name, value=", ".join(i.name for i in filtered_commands), inline=False, ) await ctx.send(embed=embed)
async def anilist(self, ctx: Context, *, username: str): """AniList profile Shows anime/manga stats and favorites. """ query = """ query ($username: String) { User (name: $username) { name avatar {large} siteUrl favourites { anime { nodes {title {romaji} siteUrl} } manga { nodes {title {romaji} siteUrl} } characters { nodes {name {full} siteUrl} } staff { nodes {name {full} siteUrl} } studios { nodes {name siteUrl} } } } } """ list_query = """ query ($username: String, $type: MediaType) { MediaListCollection (userName: $username, type: $type) { lists {status name entries {id}} } } """ await ctx.trigger_typing() user = (await anilist(query, {"username": username}))["data"]["User"] anime_lists, manga_lists = [ (await anilist(list_query, {"username": username, "type": i}))["data"][ "MediaListCollection" ]["lists"] for i in ["ANIME", "MANGA"] ] embed = Embed( ctx, title=t(ctx, "title", user=user["name"]), url=user["siteUrl"], footer="Via AniList", ) embed.set_thumbnail(url=user["avatar"]["large"]) if anime_lists: anime_list_body = "" for i in anime_lists: if i["status"]: status = ( i["status"] .replace("COMPLETED", "✅") .replace("PLANNING", "🗓️") .replace("DROPPED", "🗑️") .replace("CURRENT", "📺") .replace("PAUSED", "⏸️") .replace("REPEATING", "🔁") ) else: status = "⚙️" anime_list_body += f"{status} **{len(i['entries'])}** " f"{i['name']}\n" embed.add_field(name=t(ctx, "anime"), value=anime_list_body) if manga_lists: manga_list_body = "" for i in manga_lists: if i["status"]: status = ( i["status"] .replace("COMPLETED", "✅") .replace("PLANNING", "🗓️") .replace("DROPPED", "🗑️️") .replace("CURRENT", "📖") .replace("PAUSED", "⏸️") .replace("REPEATING", "🔁") ) else: status = "⚙️" manga_list_body += f"{status} **{len(i['entries'])}** " f"{i['name']}\n" embed.add_field(name=t(ctx, "manga"), value=manga_list_body) if any(user["favourites"][i]["nodes"] for i in user["favourites"]): favorites_body = "" for i in user["favourites"]: if not user["favourites"][i]["nodes"]: continue # Section header favorites_body += f"__{i.title()}__" # Show total amount of favs if they exceed a limit (3) if (favorites_count := len(user["favourites"][i]["nodes"])) > 3: favorites_body += f" ({favorites_count})" favorites_body += "\n" for item in user["favourites"][i]["nodes"][:3]: # There are three types of names, so unify them. if i in ["anime", "manga"]: name = item["title"]["romaji"] elif i in ["characters", "staff"]: name = item["name"]["full"] else: name = item["name"] favorites_body += f"[{name}]({item['siteUrl']})\n" embed.add_field( name=t(ctx, "favorites"), value=favorites_body, inline=False )
async def character(self, ctx: Context, *, name: str): """Character info from AniList""" query = """ query ($name: String) { Character (search: $name) { name {full} image {large} description (asHtml: false) siteUrl favourites media (perPage: 10) { edges { node { title {romaji} siteUrl } characterRole } } } } """ character = (await anilist(query, {"name": name}))["data"]["Character"] embed = Embed( ctx, title=character["name"]["full"], description="", url=character["siteUrl"], footer="Via AniList", color=Color.blue(), ) embed.set_thumbnail(url=character["image"]["large"]) if character["favourites"]: embed.description += ( t(ctx, "favorites", favorites=character["favourites"]) + "\n\n" ) if character["description"]: description = clean_description(character["description"]) embed.description += ( t(ctx, "character_description_ellipsis", description=description[:250]) if len(description) >= 250 else t(ctx, "character_description", description=description) ) appears_in = [t(ctx, "appears_in_header")] for i in character["media"]["edges"]: role = ( i["characterRole"] .replace("MAIN", "🌕") .replace("SUPPORTING", "🌗") .replace("BACKGROUND", "🌑") ) appears_in.append( t( ctx, "appears_in_item", role=role, title=i["node"]["title"]["romaji"], url=i["node"]["siteUrl"], ) ) embed.add_field(name=t(ctx, "appears_in_title"), value="\n".join(appears_in)) await ctx.send(embed=embed)