async def warn(self, ctx: Context, member: Member, *, reason: str): """Warn someone Warns do not give any punishments apart fron an entry in the warn list. """ guild, _ = await Guild.get_or_create(id=ctx.guild.id) user, _ = await User.get_or_create(id=member.id) warn = await Warn.create(moderator=ctx.author.id, guild=guild, user=user, reason=reason) embed = Embed(ctx, title=f"Warn [{warn.id}]", color=member.color) embed.description = t(ctx, "message", member=member.mention, reason=reason) await ctx.send(embed=embed) try: await member.send( t(ctx, "dm_message", guild=ctx.guild, reason=reason)) except (Forbidden, HTTPException, AttributeError): pass
async def warns(self, ctx: Context, *, member: Member = None): """See someone's warns If no member specified, this shows Your warns. """ if not member: member = ctx.author embed = Embed( ctx, title=t(ctx, "title", member=member), description="", color=member.color, ) await ctx.trigger_typing() warns = Warn.filter(user__id=member.id, guild__id=ctx.guild.id) if not await warns.count(): return await ctx.send(t(ctx, "no_mutes", member=member)) async for i in warns: moderator = ctx.bot.get_user(i.moderator) # TODO: Format time and use timezones (settings) embed.description += ( f"`{i.id}` {str(i.when.time())[:-10]} " f"{i.when.date()} **{moderator}**: *{i.reason}*\n") await ctx.send(embed=embed)
async def level_up_messages(self, ctx: Context, action: str): """Toggle level up messages on this server Level up messages are not sent until you reach level 6. """ guild, _ = await Guild.get_or_create(id=ctx.guild.id) if action == "disable": if not guild.level_up_messages: return await ctx.send( t(ctx, "already_disabled", guild=ctx.guild)) guild.level_up_messages = False await ctx.send(t(ctx, "disabled", guild=ctx.guild)) elif action == "enable": if guild.level_up_messages: return await ctx.send( t(ctx, "already_enabled", guild=ctx.guild)) guild.level_up_messages = True await ctx.send(t(ctx, "enabled", guild=ctx.guild)) await guild.save()
async def mutes(self, ctx: Context, *, member: Member = None): """See someone's mutes If no member specified, this shows Your mutes. """ if not member: member = ctx.author embed = Embed( ctx, title=t(ctx, "title", member=member), description="", color=member.color, ) await ctx.trigger_typing() mutes = Mute.filter(user__id=member.id, guild__id=ctx.guild.id) if not await mutes.count(): return await ctx.send(t(ctx, "no_mutes", member=member)) async for i in mutes: moderator = await self.bot.fetch_user(i.moderator) embed.description += ( f"`{i.id}` {str(i.start.time())[:-10]} " f"{i.start.date()} ({str(i.end - i.start)[:-7]}) " f"**{moderator}**: *{i.reason or t(ctx, 'no_reason')}* ") # TODO: Format time and use timezones if i.active: embed.description += "🔴" embed.description += "\n" await ctx.send(embed=embed)
async def mute(self, ctx: Context, member: Member, time: Timedelta, *, reason: str = None): """Mute someone Muting someone gives them the mute role specified by the muterole command and removes the role after the specified time has passed. Note: Mutes are checked every 10 seconds, so times are not perfect. """ mute = await Mute.filter(user__id=member.id, guild__id=ctx.guild.id, active=True).first() if mute: mute.end += time await mute.save() return await ctx.send( t(ctx, "message_extended", member=member, time=time)) # NOTE: Extensions don't add a mute entry, they just make the # active mute longer. # return await ctx.send(f"{member.name} is already muted.") user, _ = await User.get_or_create(id=member.id) guild, _ = await Guild.get_or_create(id=ctx.guild.id) if not guild.mute_role: return await ctx.send(t(ctx, "no_mute_role", guild=ctx.guild)) mute = await Mute.create( moderator=ctx.author.id, user=user, guild=guild, reason=reason, end=datetime.utcnow() + time, ) mute_role = ctx.guild.get_role(guild.mute_role) # TODO: Check if member has lower permissions required to mute them await member.add_roles( mute_role, reason=f"Muted by {ctx.author} for {time}, reason: {reason}") embed = Embed(ctx, title=f"Mute [{mute.id}]", color=member.color) embed.set_thumbnail(url=member.avatar_url) embed.description = t(ctx, "message", member=member.mention, time=time, reason=reason) await ctx.send(embed=embed) try: await member.send( t(ctx, "dm_message", guild=ctx.guild, time=time, reason=reason)) except (Forbidden, HTTPException, AttributeError): pass
async def language(self, ctx: Context): """Bot language""" guild = await Guild.get(id=ctx.guild.id) if not guild.locale: return await ctx.send(t(ctx, "not_set", guild=ctx.guild.name)) return await ctx.send( t(ctx, "message", guild=ctx.guild.name, locale=guild.locale))
async def bug(self, ctx: Context): """Where to report bugs and feature requests""" embed = Embed( ctx, title=t(ctx, "title"), description=t(ctx, "message"), ) await ctx.send(embed=embed)
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 invite(self, ctx: Context): """Nagatoro's bot invite link""" embed = Embed( ctx, title=t(ctx, "title"), url=t(ctx, "invite_url"), description=t(ctx, "message"), ) await ctx.send(embed=embed)
async def manga(self, ctx: Context, *, title: str): """Manga info from AniList""" query = """ query ($title: String) { Media (search: $title, type: MANGA) { title {romaji} coverImage {extraLarge color} description (asHtml: false) siteUrl rankings {rank allTime type context} status chapters volumes format averageScore genres } } """ manga = (await anilist(query, {"title": title}))["data"]["Media"] embed = Embed( ctx, title=manga["title"]["romaji"], description="", url=manga["siteUrl"], footer="Via AniList", ) embed.set_thumbnail(url=manga["coverImage"]["extraLarge"]) for i in manga["rankings"]: if not i["allTime"]: continue if i["type"] == "RATED": embed.description += "⭐" elif i["type"] == "POPULAR": embed.description += "❤️" embed.description += f" #{i['rank']} {i['context'].title()}\n" embed.description += "\n" if manga["description"]: description = clean_description(manga["description"]) embed.description += ( t(ctx, "synopsis_ellipsis", synopsis=description[:250]) if len(description) >= 250 else t(ctx, "synopsis", synopsis=description) ) if color_hex := manga["coverImage"]["color"]: embed.color = Color(int(color_hex.replace("#", ""), 16))
async def language_set(self, ctx: Context, language: str): """Set Nagatoro's language on this server""" if language not in available_locales(): return await ctx.send(t(ctx, "not_available", language=language)) guild = await Guild.get(id=ctx.guild.id) guild.locale = language await guild.save() await self.bot.generate_locale_cache() await ctx.send(t(ctx, "message", language=language))
async def prefix_delete(self, ctx: Context): """Delete the prefix from this server""" guild = await Guild.get(id=ctx.guild.id) if not guild.prefix: return await ctx.send(t(ctx, "not_set", guild=ctx.guild.name)) guild.prefix = None await guild.save() await self.bot.generate_prefix_cache() await ctx.send(t(ctx, "message", name=ctx.guild.name))
async def anime(self, ctx: Context, *, title: str): """Anime info from AniList""" query = """ query ($title: String) { Media (search: $title, type: ANIME) { title {romaji} coverImage {extraLarge color} description (asHtml: false) siteUrl rankings {rank allTime type context} status episodes duration season seasonYear format averageScore genres studios (isMain: true) {nodes {name}} } } """ anime = (await anilist(query, {"title": title}))["data"]["Media"] embed = Embed( ctx, title=anime["title"]["romaji"], description="", url=anime["siteUrl"], footer="Via AniList", ) embed.set_thumbnail(url=anime["coverImage"]["extraLarge"]) for i in anime["rankings"]: if not i["allTime"]: continue if i["type"] == "RATED": embed.description += "⭐" elif i["type"] == "POPULAR": embed.description += "❤️" embed.description += f" #{i['rank']} {i['context'].title()}\n" embed.description += "\n" if description := clean_description(anime["description"]): embed.description += ( t(ctx, "synopsis_ellipsis", synopsis=description[:250]) if len(description) >= 250 else t(ctx, "synopsis", synopsis=description) )
async def ping(self, ctx: Context): """Bot connection latency This isn't very accurate, and mainly used as a "is this bot alive?" command. """ embed = Embed( ctx, title=t(ctx, "title"), description=t(ctx, "message", ping=round(self.bot.latency * 1000)), ) await ctx.send(embed=embed)
async def unban(self, ctx: Context, user: UserC): """Unban someone Note: Only works with IDs. To get a user's ID, enable Developer Mode under Appearance Settings, right click on the user's name and select "Copy ID". """ if user.id not in [i.user.id for i in await ctx.guild.bans()]: return await ctx.send(t(ctx, "not_banned", user=user)) await ctx.guild.unban(user, reason=f"Moderator: {ctx.author}") await ctx.send(t(ctx, "message", user=user))
async def ranking_level(self, ctx: Context): """User ranking, by level""" embed = Embed(ctx, title=t(ctx, "title"), description="", color=Color.blue()) await ctx.trigger_typing() async for pos, i in aenumerate(User.all().order_by("-exp").limit(10), start=1): user = await self.bot.fetch_user(i.id) embed.description += t( ctx, "ranking_entry", pos=pos, user=user, lvl=i.level, exp=i.exp ) 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=t(ctx, "description", group), color=Color.blue(), ) commands = self.get_formatted_commands(await self.filter_commands( group.commands)) embed.add_field(name=t(ctx, "commands"), value="\n".join(commands)) await ctx.send(embed=embed)
async def send_cog_help(self, cog): ctx = self.context embed = Embed( ctx, title=t(ctx, "cog_commands", cog=cog.qualified_name), description=tc(ctx, cog), color=Color.blue(), ) commands = self.get_formatted_commands(await self.filter_commands( cog.get_commands())) embed.add_field(name=t(ctx, "commands"), value="\n".join(commands)) await ctx.send(embed=embed)
async def user(self, ctx: Context, *, user: Union[Member, User] = None): """Shows info about an user or a member""" if not user: user = ctx.author title = str(user) if not user.bot else t(ctx, "title_bot", user=user.name) embed = Embed(ctx, title=title, color=user.color) embed.set_thumbnail(url=user.avatar_url) embed.add_fields( (t(ctx, "id"), user.id), (t(ctx, "created_at"), user.created_at), ) await ctx.send(embed=embed)
async def uptime(self, ctx: Context): """Bot uptime""" timestamp_difference = round(time() - self.bot.start_timestamp) uptime = timedelta(seconds=timestamp_difference) embed = Embed(ctx, title=t(ctx, "title"), description=str(uptime)) await ctx.send(embed=embed)
async def warn_delete(self, ctx: Context, id: int): """Delete a warn from the database Use the warn id given when muting or viewing someone's warns (the number in square brackets, e.g. [32]). """ if not (warn := await Warn.get_or_none(id=id)): return await ctx.send(t(ctx, "doesnt_exist", id=id))
async def mute_delete(self, ctx: Context, id: int): """Delete a mute and unmute someone Use the mute id given when muting or viewing someone's mutes (the number in square brackets, e.g. [64]). """ if not (mute := await Mute.get_or_none(id=id)): return await ctx.send(t(ctx, "doesnt_exist", id=id))
async def balance(self, ctx: Context, *, member: Member = None): """Coin balance""" if not member: member = ctx.author user, _ = await User.get_or_create(id=member.id) await ctx.send(t(ctx, "messsage", member=member.name, bal=user.balance))
async def level(self, ctx: Context, *, member: Member = None): """User's level""" if not member: member = ctx.author user, _ = await User.get_or_create(id=member.id) await ctx.send(t(ctx, "message", member=member.name, lvl=user.level))
async def mute_role_set(self, ctx: Context, role: Role): """Set this server's mute role""" guild, _ = await Guild.get_or_create(id=ctx.guild.id) guild.mute_role = role.id await guild.save() await ctx.send(t(ctx, "message", role=role))
async def mute_role(self, ctx: Context): """Check the mute role This is the role given to muted users, it stays with them until the mute ends or they are unmuted manually. """ guild, _ = await Guild.get_or_create(id=ctx.guild.id) mute_role = ctx.guild.get_role(guild.mute_role) if not guild.mute_role: return await ctx.send(t(ctx, "not_set")) if not mute_role: return await ctx.send(t(ctx, "doesnt_exist")) return await ctx.send( t(ctx, "message", guild=ctx.guild, name=mute_role, id=mute_role.id))
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=t(ctx, "title", member=member.name), color=member.color ) embed.set_thumbnail(url=member.avatar_url) embed.add_fields( (t(ctx, "rank"), str(rank + 1)), (t(ctx, "level"), f"{user.level}"), (t(ctx, "experience"), f"{user.exp}/{next_level_exp} ({progress}%)"), (t(ctx, "balance"), t(ctx, "balance_value", bal=user.balance)), ) if mutes := await Mute.filter( guild__id=ctx.guild.id, user__id=member.id ).count(): embed.add_field(name=t(ctx, "mutes"), value=str(mutes))
async def prefix_set(self, ctx: Context, prefix: str): """Set a custom prefix for this server""" guild = await Guild.get(id=ctx.guild.id) guild.prefix = prefix await guild.save() await self.bot.generate_prefix_cache() await ctx.send(t(ctx, "message", prefix=prefix))
async def avatar(self, ctx: Context, *, user: User = None): """Shows an user's avatar""" if not user: user = ctx.author embed = Embed(ctx, title=t(ctx, "title", user=user.name)) embed.set_image(url=user.avatar_url_as(size=2048)) await ctx.send(embed=embed)