diff --git a/cogs/Levels.py b/cogs/Levels.py index be444dc..d060c76 100644 --- a/cogs/Levels.py +++ b/cogs/Levels.py @@ -118,12 +118,9 @@ def get_total_xp_for_level(level: int) -> int: try: return sum( - [ - exp - for exp in [ - LEVELS_AND_XP[lvl] for lvl in range(1, level + 1) - ] - ][::-1] + [exp for exp in [LEVELS_AND_XP[lvl] for lvl in range(1, level + 1)]][ + ::-1 + ] ) except KeyError: raise ValueError( @@ -153,9 +150,7 @@ async def change_cooldown(self, rate: int, per: float) -> None: raise LevelingSystemError( "Invalid rate or per. Values must be greater than zero" ) - self._cooldown = CooldownMapping.from_cooldown( - rate, per, BucketType.member - ) + self._cooldown = CooldownMapping.from_cooldown(rate, per, BucketType.member) self.__rate = rate self.__per = per @@ -183,9 +178,7 @@ async def set_level(self, member: Member, level: int) -> None: lvl=level, ) else: - raise LevelingSystemError( - f'Parameter "level" must be from 0-{MAX_LEVEL}' - ) + raise LevelingSystemError(f'Parameter "level" must be from 0-{MAX_LEVEL}') async def _update_record( self, member: Union[Member, int], level: int, xp: int, guild_id: int @@ -200,9 +193,7 @@ async def _update_record( ( level, xp, - member.id - if isinstance(member, (Member, ClientUser)) - else member, + member.id if isinstance(member, (Member, ClientUser)) else member, guild_id, ), ) @@ -383,9 +374,7 @@ def roundify(im, rad): d.text((115, 96), str(lvl), font=fnt, fill=(0, 0, 0, 255)) # Rank d.text((113, 130), f"#{rank}", font=fnt, fill=(0, 0, 0, 255)) - d.rectangle( - (44, 186, 44 + width, 186 + 21), fill=(255, 255, 255, 255) - ) + d.rectangle((44, 186, 44 + width, 186 + 21), fill=(255, 255, 255, 255)) txt.paste(avatar_img, (489, 23)) out = Image.alpha_composite(base, txt) @@ -468,9 +457,7 @@ async def on_level_up(self, msg: Message, level: int): if await self.is_role_reward(msg.guild, level): role = await self.get_role_reward(msg.guild, level) if role is not None: - await msg.author.add_roles( - role, reason=f"Level up to level {level}" - ) + await msg.author.add_roles(role, reason=f"Level up to level {level}") async def is_role_reward(self, guild: Guild, level: int) -> bool: query = await self.bot.db.execute( @@ -628,9 +615,8 @@ async def leaderboard(self, inter: ApplicationCommandInteraction): await inter.response.defer() limit = 10 set_user = False - records = await self.controller.get_leaderboard( - inter.guild, limit=limit - ) + # get a few more users than limit so in case user is not in server the leaderboard is still full + records = await self.controller.get_leaderboard(inter.guild, limit=limit + 5) try: cmd_user = await self.controller.get_user(inter.author) except UserNotFound: @@ -640,21 +626,32 @@ async def leaderboard(self, inter: ApplicationCommandInteraction): return await errorEmb(inter, text="No records found!") embed = Embed(title="Leaderboard", color=0x00FF00) - for i, record in enumerate(records): + j = 0 + for i in range(len(records)): + if i == len(records) or j == limit: + break + record = records[i] user = await self.bot.fetch_user(record.user_id) + # check if the user is in the server + if user not in inter.guild.members: + records.pop(i) + continue + if record.user_id == inter.author.id: embed.add_field( - name=f"{i + 1}. {user.name} ~ You ", + name=f"{j + 1}. {user.name} ~ You ", value=f"Level: {record.lvl}\nTotal XP: {record.total_exp:,}", inline=False, ) set_user = True else: embed.add_field( - name=f"{i + 1}. {user.name}", + name=f"{j + 1}. {user.name}", value=f"Level: {record.lvl}\nTotal XP: {record.total_exp:,}", inline=False, ) + + j += 1 if not set_user: rank = await self.controller.get_rank(inter.guild.id, cmd_user) embed.add_field( @@ -684,9 +681,7 @@ async def set_lvl( self, inter: ApplicationCommandInteraction, user: Member, - level: int = Param( - description="The level to set the user to", le=100, ge=0 - ), + level: int = Param(description="The level to set the user to", le=100, ge=0), ): """ Set a user's level @@ -734,9 +729,7 @@ async def add( (inter.guild.id, role.id), ): sql = "INSERT OR IGNORE INTO role_rewards (guild_id, role_id, required_lvl) VALUES (?, ?, ?)" - await self.bot.db.execute( - sql, (inter.guild.id, role.id, level_needed) - ) + await self.bot.db.execute(sql, (inter.guild.id, role.id, level_needed)) await self.bot.db.commit() return await sucEmb( inter, diff --git a/cogs/Staff.py b/cogs/Staff.py index d2d6429..e302399 100644 --- a/cogs/Staff.py +++ b/cogs/Staff.py @@ -14,6 +14,7 @@ from utils.DBhandlers import RolesHandler, WarningHandler from utils.bot import OGIROID +from utils.CONSTANTS import BAN_APPEAL_LINK from utils.exceptions import ReactionAlreadyExists, ReactionNotFound from utils.shortcuts import ( sucEmb, @@ -81,9 +82,7 @@ async def on_ready(self): self.warning: WarningHandler = WarningHandler(self.bot, self.bot.db) await self.reaction_roles.startup() - @commands.slash_command( - name="ban", description="Bans a user from the server." - ) + @commands.slash_command(name="ban", description="Bans a user from the server.") @commands.has_permissions(ban_members=True) @commands.guild_only() async def ban( @@ -96,6 +95,10 @@ async def ban( default=0, choices=[0, 1, 2, 3, 4, 5, 6, 7], ), + dm_user: bool = ParamInfo( + description="Whether to DM the user or not.", + default=True, + ), ): """Bans a user from the server.""" await inter.guild.ban( @@ -103,6 +106,21 @@ async def ban( reason=reason, clean_history_duration=dt.timedelta(days=delete_messages), ) + + if dm_user: + try: + if reason is None: + await user.send( + f"You have been banned from {inter.guild.name}.\nBan Appeal: {BAN_APPEAL_LINK}", + suppress_embeds=True, + ) + else: + await user.send( + f"You have been banned from {inter.guild.name} for {reason}.\nBan Appeal: {BAN_APPEAL_LINK}", + suppress_embeds=True, + ) + except disnake.errors.Forbidden: + pass await sucEmb(inter, "User has been banned successfully!") @commands.slash_command( @@ -126,9 +144,7 @@ async def softban( await asyncio.sleep(5) await inter.guild.unban(user=user, reason="softban unban") - @commands.slash_command( - name="kick", description="Kicks a user from the server." - ) + @commands.slash_command(name="kick", description="Kicks a user from the server.") @commands.has_permissions(kick_members=True) @commands.guild_only() async def kick( @@ -141,9 +157,7 @@ async def kick( await member.kick(reason=reason) await sucEmb(inter, "User has been kicked successfully!") - @commands.slash_command( - name="unban", description="Unbans a user from the server." - ) + @commands.slash_command(name="unban", description="Unbans a user from the server.") @commands.has_permissions(ban_members=True) @commands.guild_only() async def unban( @@ -151,9 +165,7 @@ async def unban( ): """Unbans a user from the server.""" try: - await inter.guild.unban( - disnake.Object(id=int(user_id)), reason=reason - ) + await inter.guild.unban(disnake.Object(id=int(user_id)), reason=reason) except ValueError: return await errorEmb(inter, "Invalid user ID!") except disnake.errors.NotFound: @@ -170,9 +182,7 @@ async def mute( self, inter: ApplicationCommandInteraction, member: disnake.Member, - duration: str = ParamInfo( - description="Format: 1s, 1m, 1h, 1d, max: 28d" - ), + duration: str = ParamInfo(description="Format: 1s, 1m, 1h, 1d, max: 28d"), reason: str = None, ): """Mutes a user from the server.""" @@ -225,9 +235,7 @@ async def unmute( await sucEmb(inter, "User has been unmuted successfully!") - @commands.slash_command( - name="warn", description="Warns a user from the server." - ) + @commands.slash_command(name="warn", description="Warns a user from the server.") @commands.has_permissions(manage_roles=True) @commands.guild_only() async def warn( @@ -279,9 +287,7 @@ def check(m): return m.author == inter.author and m.channel == inter.channel try: - msg = await self.bot.wait_for( - "message", check=check, timeout=60 - ) + msg = await self.bot.wait_for("message", check=check, timeout=60) except asyncio.TimeoutError: return await errorEmb(inter, "Timed out!") @@ -319,18 +325,14 @@ async def warnings( await warnings_embed(inter, member=member, warnings=warnings) - @commands.slash_command( - description="Steals an emoji from a different server." - ) + @commands.slash_command(description="Steals an emoji from a different server.") @commands.guild_only() @commands.has_permissions(manage_emojis=True) async def stealemoji( self, inter: ApplicationCommandInteraction, emoji: disnake.PartialEmoji, - name=Option( - name="name", required=False, description="Name of the emoji" - ), + name=Option(name="name", required=False, description="Name of the emoji"), ): """This clones a specified emoji that optionally only specified roles are allowed to use. @@ -371,8 +373,7 @@ async def addemoji( try: msg = await self.bot.wait_for( "message", - check=lambda m: m.author == inter.author - and m.channel == inter.channel, + check=lambda m: m.author == inter.author and m.channel == inter.channel, ) except asyncio.TimeoutError: return await inter.send("Timed out!") @@ -409,9 +410,7 @@ async def prune(self, inter: ApplicationCommandInteraction, amount: int): await inter.send("Amount is too high, please use a lower amount") return await inter.channel.purge(limit=amount) - await inter.send( - f"Deleted {amount} messages successfully!", ephemeral=True - ) + await inter.send(f"Deleted {amount} messages successfully!", ephemeral=True) @commands.slash_command(name="channellock") @commands.guild_only() @@ -477,9 +476,7 @@ async def add_reaction_role( emoji = PartialEmoji.from_str(emoji) try: - await self.reaction_roles.create_message( - message_id, role.id, str(emoji) - ) + await self.reaction_roles.create_message(message_id, role.id, str(emoji)) except ReactionAlreadyExists: return await errorEmb(inter, "Reaction already exists!") @@ -506,9 +503,7 @@ async def remove_reaction_role( await message.remove_reaction(emoji, inter.author) try: - await self.reaction_roles.remove_message( - message_id, str(emoji), role.id - ) + await self.reaction_roles.remove_message(message_id, str(emoji), role.id) except ReactionNotFound: return await errorEmb(inter, "Reaction does not exist!") @@ -573,9 +568,7 @@ def check(m): await inter.send("Please send the message", ephemeral=True) try: - msg = await self.bot.wait_for( - "message", check=check, timeout=300.0 - ) + msg = await self.bot.wait_for("message", check=check, timeout=300.0) except asyncio.exceptions.TimeoutError: return await errorEmb( inter, "Due to no response the operation was canceled" @@ -656,9 +649,7 @@ async def add_button( components = disnake.ui.View.from_message(message) - custom_id = ( - f"{role.id}-{emoji.name if emoji.is_unicode_emoji() else emoji.id}" - ) + custom_id = f"{role.id}-{emoji.name if emoji.is_unicode_emoji() else emoji.id}" new_button = disnake.ui.Button( emoji=emoji, custom_id=custom_id, style=disnake.ButtonStyle[color] @@ -666,9 +657,7 @@ async def add_button( components.add_item(new_button) try: - await self.reaction_roles.create_message( - message.id, role.id, str(emoji) - ) + await self.reaction_roles.create_message(message.id, role.id, str(emoji)) except ReactionAlreadyExists: return await errorEmb(inter, "This Reaction already exists") @@ -682,9 +671,7 @@ async def add_button( ) @commands.guild_only() @commands.has_permissions(manage_roles=True) - async def edit_message( - self, inter, message_id, channel: disnake.TextChannel - ): + async def edit_message(self, inter, message_id, channel: disnake.TextChannel): exists = False message_id = int(message_id) for message in self.reaction_roles.messages: @@ -708,9 +695,7 @@ def check(m): await inter.send("Please send the new message", ephemeral=True) try: - msg = await self.bot.wait_for( - "message", check=check, timeout=300.0 - ) + msg = await self.bot.wait_for("message", check=check, timeout=300.0) except asyncio.exceptions.TimeoutError: return await errorEmb( inter, "Due to no response the operation was canceled" @@ -735,9 +720,7 @@ def check(m): ) @commands.guild_only() @commands.has_permissions(manage_roles=True) - async def delete_message( - self, inter, message_id, channel: disnake.TextChannel - ): + async def delete_message(self, inter, message_id, channel: disnake.TextChannel): exists = False message_id = int(message_id) for message in self.reaction_roles.messages: @@ -782,15 +765,11 @@ async def remove_button( message_id = int(message_id.strip()) emoji = PartialEmoji.from_str(emoji.strip()) - button = await self.reaction_roles.exists( - message_id, str(emoji), role.id - ) + button = await self.reaction_roles.exists(message_id, str(emoji), role.id) if not button: return await errorEmb(inter, "The button does not exist.") - await self.reaction_roles.remove_message( - message_id, str(emoji), role.id - ) + await self.reaction_roles.remove_message(message_id, str(emoji), role.id) message = await channel.fetch_message(message_id) @@ -826,9 +805,7 @@ async def button_click(self, inter): except ValueError: return - if not await self.reaction_roles.exists( - message.id, str(emoji), role_id - ): + if not await self.reaction_roles.exists(message.id, str(emoji), role_id): return await errorEmb(inter, "This doesnt exists in the database") await self.reaction_roles.increment_roles_given(message.id, str(emoji)) @@ -839,9 +816,7 @@ async def button_click(self, inter): await member.add_roles( role, reason=f"Clicked button to get role. gave {role.name}" ) - await self.reaction_roles.increment_roles_given( - message.id, str(emoji) - ) + await self.reaction_roles.increment_roles_given(message.id, str(emoji)) return await sucEmb(inter, f"Added Role {role.mention}") else: await member.remove_roles( @@ -850,9 +825,7 @@ async def button_click(self, inter): ) return await sucEmb(inter, f"Removed Role {role.mention}") - @commands.slash_command( - name="staffvote", description="Propose a Staff Vote." - ) + @commands.slash_command(name="staffvote", description="Propose a Staff Vote.") @commands.guild_only() @commands.has_permissions(ban_members=True) async def staffvote(self, inter): # todo remove @@ -881,7 +854,9 @@ async def staffhelp(self, inter): await inter.send(embed=emb) - @commands.slash_command(name="anonymous_dm", description="Send an anonymous dm to a user.") + @commands.slash_command( + name="anonymous_dm", description="Send an anonymous dm to a user." + ) @commands.guild_only() @commands.has_permissions(manage_messages=True) async def anonymous_dm(self, inter, user: disnake.Member, *, message: str): @@ -889,5 +864,6 @@ async def anonymous_dm(self, inter, user: disnake.Member, *, message: str): await user.send(message) await sucEmb(inter, "Sent!") + def setup(bot): bot.add_cog(Staff(bot)) diff --git a/utils/CONSTANTS.py b/utils/CONSTANTS.py index 936d43f..3bee08f 100644 --- a/utils/CONSTANTS.py +++ b/utils/CONSTANTS.py @@ -123,6 +123,8 @@ def status(stat): return statuses[stat] +BAN_APPEAL_LINK = "https://forms.gle/jZ569ePNs6rfazA39" + congrats_messages = [ "Happy Birthday", "Today is the Birthday of",