2024-02-28 10:58:57 -05:00
# pylint: disable=cyclic-import
2023-12-17 02:16:44 -05:00
import json
2024-03-08 14:19:48 -05:00
from datetime import datetime
2023-12-30 04:10:25 -05:00
from datetime import timedelta as td
2024-04-05 10:42:13 -04:00
from typing import Optional , Union
2023-12-18 17:24:40 -05:00
2024-03-08 14:19:48 -05:00
from dateutil . relativedelta import relativedelta as rd
2024-02-02 11:22:08 -05:00
from discord import Guild , Interaction , Member , SelectOption , User
2023-12-18 17:24:40 -05:00
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 ,
2024-02-13 18:02:13 -05:00
) - > Union [ bool , str ] :
2023-12-17 02:16:44 -05:00
""" 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 )
2024-02-14 10:35:57 -05:00
and resolved_permissions . administrator is not True
2023-12-17 02:16:44 -05:00
) :
return permission
return False
async def check_moddable (
target : Union [ User , Member ] , interaction : Interaction , permissions : list
2024-02-13 18:02:13 -05:00
) - > bool :
2023-12-17 02:16:44 -05:00
""" Checks if a moderator can moderate a target. """
if check_permissions ( interaction . client . user , permissions , guild = interaction . guild ) :
await interaction . response . send_message (
2024-02-02 11:22:08 -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-02-02 11:22:08 -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-02-02 11:22:08 -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-02-02 11:22:08 -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
2024-02-02 11:22:08 -05:00
async def get_next_case_number ( guild_id : str , cursor = None ) - > int :
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 "
)
2024-01-08 04:38:12 -05:00
result = cursor . fetchone ( )
return ( result [ 0 ] + 1 ) if result else 1
2023-12-17 02:16:44 -05:00
2024-02-13 18:02:13 -05:00
def generate_dict ( result ) - > dict :
case = {
2023-12-17 02:16:44 -05:00
" 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 ] ) ,
2024-02-02 11:22:08 -05:00
" metadata " : json . loads ( result [ 15 ] ) ,
2023-12-17 02:16:44 -05:00
}
return case
2024-02-13 18:25:32 -05:00
async def fetch_user_dict ( client : commands . Bot , user_id : str ) - > dict :
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 :
2024-02-13 18:28:06 -05:00
try :
user = client . get_user ( int ( user_id ) )
if user is None :
user = await client . fetch_user ( int ( user_id ) )
2023-12-17 02:16:44 -05:00
user_dict = {
" id " : user . id ,
" name " : user . name ,
" discriminator " : user . discriminator ,
}
2024-02-13 18:28:06 -05:00
except NotFound :
user_dict = {
" id " : user_id ,
" name " : " Deleted User " ,
" discriminator " : " 0 " ,
}
2023-12-17 02:16:44 -05:00
return user_dict
2024-02-13 18:19:41 -05:00
async def fetch_channel_dict ( guild : Guild , channel_id : int ) - > dict :
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 :
2024-02-13 18:19:41 -05:00
channel = guild . get_channel ( int ( channel_id ) )
2023-12-17 12:54:41 -05:00
if not channel :
2024-02-13 18:18:44 -05:00
channel = await guild . fetch_channel ( channel_id )
2023-12-17 02:16:44 -05:00
2024-02-02 11:22:08 -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
2024-02-13 18:19:41 -05:00
async def fetch_role_dict ( guild : Guild , role_id : int ) - > dict :
2023-12-18 18:33:37 -05:00
""" This function returns a dictionary containing either role information or a standard deleted role template. """
2024-02-13 18:19:41 -05:00
role = guild . get_role ( int ( role_id ) )
2023-12-17 12:54:41 -05:00
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
2024-02-13 18:02:13 -05:00
async def log ( interaction : Interaction , moderation_id : int , resolved : bool = False ) - > None :
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
2024-01-08 04:18:54 -05:00
from . factory import log_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 :
2024-02-02 11:22:08 -05:00
embed = await log_factory (
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
2024-02-02 11:22:08 -05:00
2024-02-13 18:02:13 -05:00
async def send_evidenceformat ( interaction : Interaction , case_dict : dict ) - > None :
2023-12-18 18:33:37 -05:00
""" 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. """
2024-01-08 04:18:54 -05:00
from . factory import evidenceformat_factory
2023-12-18 18:33:37 -05:00
2024-02-02 11:22:08 -05:00
send_evidence_bool = (
await config . user ( interaction . user ) . auto_evidenceformat ( )
2023-12-18 18:33:37 -05:00
or await config . guild ( interaction . guild ) . auto_evidenceformat ( )
2024-02-02 11:22:08 -05:00
or False
)
2023-12-18 18:33:37 -05:00
if send_evidence_bool is False :
return
2024-01-08 04:18:54 -05:00
content = await evidenceformat_factory ( interaction = interaction , case_dict = case_dict )
2023-12-18 18:33:37 -05:00
await interaction . followup . send ( content = content , ephemeral = True )
2023-12-30 04:10:25 -05:00
2024-02-02 11:22:08 -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 } "
2024-01-16 09:23:45 -05:00
2024-02-02 11:22:08 -05:00
2024-04-05 10:42:13 -04:00
def get_bool_emoji ( value : Optional [ bool ] ) - > str :
2024-01-16 09:23:45 -05:00
""" Returns a unicode emoji based on a boolean value. """
if value is True :
return " \N{WHITE HEAVY CHECK MARK} "
if value is False :
return " \N{NO ENTRY SIGN} "
return " \N{BLACK QUESTION MARK ORNAMENT} \N{VARIATION SELECTOR-16} "
2024-02-02 11:22:08 -05:00
2024-01-16 09:23:45 -05:00
def get_pagesize_str ( value : Union [ int , None ] ) - > str :
""" Returns a string based on a pagesize value. """
if value is None :
return " \N{BLACK QUESTION MARK ORNAMENT} \N{VARIATION SELECTOR-16} "
return str ( value ) + " cases per page "
2024-02-02 11:22:08 -05:00
2024-01-16 09:23:45 -05:00
def create_pagesize_options ( ) - > list [ SelectOption ] :
""" Returns a list of SelectOptions for pagesize configuration. """
options = [ ]
options . append (
SelectOption (
label = " Default " ,
value = " default " ,
description = " Reset the pagesize to the default value. " ,
)
)
for i in range ( 1 , 21 ) :
options . append (
SelectOption (
label = str ( i ) ,
value = str ( i ) ,
description = f " Set the pagesize to { i } . " ,
)
)
return options
2024-03-08 14:19:48 -05:00
def timedelta_from_relativedelta ( relativedelta : rd ) - > td :
""" Converts a relativedelta object to a timedelta object. """
now = datetime . now ( )
then = now - relativedelta
return now - then