diff --git a/cogs/events/on_button_click.py b/cogs/events/on_button_click.py index ae6926e..ee40303 100644 --- a/cogs/events/on_button_click.py +++ b/cogs/events/on_button_click.py @@ -68,7 +68,7 @@ async def on_button_click(self, interaction: MessageInteraction): interaction.guild.id, interaction.author.id ) return await interaction.respond( - content=f"You already have an existing ticket channel! {(await self.bot.fetch_channel(ticket['id'])).mention}", + content=f"You already have an existing ticket channel! {(self.bot.get_channel(ticket['id']) or await self.bot.fetch_channel(ticket['id'])).mention}", ephemeral=True, ) diff --git a/cogs/events/on_raw_message_edit.py b/cogs/events/on_raw_message_edit.py index 1be2a40..3094c74 100644 --- a/cogs/events/on_raw_message_edit.py +++ b/cogs/events/on_raw_message_edit.py @@ -17,7 +17,9 @@ async def on_raw_message_edit(self, payload: RawMessageUpdateEvent): if not payload.guild_id: return - channel = await self.bot.fetch_channel(payload.channel_id) + channel = self.bot.get_channel( + payload.channel_id + ) or await self.bot.fetch_channel(payload.channel_id) message = await channel.fetch_message(payload.message_id) if message.is_system() or message.author.bot: diff --git a/cogs/events/on_raw_reaction_add.py b/cogs/events/on_raw_reaction_add.py index da44c52..c6c054f 100644 --- a/cogs/events/on_raw_reaction_add.py +++ b/cogs/events/on_raw_reaction_add.py @@ -77,20 +77,20 @@ async def on_raw_reaction_add(self, payload: RawReactionActionEvent): ) ] ) - await self.send_message_to_mods( + await self.bot.utils_class.send_message_to_mods( f"⚠️ - I don't have the right permissions to remove the roles {roles} from the member `{payload.member}`!", payload.guild_id, ) try: return await payload.member.send( - f"⚠️ - I don't have the right permissions to remove the roles {roles} from you so you can't pass the prestige! Please inform a server administrator! (Guild: {await self.bot.fetch_guild(payload.guild_id).name}, ID: {payload.guild_id})" + f"⚠️ - I don't have the right permissions to remove the roles {roles} from you so you can't pass the prestige! Please inform a server administrator! (Guild: {(self.bot.get_guild(payload.guild_id) or await self.bot.fetch_guild(payload.guild_id)).name}, ID: {payload.guild_id})" ) except Forbidden: try: - channel = await self.bot.fetch_channel( + channel = self.bot.get_channel( payload.channel_id - ) + ) or await self.bot.fetch_channel(payload.channel_id) except Forbidden as f: f.text = f"⚠️ - I don't have the right permissions to fetch the channel under the ID {payload.channel_id}!" raise @@ -110,17 +110,19 @@ async def on_raw_reaction_add(self, payload: RawReactionActionEvent): ] ) except Forbidden: - await self.send_message_to_mods( + await self.bot.utils_class.send_message_to_mods( f"⚠️ - I don't have the right permissions to add the role {self.bot.configs[payload.guild_id]['xp']['prestiges'][(int(db_user['prestige']) + 1) or 1]} to the member `{payload.member}`!", payload.guild_id, ) try: return await payload.member.send( - f"⚠️ - I don't have the right permissions to add the role {self.bot.configs[payload.guild_id]['xp']['prestiges'][(int(db_user['prestige']) + 1) or 1]} to you so you can't pass the prestige! Please inform a server administrator! (Guild: {await self.bot.fetch_guild(payload.guild_id).name}, ID: {payload.guild_id})" + f"⚠️ - I don't have the right permissions to add the role {self.bot.configs[payload.guild_id]['xp']['prestiges'][(int(db_user['prestige']) + 1) or 1]} to you so you can't pass the prestige! Please inform a server administrator! (Guild: {(self.bot.get_guild(payload.guild_id) or await self.bot.fetch_guild(payload.guild_id)).name}, ID: {payload.guild_id})" ) except Forbidden: - channel = await self.bot.fetch_channel(payload.channel_id) + channel = self.bot.get_channel( + payload.channel_id + ) or await self.bot.fetch_channel(payload.channel_id) try: return await channel.send( @@ -151,7 +153,8 @@ async def on_raw_reaction_add(self, payload: RawReactionActionEvent): raise finally: message = await ( - await self.bot.fetch_channel(payload.channel_id) + self.bot.get_channel(payload.channel_id) + or await self.bot.fetch_channel(payload.channel_id) ).fetch_message(payload.message_id) await message.remove_reaction("✅", self.bot.user) await message.remove_reaction("❌", self.bot.user) diff --git a/cogs/events/on_ready.py b/cogs/events/on_ready.py index cf9d478..b4c7b3e 100644 --- a/cogs/events/on_ready.py +++ b/cogs/events/on_ready.py @@ -1,7 +1,7 @@ from logging import info from typing import Union -from discord import Activity, ActivityType +from discord import Activity, ActivityType, NotFound from discord.ext.commands import Cog from lavalink import add_event_hook, Client from lavalink.events import NodeConnectedEvent, QueueEndEvent, TrackEndEvent @@ -74,7 +74,14 @@ async def track_hook(self, event: Union[TrackEndEvent, QueueEndEvent]): # it indicates that there are no tracks left in the player's queue. # To save on resources, we can tell the bot to disconnect from the voicechannel. guild_id = int(event.player.guild_id) - guild = await self.bot.fetch_guild(guild_id) + + try: + guild = self.bot.get_guild(guild_id) or await self.bot.fetch_guild( + guild_id + ) + except NotFound: + return + await self.bot.utils_class.clear_playlist(guild) await guild.change_voice_state(channel=None) elif isinstance(event, TrackEndEvent): diff --git a/cogs/moderation/config.py b/cogs/moderation/config.py index c7093ac..bf5bd8e 100644 --- a/cogs/moderation/config.py +++ b/cogs/moderation/config.py @@ -2635,7 +2635,7 @@ async def config_channels_polls_command( ctx.guild.id, poll_msg.id, poll["duration_s"] - - (time() - poll["created_at_ms"]), + - (time() - poll["created_at_s"]), time(), poll["choices"], poll["responses"] if "responses" in poll else None, diff --git a/cogs/moderation/poll.py b/cogs/moderation/poll.py index e1326e4..c2b0f4f 100644 --- a/cogs/moderation/poll.py +++ b/cogs/moderation/poll.py @@ -183,7 +183,7 @@ async def poll_infos_command(self, ctx: Context, id_message: int = None): em.add_field( name="**⏲️ - Time remaining:**", value=self.bot.utils_class.duration( - poll["duration_s"] - (time() - poll["created_at_ms"]) + poll["duration_s"] - (time() - poll["created_at_s"]) ), inline=True, ) @@ -247,7 +247,7 @@ async def poll_infos_command(self, ctx: Context, id_message: int = None): ] em.add_field( name=f'Poll "*{poll_message.embeds[0].title}*":', - value=f"**⏲️ - Time remaining:** {self.bot.utils_class.duration(poll['duration_s'] - (time() - poll['created_at_ms']))}{nl}**🔘 - Number of choices:** {len(poll['choices'])}{nl}**🔢 - Number of answers:** {len(poll['responses']) if 'responses' in poll else 'No answers recorded.'}{nl}**🏆 - Leading choice:** {', '.join(winner_choices) if len(winner_choices) != len(poll['choices']) else 'No leading choice.'}", + value=f"**⏲️ - Time remaining:** {self.bot.utils_class.duration(poll['duration_s'] - (time() - poll['created_at_s']))}{nl}**🔘 - Number of choices:** {len(poll['choices'])}{nl}**🔢 - Number of answers:** {len(poll['responses']) if 'responses' in poll else 'No answers recorded.'}{nl}**🏆 - Leading choice:** {', '.join(winner_choices) if len(winner_choices) != len(poll['choices']) else 'No leading choice.'}", inline=True, ) x += 1 diff --git a/cogs/moderation/sanction.py b/cogs/moderation/sanction.py new file mode 100644 index 0000000..62a827a --- /dev/null +++ b/cogs/moderation/sanction.py @@ -0,0 +1,571 @@ +from discord import Embed, Forbidden, Member +from discord.ext.commands import ( + bot_has_permissions, + bot_has_guild_permissions, + BucketType, + Cog, + Context, + group, + has_guild_permissions, + max_concurrency, +) +from time import time + +from bot import Omnitron +from data import Utils + + +class Moderation(Cog): + def __init__(self, bot: Omnitron): + self.bot = bot + + """ MAIN GROUP """ + + @group( + pass_context=True, + name="sanction", + aliases=["sanctions", "strike", "strikes"], + usage="(sub-command)", + description="This command manage the server's sanctions", + ) + @Utils.check_bot_starting() + @Utils.check_moderator() + @bot_has_permissions(send_messages=True) + async def sanction_group(self, ctx: Context): + if ctx.invoked_subcommand is None: + await ctx.send( + embed=self.bot.utils_class.get_embed_from_ctx( + ctx, title="Server's sanction feature" + ) + ) + + """ MAIN GROUP'S GROUP(S) """ + + @sanction_group.group( + pass_context=True, + name="warn", + aliases=["warns"], + brief="⚠️", + usage="(sub-command)", + description="This option manage the server's warns", + ) + @max_concurrency(1, per=BucketType.guild) + async def sanction_warn_group(self, ctx: Context): + if ctx.invoked_subcommand is None: + await ctx.send( + embed=self.bot.utils_class.get_embed_from_ctx( + ctx, title="Server's warns feature" + ) + ) + + @sanction_group.group( + pass_context=True, + name="mute", + aliases=["mutes"], + brief="🔕️", + usage="(sub-command)", + description="This option manage the server's mutes", + ) + @max_concurrency(1, per=BucketType.guild) + async def sanction_mute_group(self, ctx: Context): + if ctx.invoked_subcommand is None: + await ctx.send( + embed=self.bot.utils_class.get_embed_from_ctx( + ctx, title="Server's mute feature" + ) + ) + + """ MAIN GROUP'S COMMAND(S) """ + + @sanction_group.group( + pass_context=True, + name="kick", + brief="⚡", + usage='@member ("reason")', + description="Kick a member from the server with a reason attached if specified", + ) + @has_guild_permissions(kick_members=True) + @bot_has_guild_permissions(kick_members=True) + @max_concurrency(1, per=BucketType.member) + async def sanction_kick_command( + self, ctx: Context, member: Member, *, reason: str = None + ): + em = Embed( + colour=self.bot.color, + title=f"🚫 - Kick", + description=f"The member {member} has been kicked by {ctx.author.mention}", + ) + + em.set_thumbnail(url=ctx.guild.icon_url) + em.set_author(name=ctx.author.display_name, icon_url=member.avatar_url) + em.set_footer(text=self.bot.user.name, icon_url=self.bot.user.avatar_url) + + if reason: + em.add_field(name="raison:", value=reason, inline=False) + + try: + await member.kick( + reason=f"The member {member} has been kicked by {ctx.author} {f'for the reason: {reason}' if reason else ''}" + ) + except Forbidden: + return await ctx.reply( + f"⛔ - {ctx.author.mention} - I can't kick the member `{member}`!", + delete_after=20, + ) + + await ctx.send(embed=em) + + @sanction_group.group( + pass_context=True, + name="ban", + brief="🔨", + usage='@member ("reason") ( )', + description="Ban a member for a certain duration with a reason attached if specified! (default/minimum duration = 1 day) (duration format -> ", + ) + @has_guild_permissions(ban_members=True) + @bot_has_guild_permissions(ban_members=True) + @max_concurrency(1, per=BucketType.member) + async def sanction_ban_command(self, ctx: Context, member: Member, *args: str): + reason = None + _duration = "1" + type_duration = "d" + + if args and not "".join(args[0][:-1]).isdigit(): + reason = args[0] + + if reason: + if len(args) > 2: + _duration, type_duration = (*args[1::],) + elif len(args) > 1: + _duration = args[1][0:-1] + type_duration = args[1][-1] + elif args: + if len(args) > 1: + _duration, type_duration = (*args[0::],) + else: + _duration = args[0][0:-1] + type_duration = args[0][-1] + + if not _duration.isdigit(): + try: + await ctx.reply( + f"⚠️ - {ctx.author.mention} - Please provide a valid duration! `{self.bot.utils_class.get_guild_pre(ctx.message)[0]}{f'{ctx.command.parents[0]}' if ctx.command.parents else f'help {ctx.command.qualified_name}'}` to get more help.", + delete_after=15, + ) + except Forbidden as f: + f.text = f"⚠️ - I don't have the right permissions to send messages in the channel {ctx.channel.mention} (message: `⚠️ - {ctx.author.mention} - Please provide a valid duration! `{self.get_guild_pre(ctx.message)[0]}{f'{ctx.command.parents[0]}' if ctx.command.parents else f'help {ctx.command.qualified_name}'}` to get more help.`)!" + raise + return + + duration_s = await self.bot.utils_class.parse_duration( + int(_duration), type_duration, ctx + ) + if not duration_s: + return + + em = Embed( + colour=self.bot.color, + title=f"🚫 - Ban", + description=f"The member {member} has been banned by {ctx.author.mention}", + ) + + em.set_thumbnail(url=ctx.guild.icon_url) + em.set_author(name=ctx.author.display_name, icon_url=member.avatar_url) + em.set_footer(text=self.bot.user.name, icon_url=self.bot.user.avatar_url) + + if reason: + em.add_field(name="raison:", value=reason, inline=False) + + try: + await member.ban( + reason=f"The member {member} has been banned by {ctx.author}" + + ( + f" for {self.bot.utils_class.duration(duration_s)}" + if duration_s + else "" + ) + + (f" for the reason: {reason}'" if reason else "") + ) + self.bot.user_repo.ban_user( + ctx.guild.id, + member.id, + duration_s, + time(), + ctx.author.display_name, + reason, + ) + + if duration_s: + em.description += f" for {self.bot.utils_class.duration(duration_s)}" + self.bot.utils_class.task_launcher( + self.bot.utils_class.ban_completion, + ( + self.bot.user_repo.get_user(ctx.guild.id, member.id), + ctx.guild.id, + ), + count=1, + ) + except Forbidden: + return await ctx.reply( + f"⛔ - {ctx.author.mention} - I can't ban the member `{member}`!", + delete_after=20, + ) + + await ctx.send(embed=em) + + """ MAIN GROUP'S WARN COMMAND(S) """ + + @sanction_warn_group.command( + name="add", + brief="⚠️", + usage='@member ("reason")', + description="Warn a member with a reason attached if specified", + ) + @max_concurrency(1, per=BucketType.member) + async def sanction_warn_add_command( + self, ctx: Context, member: Member, *, reason: str = None + ): + if "muted_role" not in self.bot.configs[ctx.guild.id]: + return await ctx.reply( + f"⚠️ - {ctx.author.mention} - The server doesn't have a muted role yet! Please configure one with the command `{self.bot.utils_class.get_guild_pre(ctx.message)[0]}config muted_role` to set one!", + delete_after=20, + ) + + em = Embed( + colour=self.bot.color, + title=f"🚫 - Warn", + description=f"The user `{member}` has been warned by {ctx.author.mention}", + ) + + em.set_thumbnail(url=ctx.guild.icon_url) + em.set_author(name=ctx.author.display_name, icon_url=member.avatar_url) + em.set_footer(text=self.bot.user.name, icon_url=self.bot.user.avatar_url) + + if reason: + em.add_field(name="reason:", value=reason, inline=False) + + self.bot.user_repo.warn_user( + ctx.guild.id, member.id, time(), ctx.author.display_name, reason + ) + warns = len(self.bot.user_repo.get_warns(ctx.guild.id, member.id)) + em.add_field( + name=f"**Number of warnings of {member.display_name}:**", + value=f"{warns}", + inline=False, + ) + + if warns == 2 or warns == 4: + if ctx.guild.me.permissions_in(ctx.channel).manage_roles: + em.add_field( + name="sanction", + value=f"🔇 - Muted {'3H' if warns == 2 else '24H'} - 🔇", + inline=False, + ) + + try: + await member.add_roles(self.bot.configs[ctx.guild.id]["muted_role"]) + except Forbidden as f: + f.text = f"⚠️ - I don't have the right permissions to add the role `{self.bot.configs[ctx.guild.id]['muted_role']}` to {member} (maybe the role is above mine)" + raise + + self.bot.user_repo.mute_user( + ctx.guild.id, + member.id, + 10800 if warns == 2 else 86400, + time(), + self.bot.user.display_name, + f"{'2nd' if warns == 2 else '4th'} warn", + ) + self.bot.tasks[ctx.guild.id]["mute_completions"][ + member.id + ] = self.bot.utils_class.task_launcher( + self.bot.utils_class.mute_completion, + ( + self.bot.user_repo.get_user(member.guild.id, member.id), + member.guild.id, + ), + count=1, + ) + else: + await self.bot.utils_class.send_message_to_mods( + f"⚠️ - I don't have the right permissions to manage roles in this server (i tried to add the muted role to {member} after his {'2nd' if warns == 2 else '4th'} warn)!", + ctx.guild.id, + ) + elif warns == 5: + em.add_field(name="sanction", value="⚠️ - Warning - ⚠", inline=False) + + try: + await member.send( + f"⚠ - ️️{member.mention} You are on your 5th warn! The next time you're warn, you will be kicked from this server {ctx.guild}! - ⚠️" + ) + except Forbidden: + await ctx.send( + f"❌ - ️️{ctx.author.mention} - Couldn't send the message to {member}, please inform him that on the next warn he will be kicked from the server!" + ) + elif warns > 5: + em.add_field(name="sanction", value="🚫 - kick - 🚫", inline=False) + + if ctx.guild.me.permissions_in(ctx.channel).kick_members: + await member.kick(reason="6th warn") + else: + await ctx.send( + f"❌ - {ctx.author.mention} - I don't have the permission to kick members! (try kicking him yourself then!)" + ) + + await ctx.send(embed=em) + + @sanction_warn_group.command( + name="list", + brief="ℹ️", + usage="(@member)", + description="Show the list of a member's warns or your warns!", + ) + async def sanction_warn_list_command(self, ctx: Context, member: Member = None): + if not member: + member = ctx.author + + em = Embed( + colour=self.bot.color, title=f"⚠️ - list of previous warns from {member}" + ) + + em.set_thumbnail(url=ctx.guild.icon_url) + em.set_author(name=ctx.author.display_name, icon_url=member.avatar_url) + em.set_footer(text=self.bot.user.name, icon_url=self.bot.user.avatar_url) + + warns = self.bot.user_repo.get_warns(ctx.guild.id, member.id) + + if not warns: + return await ctx.reply( + f"ℹ️ - {ctx.author.mention} - {f'The member {member}' if member != ctx.author else 'You'} has never been warned." + ) + + x = 0 + nl = "\n" + while x < len(warns) and x <= 24: + if x == 24: + em.add_field( + name="**Too many warns to display them all**", + value="...", + inline=False, + ) + else: + em.add_field( + name=f"**{x + 1}:**", + value=f"**date :** {warns[x]['at']}{nl}**by :** {warns[x]['by']}{nl}**reason :** {warns[x]['reason'] if 'reason' in warns[x] else 'no reason specified'}", + inline=True, + ) + x += 1 + + await ctx.send(embed=em) + + """ MAIN GROUP'S MUTE COMMAND(S) """ + + @sanction_mute_group.command( + name="add", + brief="🔇", + usage='@member ("reason") ( )', + description="Mute a member for a certain duration with a reason attached if specified! (default/minimum duration = 10 min) (duration format -> ", + ) + @bot_has_permissions(manage_roles=True) + @max_concurrency(1, per=BucketType.member) + async def sanction_mute_add_command(self, ctx: Context, member: Member, *args: str): + if "muted_role" not in self.bot.configs[ctx.guild.id]: + return await ctx.reply( + f"⚠️ - {ctx.author.mention} - The server doesn't have a muted role yet! Please configure one with the command `{self.bot.utils_class.get_guild_pre(ctx.message)[0]}config muted_role` to set one!", + delete_after=20, + ) + + reason = None + _duration = "10" + type_duration = "m" + + if args and not "".join(args[0][:-1]).isdigit(): + reason = args[0] + + if reason: + if len(args) > 2: + _duration, type_duration = (*args[1::],) + elif len(args) > 1: + _duration = args[1][0:-1] + type_duration = args[1][-1] + elif args: + if len(args) > 1: + _duration, type_duration = (*args[0::],) + else: + _duration = args[0][0:-1] + type_duration = args[0][-1] + + if not _duration.isdigit(): + try: + await ctx.reply( + f"⚠️ - {ctx.author.mention} - Please provide a valid duration! `{self.bot.utils_class.get_guild_pre(ctx.message)[0]}{f'{ctx.command.parents[0]}' if ctx.command.parents else f'help {ctx.command.qualified_name}'}` to get more help.", + delete_after=15, + ) + except Forbidden as f: + f.text = f"⚠️ - I don't have the right permissions to send messages in the channel {ctx.channel.mention} (message: `⚠️ - {ctx.author.mention} - Please provide a valid duration! `{self.get_guild_pre(ctx.message)[0]}{f'{ctx.command.parents[0]}' if ctx.command.parents else f'help {ctx.command.qualified_name}'}` to get more help.`)!" + raise + return + + duration_s = await self.bot.utils_class.parse_duration( + int(_duration), type_duration, ctx + ) + if not duration_s: + return + + em = Embed( + colour=self.bot.color, + title=f"🔇 - Mute", + description=f"The member `{member}` has been muted by {ctx.author.mention}", + ) + + em.set_thumbnail(url=ctx.guild.icon_url) + em.set_author(name=ctx.author.display_name, icon_url=member.avatar_url) + em.set_footer(text=self.bot.user.name, icon_url=self.bot.user.avatar_url) + + if reason: + em.add_field(name="reason:", value=reason, inline=False) + + db_user = self.bot.user_repo.get_user(ctx.guild.id, member.id) + + if ( + self.bot.configs[ctx.guild.id]["muted_role"] not in member.roles + or not db_user["muted"] + ): + em.description = f"The member `{member}` has been muted by {ctx.author.mention} for {self.bot.utils_class.duration(duration_s)}" + await member.add_roles( + self.bot.configs[ctx.guild.id]["muted_role"], + reason="Muted from command sanction.", + ) + self.bot.user_repo.mute_user( + ctx.guild.id, + member.id, + duration_s, + time(), + ctx.author.display_name, + reason, + ) + self.bot.tasks[ctx.guild.id]["mute_completions"][ + member.id + ] = self.bot.utils_class.task_launcher( + self.bot.utils_class.mute_completion, + ( + self.bot.user_repo.get_user(ctx.guild.id, member.id), + ctx.guild.id, + ), + count=1, + ) + else: + last_mute = self.bot.user_repo.get_last_mute(ctx.guild.id, member.id) + em.description = f"The member {member} is already muted" + + em.remove_field(0) + + em.add_field(name="**muted by:**", value=last_mute["by"], inline=True) + em.add_field(name="**date:**", value=last_mute["at"], inline=True) + em.add_field(name="**duration:**", value=last_mute["duration"], inline=True) + em.add_field( + name="**time remaining:**", + value=self.bot.utils_class.duration( + last_mute["duration_s"] - (time() - last_mute["at_s"]) + ), + inline=True, + ) + + if "reason" in last_mute: + em.add_field(name="**reason:**", value=last_mute["reason"], inline=True) + + await ctx.send(embed=em) + + @sanction_mute_group.command( + name="list", + brief="ℹ", + usage="(@member)", + description="Show the list of a member's mutes or your mutes!", + ) + async def sanction_mute_list_command( + self, + ctx: Context, + member: Member = None, + ): + if not member: + member = ctx.author + + em = Embed( + colour=self.bot.color, + title=f"🔇 - List of previous mutes of {member}", + ) + + em.set_thumbnail(url=ctx.guild.icon_url) + em.set_author(name=ctx.author.display_name, icon_url=member.avatar_url) + em.set_footer(text=self.bot.user.name, icon_url=self.bot.user.avatar_url) + + db_user = self.bot.user_repo.get_user(ctx.guild.id, member.id) + + if "mutes" not in db_user or len(db_user["mutes"]) < 1: + return await ctx.reply( + f"ℹ️ - {ctx.author.mention} - {f'The member {member}' if member != ctx.author else 'You'} has never been muted." + ) + + x = 0 + nl = "\n" + while x < len(db_user["mutes"]) and x <= 24: + if x == 24: + em.add_field( + name="**Too many mutes to display them all**.", + value="...", + inline=False, + ) + else: + em.add_field( + name=f"**{x + 1}:**", + value=f"**date :** {db_user['mutes'][x]['at']}{nl}**by :** {db_user['mutes'][x]['by']}{nl}**duration :** {db_user['mutes'][x]['duration']}{nl}**reason :** {db_user['mutes'][x]['reason'] if 'reason' in db_user['mutes'][x] else 'no reason specified'}", + inline=True, + ) + x += 1 + + await ctx.send(embed=em) + + @sanction_mute_group.command( + name="remove", + brief="🔉", + usage='@member ("reason")', + description="Unmute a member with a reason attached if specified!", + ) + @bot_has_permissions(manage_roles=True) + @max_concurrency(1, per=BucketType.member) + async def sanction_mute_remove_command( + self, + ctx: Context, + member: Member, + *, + reason: str = None, + ): + db_user = self.bot.user_repo.get_user(ctx.guild.id, member.id) + + if ( + self.bot.configs[ctx.guild.id]["muted_role"] in member.roles + or db_user["muted"] + ): + await member.remove_roles( + self.bot.configs[ctx.guild.id]["muted_role"], reason=reason + ) + self.bot.user_repo.unmute_user(ctx.guild.id, member.id) + + if ( + "reason" in db_user["mutes"][-1] + and db_user["mutes"][-1]["reason"] != "joined the server" + ): + self.bot.tasks[ctx.guild.id]["mute_completions"][member.id].cancel() + del self.bot.tasks[ctx.guild.id]["mute_completions"][member.id] + + await ctx.send( + f"🔊 - The member {member} has been unmuted by {ctx.author.mention}." + ) + else: + await ctx.send( + f"🔊 - {ctx.author.mention} - The member {member} is not or no longer muted." + ) + + +def setup(bot): + bot.add_cog(Moderation(bot)) diff --git a/data/Database/poll.py b/data/Database/poll.py index 172f1a7..27a2c39 100644 --- a/data/Database/poll.py +++ b/data/Database/poll.py @@ -30,7 +30,7 @@ def create_poll( "duration": Utils.duration(_duration), "duration_s": _duration, "created_at": datetime.fromtimestamp(at).strftime("%d/%m/%Y, %H:%M:%S"), - "created_at_ms": at, + "created_at_s": at, "choices": { choice.label: 0 for choice in list(chain.from_iterable(choices)) } @@ -59,7 +59,7 @@ def delete_poll(self, guild_id: int, _id: int) -> bool: "duration": poll["duration"], "duration_s": poll["duration_s"], "created_at": poll["created_at"], - "created_at_ms": poll["created_at_ms"], + "created_at_s": poll["created_at_s"], "choices": poll["choices"], "responses": poll["responses"] if "responses" in poll else None, }, diff --git a/data/Database/user.py b/data/Database/user.py index bdcda62..71d093a 100644 --- a/data/Database/user.py +++ b/data/Database/user.py @@ -141,7 +141,7 @@ def warn_user( f"{path}/{x}", args={ "at": datetime.fromtimestamp(at).strftime("%d/%m/%Y, %H:%M:%S"), - "at_ms": at, + "at_s": at, "by": by, "reason": reason, }, @@ -177,7 +177,7 @@ def mute_user( "duration": self.bot.utils_class.duration(_duration), "duration_s": _duration, "at": datetime.fromtimestamp(at).strftime("%d/%m/%Y, %H:%M:%S"), - "at_ms": at, + "at_s": at, "by": by, "reason": reason, }, @@ -220,18 +220,16 @@ def ban_user( self, guild_id: int, _id: int, _duration, at: float, by: str, reason: str = None ) -> None: self.model.create( - f"logs/ban/{_id}", + f"{self.path}/{_id}/ban", args={ - "id": _id, "duration": self.bot.utils_class.duration(_duration) if isinstance(_duration, int) else "all Eternity", "duration_s": _duration if isinstance(_duration, int) else "infinite", "at": datetime.fromtimestamp(at).strftime("%d/%m/%Y, %H:%M:%S"), - "at_ms": at, + "at_s": at, "by": by, "reason": reason, - "still": True, }, ) @@ -240,17 +238,17 @@ def ban_user( def unban_user( self, guild_id: int, _id: int, at: float, by: str, reason: str = None ) -> None: - self.model.update(f"logs/ban/{_id}", args={"still": False}) self.model.create( - f"logs/unban/{_id}", + f"{self.path}/{_id}/unban/{int(at)}", args={ - "id": _id, "at": datetime.fromtimestamp(at).strftime("%d/%m/%Y, %H:%M:%S"), - "at_ms": at, + "at_s": at, "by": by, "reason": reason, + "original_ban": self.model.get(f"{self.path}/{_id}/ban"), }, ) + self.model.delete(f"{self.path}/{_id}/ban") @Utils.resolve_guild_path @__check_user_exists @@ -261,7 +259,7 @@ def kick_user( f"logs/kick/{_id}", args={ "at": datetime.fromtimestamp(at).strftime("%d/%m/%Y, %H:%M:%S"), - "at_ms": at, + "at_s": at, "by": by, "reason": reason, }, @@ -378,7 +376,7 @@ def new_invite(self, guild_id: int, _id: int, at: float, content: str) -> None: f"{self.path}/{_id}/invit_links/{ceil(at)}", args={ "at": datetime.fromtimestamp(at).strftime("%d/%m/%Y, %H:%M:%S"), - "at_ms": at, + "at_s": at, "content": content, }, ) diff --git a/data/utils.py b/data/utils.py index 9a6038c..532c5cd 100644 --- a/data/utils.py +++ b/data/utils.py @@ -94,10 +94,23 @@ async def mute_completion(self, db_user: OrderedDict, guild_id: int): and mute["reason"] == "joined the server" and db_user["id"] in self.bot.tasks[guild_id]["mute_completions"] ): + self.bot.user_repo.unmute_user(guild_id, db_user["id"]) self.bot.user_repo.clear_join_mutes(guild_id, db_user["id"]) + + if "muted_role" in self.bot.configs[guild_id]: + try: + await ( + await ( + self.bot.get_guild(guild_id) + or await self.bot.fetch_guild(guild_id) + ).try_member(int(db_user["id"])) + ).remove_roles(self.bot.configs[guild_id]["muted_role"]) + except NotFound: + pass + return - timeout = mute["duration_s"] - (time() - mute["at_ms"]) + timeout = mute["duration_s"] - (time() - mute["at_s"]) if timeout <= 0: timeout = 2 @@ -106,7 +119,7 @@ async def mute_completion(self, db_user: OrderedDict, guild_id: int): self.bot.user_repo.unmute_user(guild_id, db_user["id"]) try: - guild = await self.bot.fetch_guild(guild_id) + guild = self.bot.get_guild(guild_id) or await self.bot.fetch_guild(guild_id) member = await guild.try_member(int(db_user["id"])) except NotFound: member = db_user["name"] @@ -154,9 +167,49 @@ async def mute_completion(self, db_user: OrderedDict, guild_id: int): if db_user["id"] in self.bot.tasks[guild_id]["mute_completions"]: del self.bot.tasks[guild_id]["mute_completions"][db_user["id"]] + async def ban_completion(self, banned_user: OrderedDict, guild_id: int): + """This method manage the ban completion""" + await self.bot.wait_until_ready() + timeout = banned_user["ban"]["duration_s"] - ( + time() - banned_user["ban"]["at_s"] + ) + + if timeout <= 0: + timeout = 2 + + await sleep(timeout) + + guild = self.bot.get_guild(guild_id) or await self.bot.fetch_guild(guild_id) + bans = await guild.bans() + user = None + + for ban in bans: + if ban.user.id == banned_user["id"]: + user = ban.user + + reason = f"Unbanned automatically after {self.duration(banned_user['ban']['duration_s'])}" + + self.bot.user_repo.unban_user( + guild_id, banned_user["id"], time(), f"{self.bot.user}", reason + ) + + if user: + try: + await guild.unban(user, reason=reason) + except Forbidden: + return await self.send_message_to_mods( + f"⚠️ - I don't have the right permissions to unban the user {f'{user}' if user else 'with the id: ' + banned_user['id']}! ({reason})", + guild_id, + ) + + await self.send_message_to_mods( + f"🚫 - The user {f'{user}' if user else 'with the id: ' + banned_user['id']} is no longer banned from the server.", + guild_id, + ) + async def send_message_to_mods(self, message: str, guild_id: int): """This method send a message to all mods""" - guild = await self.bot.fetch_guild(guild_id) + guild = self.bot.get_guild(guild_id) or await self.bot.fetch_guild(guild_id) if "mods_channel" in self.bot.configs[guild_id]: try: @@ -265,14 +318,14 @@ async def parse_duration( elif ( ctx.command.parents[0].name if ctx.command.parents else "" ) == "poll" and ( - _duration <= 600 + _duration < 600 and type_duration == "s" - or _duration <= 10 + or _duration < 10 and type_duration == "m" ): try: await ctx.reply( - f"⚠️ - {ctx.author.mention} - Please provide a minimum duration greater than 10 minutes to create a poll! `{self.get_guild_pre(ctx.message)[0]}{f'{ctx.command.parents[0]}' if ctx.command.parents else f'help {ctx.command.qualified_name}'}` to get more help.", + f"⚠️ - {ctx.author.mention} - Please provide a minimum duration greater or equalt to 10 minutes to create a poll! `{self.get_guild_pre(ctx.message)[0]}{f'{ctx.command.parents[0]}' if ctx.command.parents else f'help {ctx.command.qualified_name}'}` to get more help.", delete_after=15, ) except Forbidden as f: @@ -280,6 +333,42 @@ async def parse_duration( raise return False + elif ctx.command.qualified_name == "sanction mute add" and ( + _duration < 600 + and type_duration == "s" + or _duration < 10 + and type_duration == "m" + ): + try: + await ctx.reply( + f"⚠️ - {ctx.author.mention} - Please provide a minimum duration greater or equal to 10 minutes to mute a member! `{self.get_guild_pre(ctx.message)[0]}{f'{ctx.command.parents[0]}' if ctx.command.parents else f'help {ctx.command.qualified_name}'}` to get more help.", + delete_after=15, + ) + except Forbidden as f: + f.text = f"⚠️ - I don't have the right permissions to send messages in the channel {ctx.channel.mention} (message: `⚠️ - {ctx.author.mention} - Please provide a minimum duration greater than 10 minutes to create a poll! `{self.get_guild_pre(ctx.message)[0]}{f'{ctx.command.parents[0]}' if ctx.command.parents else f'help {ctx.command.qualified_name}'}` to get more help.`)!" + raise + + return False + # elif ctx.command.qualified_name == "sanction ban" and ( + # _duration < 86400 + # and type_duration == "s" + # or _duration < 1440 + # and type_duration == "m" + # or _duration < 24 + # and type_duration == "h" + # or _duration < 1 + # and type_duration == "d" + # ): + # try: + # await ctx.reply( + # f"⚠️ - {ctx.author.mention} - Please provide a minimum duration greater or equal to 1 day to ban a member! `{self.get_guild_pre(ctx.message)[0]}{f'{ctx.command.parents[0]}' if ctx.command.parents else f'help {ctx.command.qualified_name}'}` to get more help.", + # delete_after=15, + # ) + # except Forbidden as f: + # f.text = f"⚠️ - I don't have the right permissions to send messages in the channel {ctx.channel.mention} (message: `⚠️ - {ctx.author.mention} - Please provide a minimum duration greater than 10 minutes to create a poll! `{self.get_guild_pre(ctx.message)[0]}{f'{ctx.command.parents[0]}' if ctx.command.parents else f'help {ctx.command.qualified_name}'}` to get more help.`)!" + # raise + # + # return False if type_duration == "s": return _duration * 1 @@ -335,7 +424,7 @@ async def poll_completion(self, *args): poll = args[1] if len(args) < 3 or len(args) > 2 and not args[2]: - timeout = poll["duration_s"] - (time() - poll["created_at_ms"]) + timeout = poll["duration_s"] - (time() - poll["created_at_s"]) if timeout <= 0: timeout = 2 @@ -496,6 +585,16 @@ def init_guild(self, guild: Guild): else: bot.user_repo.unmute_user(guild.id, db_user["id"]) + if "ban" in db_user: + self.task_launcher( + self.ban_completion, + ( + db_user, + guild.id, + ), + count=1, + ) + """ CONFIG """ bot.configs[guild.id] = {"prefix": bot.config_repo.get_prefix(guild.id)}