# pylint: disable=cyclic-import import json from datetime import timedelta as td from typing import Union from discord import Guild, Interaction, Member, User from discord.errors import Forbidden, NotFound from redbot.core import commands from .config import config def check_permissions( user: User, permissions: list, ctx: Union[commands.Context, Interaction] = None, guild: Guild = None, ): """Checks if a user has a specific permission (or a list of permissions) in a channel.""" if ctx: member = ctx.guild.get_member(user.id) resolved_permissions = ctx.channel.permissions_for(member) elif guild: member = guild.get_member(user.id) resolved_permissions = member.guild_permissions else: raise (KeyError) for permission in permissions: if ( not getattr(resolved_permissions, permission, False) and not resolved_permissions.administrator is True ): return permission return False async def check_moddable( target: Union[User, Member], interaction: Interaction, permissions: list ): """Checks if a moderator can moderate a target.""" if check_permissions(interaction.client.user, permissions, guild=interaction.guild): await interaction.response.send_message( f"I do not have the `{permissions}` permission, required for this action.", ephemeral=True, ) return False if await config.guild(interaction.guild).use_discord_permissions() is True: if check_permissions(interaction.user, permissions, guild=interaction.guild): await interaction.response.send_message( f"You do not have the `{permissions}` permission, required for this action.", ephemeral=True, ) return False if interaction.user.id == target.id: await interaction.response.send_message( content="You cannot moderate yourself!", ephemeral=True ) return False if target.bot: await interaction.response.send_message( content="You cannot moderate bots!", ephemeral=True ) return False if isinstance(target, Member): if interaction.user.top_role <= target.top_role: await interaction.response.send_message( content="You cannot moderate members with a higher role than you!", ephemeral=True, ) return False if ( interaction.guild.get_member(interaction.client.user.id).top_role <= target.top_role ): await interaction.response.send_message( content="You cannot moderate members with a role higher than the bot!", ephemeral=True, ) return False immune_roles = await config.guild(target.guild).immune_roles() for role in target.roles: if role.id in immune_roles: await interaction.response.send_message( content="You cannot moderate members with an immune role!", ephemeral=True, ) return False return True async def get_next_case_number(guild_id: str, cursor=None): """This function returns the next case number from the MySQL table for a specific guild.""" from .database import connect if not cursor: database = connect() cursor = database.cursor() cursor.execute( f"SELECT moderation_id FROM `moderation_{guild_id}` ORDER BY moderation_id DESC LIMIT 1" ) return cursor.fetchone()[0] + 1 def generate_dict(result): case: dict = { "moderation_id": result[0], "timestamp": result[1], "moderation_type": result[2], "target_type": result[3], "target_id": result[4], "moderator_id": result[5], "role_id": result[6], "duration": result[7], "end_timestamp": result[8], "reason": result[9], "resolved": result[10], "resolved_by": result[11], "resolve_reason": result[12], "expired": result[13], "changes": json.loads(result[14]), "metadata": json.loads(result[15]) } return case async def fetch_user_dict(interaction: Interaction, user_id: str): """This function returns a dictionary containing either user information or a standard deleted user template.""" if user_id == "?": user_dict = {"id": "?", "name": "Unknown User", "discriminator": "0"} else: try: user = interaction.client.get_user(user_id) if user is None: user = await interaction.client.fetch_user(user_id) user_dict = { "id": user.id, "name": user.name, "discriminator": user.discriminator, } except NotFound: user_dict = { "id": user_id, "name": "Deleted User", "discriminator": "0", } return user_dict async def fetch_channel_dict(interaction: Interaction, channel_id: str): """This function returns a dictionary containing either channel information or a standard deleted channel template.""" try: channel = interaction.guild.get_channel(channel_id) if not channel: channel = await interaction.guild.fetch_channel(channel_id) channel_dict = {"id": channel.id, "name": channel.name, "mention": channel.mention} except NotFound: channel_dict = {"id": channel_id, "name": "Deleted Channel", "mention": None} return channel_dict async def fetch_role_dict(interaction: Interaction, role_id: str): """This function returns a dictionary containing either role information or a standard deleted role template.""" role = interaction.guild.get_role(role_id) if not role: role_dict = {"id": role_id, "name": "Deleted Role"} role_dict = {"id": role.id, "name": role.name} return role_dict async def log(interaction: Interaction, moderation_id: int, resolved: bool = False): """This function sends a message to the guild's configured logging channel when an infraction takes place.""" from .database import fetch_case from .embed_factory import embed_factory logging_channel_id = await config.guild(interaction.guild).log_channel() if logging_channel_id != " ": logging_channel = interaction.guild.get_channel(logging_channel_id) case = await fetch_case(moderation_id, interaction.guild.id) if case: embed = await embed_factory( "log", await interaction.client.get_embed_color(None), interaction=interaction, case_dict=case, resolved=resolved ) try: await logging_channel.send(embed=embed) except Forbidden: return async def send_evidenceformat(interaction: Interaction, case_dict: dict): """This function sends an ephemeral message to the moderator who took the moderation action, with a pre-made codeblock for use in the mod-evidence channel.""" from .embed_factory import embed_factory send_evidence_bool = (await config.user(interaction.user).auto_evidenceformat() or await config.guild(interaction.guild).auto_evidenceformat() or False) if send_evidence_bool is False: return content = await embed_factory( "evidenceformat", await interaction.client.get_embed_color(None), interaction=interaction, case_dict=case_dict ) await interaction.followup.send(content=content, ephemeral=True) def convert_timedelta_to_str(timedelta: td): """This function converts a timedelta object to a string.""" total_seconds = int(timedelta.total_seconds()) hours = total_seconds // 3600 minutes = (total_seconds % 3600) // 60 seconds = total_seconds % 60 return f"{hours}:{minutes}:{seconds}"