2023-12-17 03:11:51 -05:00
# pylint: disable=cyclic-import
2023-12-17 02:16:44 -05:00
import json
2023-12-30 04:10:25 -05:00
from datetime import timedelta as td
2023-12-17 02:16:44 -05:00
from typing import Union
2023-12-18 17:24:40 -05:00
from discord import Guild , Interaction , Member , User
from discord . errors import Forbidden , NotFound
2023-12-17 02:16:44 -05:00
from redbot . core import commands
2024-01-05 04:21:05 -05:00
from redbot . core . utils . chat_formatting import error
2023-12-18 17:24:40 -05:00
2023-12-17 02:16:44 -05:00
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 (
2024-01-05 04:21:05 -05:00
error ( f " I do not have the ` { permissions } ` permission, required for this action. " ) ,
2023-12-17 02:16:44 -05:00
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 (
2024-01-05 04:21:05 -05:00
error ( f " You do not have the ` { permissions } ` permission, required for this action. " ) ,
2023-12-17 02:16:44 -05:00
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 (
2024-01-05 04:21:05 -05:00
content = error ( " You cannot moderate members with a higher role than you! " ) ,
2023-12-17 02:16:44 -05:00
ephemeral = True ,
)
return False
if (
interaction . guild . get_member ( interaction . client . user . id ) . top_role
< = target . top_role
) :
await interaction . response . send_message (
2024-01-05 04:21:05 -05:00
content = error ( " You cannot moderate members with a role higher than the bot! " ) ,
2023-12-17 02:16:44 -05:00
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 (
2024-01-05 04:21:05 -05:00
content = error ( " You cannot moderate members with an immune role! " ) ,
2023-12-17 02:16:44 -05:00
ephemeral = True ,
)
return False
return True
async def get_next_case_number ( guild_id : str , cursor = None ) :
2023-12-18 18:33:37 -05:00
""" This function returns the next case number from the MySQL table for a specific guild. """
2023-12-17 03:11:51 -05:00
from . database import connect
2023-12-17 02:16:44 -05:00
if not cursor :
2023-12-28 04:23:55 -05:00
database = connect ( )
2023-12-17 02:16:44 -05:00
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 ] ,
2023-12-17 12:54:41 -05:00
" 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 ] )
2023-12-17 02:16:44 -05:00
}
return case
async def fetch_user_dict ( interaction : Interaction , user_id : str ) :
2023-12-18 18:33:37 -05:00
""" This function returns a dictionary containing either user information or a standard deleted user template. """
2023-12-17 02:16:44 -05:00
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
2023-12-17 12:54:41 -05:00
async def fetch_channel_dict ( interaction : Interaction , channel_id : str ) :
2023-12-18 18:33:37 -05:00
""" This function returns a dictionary containing either channel information or a standard deleted channel template. """
2023-12-17 02:16:44 -05:00
try :
2023-12-17 12:54:41 -05:00
channel = interaction . guild . get_channel ( channel_id )
if not channel :
channel = await interaction . guild . fetch_channel ( channel_id )
2023-12-17 02:16:44 -05:00
2023-12-17 12:54:41 -05:00
channel_dict = { " id " : channel . id , " name " : channel . name , " mention " : channel . mention }
2023-12-17 02:16:44 -05:00
except NotFound :
2023-12-17 12:54:41 -05:00
channel_dict = { " id " : channel_id , " name " : " Deleted Channel " , " mention " : None }
return channel_dict
async def fetch_role_dict ( interaction : Interaction , role_id : str ) :
2023-12-18 18:33:37 -05:00
""" This function returns a dictionary containing either role information or a standard deleted role template. """
2023-12-17 12:54:41 -05:00
role = interaction . guild . get_role ( role_id )
if not role :
2023-12-17 02:16:44 -05:00
role_dict = { " id " : role_id , " name " : " Deleted Role " }
2023-12-17 12:54:41 -05:00
role_dict = { " id " : role . id , " name " : role . name }
2023-12-17 02:16:44 -05:00
return role_dict
2023-12-17 03:06:09 -05:00
async def log ( interaction : Interaction , moderation_id : int , resolved : bool = False ) :
2023-12-18 18:33:37 -05:00
""" This function sends a message to the guild ' s configured logging channel when an infraction takes place. """
2023-12-17 03:11:51 -05:00
from . database import fetch_case
2023-12-18 17:24:40 -05:00
from . embed_factory import embed_factory
2023-12-17 02:36:18 -05:00
2023-12-17 02:16:44 -05:00
logging_channel_id = await config . guild ( interaction . guild ) . log_channel ( )
if logging_channel_id != " " :
logging_channel = interaction . guild . get_channel ( logging_channel_id )
2023-12-17 02:36:18 -05:00
case = await fetch_case ( moderation_id , interaction . guild . id )
2023-12-17 02:16:44 -05:00
if case :
embed = await embed_factory (
2023-12-17 03:36:35 -05:00
" log " , await interaction . client . get_embed_color ( None ) , interaction = interaction , case_dict = case , resolved = resolved
2023-12-17 02:16:44 -05:00
)
try :
await logging_channel . send ( embed = embed )
except Forbidden :
return
2023-12-18 18:33:37 -05:00
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 )
2023-12-30 04:10:25 -05:00
2023-12-30 04:13:00 -05:00
def convert_timedelta_to_str ( timedelta : td ) - > str :
2023-12-30 04:10:25 -05:00
""" 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 } "