2023-12-14 18:55:35 -05:00
# _____ _
# / ____| (_)
# | (___ ___ __ _ _____ ___ _ __ ___ _ __ ___ ___ _ __
# \___ \ / _ \/ _` / __\ \ /\ / / | '_ ` _ \| '_ ` _ \ / _ \ '__|
# ____) | __/ (_| \__ \\ V V /| | | | | | | | | | | | __/ |
# |_____/ \___|\__,_|___/ \_/\_/ |_|_| |_| |_|_| |_| |_|\___|_|
2023-12-14 18:35:25 -05:00
import json
import os
2023-12-18 17:24:40 -05:00
import time
2023-12-28 04:23:55 -05:00
import sqlite3
2023-12-14 18:35:25 -05:00
from datetime import datetime , timedelta , timezone
2023-12-21 17:47:13 -05:00
from math import ceil
2023-12-18 15:57:51 -05:00
2023-12-14 18:35:25 -05:00
import discord
import humanize
from discord . ext import tasks
from pytimeparse2 import disable_dateutil , parse
2024-01-15 06:49:20 -05:00
from redbot . core import app_commands , commands , data_manager
2023-12-14 18:35:25 -05:00
from redbot . core . app_commands import Choice
2024-01-15 09:03:02 -05:00
from redbot . core . bot import Red
2024-01-05 04:21:05 -05:00
from redbot . core . utils . chat_formatting import box , error , warning
2023-12-18 15:57:51 -05:00
2024-01-13 10:44:08 -05:00
from . abc import CompositeMetaClass
from . configuration . commands import Configuration
2023-12-18 17:09:15 -05:00
from . utilities . config import config , register_config
from . utilities . database import connect , create_guild_table , fetch_case , mysql_log
2024-01-08 04:18:54 -05:00
from . utilities . factory import case_factory , changes_factory , evidenceformat_factory , message_factory
2023-12-18 17:09:15 -05:00
from . utilities . logger import logger
2023-12-30 04:10:25 -05:00
from . utilities . utils import convert_timedelta_to_str , check_moddable , check_permissions , fetch_channel_dict , fetch_user_dict , generate_dict , log , send_evidenceformat
2023-12-18 17:24:40 -05:00
2023-12-14 18:35:25 -05:00
2024-01-15 06:51:54 -05:00
class Aurora ( Configuration , commands . Cog , metaclass = CompositeMetaClass ) : # pylint: disable=too-many-ancestors
2023-12-28 05:52:30 -05:00
""" Aurora is a fully-featured moderation system.
It is heavily inspired by GalacticBot , and is designed to be a more user - friendly alternative to Red ' s core Mod cogs.
This cog stores all of its data in an SQLite database . """
__author__ = " SeaswimmerTheFsh "
2024-01-12 02:07:24 -05:00
__version__ = " 2.0.2 "
2023-12-14 18:35:25 -05:00
2023-12-14 19:04:27 -05:00
async def red_delete_data_for_user ( self , * , requester , user_id : int ) :
if requester == " discord_deleted_user " :
2023-12-18 15:57:51 -05:00
await config . user_from_id ( user_id ) . clear ( )
2023-12-14 19:04:27 -05:00
2023-12-28 04:23:55 -05:00
database = connect ( )
2023-12-14 19:04:27 -05:00
cursor = database . cursor ( )
cursor . execute ( " SHOW TABLES; " )
tables = [ table [ 0 ] for table in cursor . fetchall ( ) ]
condition = " target_id = %s OR moderator_id = %s ; "
for table in tables :
delete_query = f " DELETE FROM { table [ 0 ] } WHERE { condition } "
cursor . execute ( delete_query , ( user_id , user_id ) )
database . commit ( )
cursor . close ( )
database . close ( )
if requester == " owner " :
2023-12-18 15:57:51 -05:00
await config . user_from_id ( user_id ) . clear ( )
2023-12-14 19:04:27 -05:00
if requester == " user " :
2023-12-18 15:57:51 -05:00
await config . user_from_id ( user_id ) . clear ( )
2023-12-14 19:04:27 -05:00
if requester == " user_strict " :
2023-12-18 15:57:51 -05:00
await config . user_from_id ( user_id ) . clear ( )
2023-12-14 19:04:27 -05:00
else :
2023-12-17 02:16:44 -05:00
logger . warning ( " Invalid requester passed to red_delete_data_for_user: %s " , requester )
2023-12-14 19:04:27 -05:00
2024-01-15 09:03:02 -05:00
def __init__ ( self , bot : Red ) :
2024-01-15 06:49:20 -05:00
super ( ) . __init__ ( )
2023-12-14 18:35:25 -05:00
self . bot = bot
2023-12-18 15:57:51 -05:00
register_config ( config )
2023-12-14 18:35:25 -05:00
disable_dateutil ( )
2024-01-01 12:47:13 -05:00
self . handle_expiry . start ( ) # pylint: disable=no-member
2023-12-14 18:35:25 -05:00
async def cog_load ( self ) :
2024-01-01 12:47:13 -05:00
""" This method prepares the database schema for all of the guilds the bot is currently in. """
2023-12-14 18:35:25 -05:00
guilds : list [ discord . Guild ] = self . bot . guilds
try :
for guild in guilds :
if not await self . bot . cog_disabled_in_guild ( self , guild ) :
2023-12-17 02:36:18 -05:00
await create_guild_table ( guild )
2023-12-14 18:35:25 -05:00
except ConnectionRefusedError :
return
async def cog_unload ( self ) :
2024-01-01 12:47:13 -05:00
self . handle_expiry . cancel ( ) # pylint: disable=no-member
2023-12-14 18:35:25 -05:00
@commands.Cog.listener ( ' on_guild_join ' )
async def db_generate_guild_join ( self , guild : discord . Guild ) :
""" This method prepares the database schema whenever the bot joins a guild. """
if not await self . bot . cog_disabled_in_guild ( self , guild ) :
try :
2023-12-17 02:16:44 -05:00
await create_guild_table ( guild )
2023-12-14 18:35:25 -05:00
except ConnectionRefusedError :
return
@commands.Cog.listener ( ' on_audit_log_entry_create ' )
async def autologger ( self , entry : discord . AuditLogEntry ) :
""" This method automatically logs moderations done by users manually ( " right clicks " ). """
if not await self . bot . cog_disabled_in_guild ( self , entry . guild ) :
2023-12-18 15:57:51 -05:00
if await config . guild ( entry . guild ) . ignore_other_bots ( ) is True :
2023-12-14 18:35:25 -05:00
if entry . user . bot or entry . target . bot :
return
else :
if entry . user . id == self . bot . user . id :
return
duration = " NULL "
if entry . reason :
reason = entry . reason + " (This action was performed without the bot.) "
else :
reason = " This action was performed without the bot. "
if entry . action == discord . AuditLogAction . kick :
moderation_type = ' KICK '
elif entry . action == discord . AuditLogAction . ban :
moderation_type = ' BAN '
elif entry . action == discord . AuditLogAction . unban :
moderation_type = ' UNBAN '
elif entry . action == discord . AuditLogAction . member_update :
if entry . after . timed_out_until is not None :
timed_out_until_aware = entry . after . timed_out_until . replace ( tzinfo = timezone . utc )
duration_datetime = timed_out_until_aware - datetime . now ( tz = timezone . utc )
minutes = round ( duration_datetime . total_seconds ( ) / 60 )
duration = timedelta ( minutes = minutes )
moderation_type = ' MUTE '
else :
moderation_type = ' UNMUTE '
else :
return
2023-12-18 19:02:37 -05:00
await mysql_log ( entry . guild . id , entry . user . id , moderation_type , ' USER ' , entry . target . id , 0 , duration , reason )
2023-12-14 18:35:25 -05:00
#######################################################################################################################
### COMMANDS
#######################################################################################################################
@app_commands.command ( name = " note " )
async def note ( self , interaction : discord . Interaction , target : discord . User , reason : str , silent : bool = None ) :
""" Add a note to a user.
Parameters
- - - - - - - - - - -
target : discord . User
Who are you noting ?
reason : str
Why are you noting this user ?
silent : bool
Should the user be messaged ? """
2023-12-17 02:16:44 -05:00
if not await check_moddable ( target , interaction , [ ' moderate_members ' ] ) :
2023-12-15 13:54:58 -05:00
return
2023-12-14 18:35:25 -05:00
await interaction . response . send_message ( content = f " { target . mention } has recieved a note! \n **Reason** - ` { reason } ` " )
if silent is None :
2023-12-18 15:57:51 -05:00
silent = not await config . guild ( interaction . guild ) . dm_users ( )
2023-12-14 18:35:25 -05:00
if silent is False :
try :
2024-01-08 04:18:54 -05:00
embed = await message_factory ( await self . bot . get_embed_color ( interaction . channel ) , guild = interaction . guild , moderator = interaction . user , reason = reason , moderation_type = ' note ' , response = await interaction . original_response ( ) )
2023-12-14 18:35:25 -05:00
await target . send ( embed = embed )
except discord . errors . HTTPException :
pass
2023-12-17 12:56:46 -05:00
moderation_id = await mysql_log ( interaction . guild . id , interaction . user . id , ' NOTE ' , ' USER ' , target . id , 0 , ' NULL ' , reason )
2023-12-16 21:28:09 -05:00
await interaction . edit_original_response ( content = f " { target . mention } has received a note! (Case `# { moderation_id : , } `) \n **Reason** - ` { reason } ` " )
2023-12-17 03:06:09 -05:00
await log ( interaction , moderation_id )
2023-12-14 18:35:25 -05:00
2023-12-18 18:33:37 -05:00
case = await fetch_case ( moderation_id , interaction . guild . id )
await send_evidenceformat ( interaction , case )
2023-12-14 18:35:25 -05:00
@app_commands.command ( name = " warn " )
async def warn ( self , interaction : discord . Interaction , target : discord . Member , reason : str , silent : bool = None ) :
""" Warn a user.
Parameters
- - - - - - - - - - -
target : discord . Member
Who are you warning ?
reason : str
Why are you warning this user ?
silent : bool
Should the user be messaged ? """
2023-12-17 02:16:44 -05:00
if not await check_moddable ( target , interaction , [ ' moderate_members ' ] ) :
2023-12-15 13:54:58 -05:00
return
2023-12-14 18:35:25 -05:00
await interaction . response . send_message ( content = f " { target . mention } has been warned! \n **Reason** - ` { reason } ` " )
if silent is None :
2023-12-18 15:57:51 -05:00
silent = not await config . guild ( interaction . guild ) . dm_users ( )
2023-12-14 18:35:25 -05:00
if silent is False :
try :
2024-01-08 04:18:54 -05:00
embed = await message_factory ( await self . bot . get_embed_color ( interaction . channel ) , guild = interaction . guild , moderator = interaction . user , reason = reason , moderation_type = ' warned ' , response = await interaction . original_response ( ) )
2023-12-14 18:35:25 -05:00
await target . send ( embed = embed )
except discord . errors . HTTPException :
pass
2023-12-17 12:56:46 -05:00
moderation_id = await mysql_log ( interaction . guild . id , interaction . user . id , ' WARN ' , ' USER ' , target . id , 0 , ' NULL ' , reason )
2023-12-16 21:28:09 -05:00
await interaction . edit_original_response ( content = f " { target . mention } has been warned! (Case `# { moderation_id : , } `) \n **Reason** - ` { reason } ` " )
2023-12-17 03:06:09 -05:00
await log ( interaction , moderation_id )
2023-12-14 18:35:25 -05:00
2023-12-18 18:33:37 -05:00
case = await fetch_case ( moderation_id , interaction . guild . id )
await send_evidenceformat ( interaction , case )
2023-12-16 21:02:54 -05:00
@app_commands.command ( name = " blacklist " )
2024-01-06 13:31:59 -05:00
async def blacklist ( self , interaction : discord . Interaction , target : discord . Member , role : str , reason : str , duration : str = None , silent : bool = None ) :
2023-12-16 21:02:54 -05:00
""" Add a blacklist role to a user.
Parameters
- - - - - - - - - - -
target : discord . Member
Who are you blacklisting ?
role : str
2024-01-06 13:31:59 -05:00
What blacklist role are you applying to the target ?
2023-12-16 21:02:54 -05:00
reason : str
Why are you blacklisting this user ?
2024-01-06 13:31:59 -05:00
duration : str
How long are you blacklisting this user for ?
2023-12-16 21:02:54 -05:00
silent : bool
Should the user be messaged ? """
2023-12-18 15:57:51 -05:00
blacklist_roles = await config . guild ( interaction . guild ) . blacklist_roles ( )
2023-12-16 21:02:54 -05:00
if not blacklist_roles :
2024-01-06 13:31:59 -05:00
await interaction . response . send_message ( content = error ( " There are no blacklist roles set for this server! " ) , ephemeral = True )
2023-12-16 21:02:54 -05:00
return
matching_role = None
for role_dict in blacklist_roles :
if role_dict [ ' id ' ] == role :
matching_role = role_dict
break
if not matching_role :
2024-01-06 13:31:59 -05:00
await interaction . response . send_message ( content = error ( " Please provide a valid blacklist role! " ) , ephemeral = True )
2023-12-16 21:02:54 -05:00
return
2023-12-17 02:16:44 -05:00
if not await check_moddable ( target , interaction , [ ' moderate_members ' , ' manage_roles ' ] ) :
2023-12-16 21:02:54 -05:00
return
if role in [ role . id for role in target . roles ] :
2024-01-05 04:21:05 -05:00
await interaction . response . send_message ( content = error ( f " { target . mention } already has the blacklist role! " ) , ephemeral = True )
2023-12-16 21:02:54 -05:00
return
2023-12-17 02:36:18 -05:00
if silent is None :
2023-12-18 15:57:51 -05:00
silent = not await config . guild ( interaction . guild ) . dm_users ( )
2023-12-17 02:36:18 -05:00
if silent is False :
try :
2024-01-08 04:18:54 -05:00
embed = await message_factory ( await self . bot . get_embed_color ( interaction . channel ) , guild = interaction . guild , moderator = interaction . user , reason = reason , moderation_type = ' blacklisted ' , response = await interaction . original_response ( ) , duration = matching_role [ ' duration ' ] )
2023-12-17 02:36:18 -05:00
await target . send ( embed = embed )
except discord . errors . HTTPException :
pass
2023-12-16 21:02:54 -05:00
role_obj = interaction . guild . get_role ( role )
2024-01-06 13:31:59 -05:00
await target . add_roles ( role , reason = f " Blacklisted by { interaction . user . id } for { humanize . precisedelta ( duration ) } for: { reason } " )
2023-12-16 21:02:54 -05:00
await interaction . response . send_message ( content = f " { target . mention } has been blacklisted with the { role_obj . name } role for { humanize . precisedelta ( matching_role [ ' duration ' ] ) } ! \n **Reason** - ` { reason } ` " )
2024-01-06 13:31:59 -05:00
moderation_id = await mysql_log ( interaction . guild . id , interaction . user . id , ' BLACKLIST ' , ' USER ' , target . id , role , duration , reason )
2023-12-16 21:28:09 -05:00
await interaction . edit_original_response ( content = f " { target . mention } has been blacklisted with the { role_obj . name } role for { humanize . precisedelta ( matching_role [ ' duration ' ] ) } ! (Case `# { moderation_id : , } `) \n **Reason** - ` { reason } ` " )
2023-12-17 03:06:09 -05:00
await log ( interaction , moderation_id )
2023-12-15 17:38:53 -05:00
2023-12-18 18:33:37 -05:00
case = await fetch_case ( moderation_id , interaction . guild . id )
await send_evidenceformat ( interaction , case )
2023-12-14 18:35:25 -05:00
@app_commands.command ( name = " mute " )
async def mute ( self , interaction : discord . Interaction , target : discord . Member , duration : str , reason : str , silent : bool = None ) :
""" Mute a user.
Parameters
- - - - - - - - - - -
target : discord . Member
Who are you unbanning ?
duration : str
How long are you muting this user for ?
reason : str
Why are you unbanning this user ?
silent : bool
Should the user be messaged ? """
2023-12-17 02:16:44 -05:00
if not await check_moddable ( target , interaction , [ ' moderate_members ' ] ) :
2023-12-14 18:35:25 -05:00
return
2023-12-15 13:54:58 -05:00
2023-12-14 18:35:25 -05:00
if target . is_timed_out ( ) is True :
2024-01-05 04:21:05 -05:00
await interaction . response . send_message ( error ( f " { target . mention } is already muted! " ) , allowed_mentions = discord . AllowedMentions ( users = False ) , ephemeral = True )
2023-12-14 18:35:25 -05:00
return
try :
parsed_time = parse ( sval = duration , as_timedelta = True , raise_exception = True )
except ValueError :
2024-01-05 04:21:05 -05:00
await interaction . response . send_message ( error ( " Please provide a valid duration! " ) , ephemeral = True )
2023-12-14 18:35:25 -05:00
return
if parsed_time . total_seconds ( ) / 1000 > 2419200000 :
2024-01-05 04:21:05 -05:00
await interaction . response . send_message ( error ( " Please provide a duration that is less than 28 days. " ) )
2023-12-14 18:35:25 -05:00
return
await target . timeout ( parsed_time , reason = f " Muted by { interaction . user . id } for: { reason } " )
await interaction . response . send_message ( content = f " { target . mention } has been muted for { humanize . precisedelta ( parsed_time ) } ! \n **Reason** - ` { reason } ` " )
if silent is None :
2023-12-18 15:57:51 -05:00
silent = not await config . guild ( interaction . guild ) . dm_users ( )
2023-12-14 18:35:25 -05:00
if silent is False :
try :
2024-01-08 04:18:54 -05:00
embed = await message_factory ( await self . bot . get_embed_color ( interaction . channel ) , guild = interaction . guild , moderator = interaction . user , reason = reason , moderation_type = ' muted ' , response = await interaction . original_response ( ) , duration = parsed_time )
2023-12-14 18:35:25 -05:00
await target . send ( embed = embed )
except discord . errors . HTTPException :
pass
2023-12-17 12:56:46 -05:00
moderation_id = await mysql_log ( interaction . guild . id , interaction . user . id , ' MUTE ' , ' USER ' , target . id , 0 , parsed_time , reason )
2023-12-16 21:28:09 -05:00
await interaction . edit_original_response ( content = f " { target . mention } has been muted for { humanize . precisedelta ( parsed_time ) } ! (Case `# { moderation_id : , } `) \n **Reason** - ` { reason } ` " )
2023-12-17 03:06:09 -05:00
await log ( interaction , moderation_id )
2023-12-14 18:35:25 -05:00
2023-12-18 18:33:37 -05:00
case = await fetch_case ( moderation_id , interaction . guild . id )
await send_evidenceformat ( interaction , case )
2023-12-14 18:35:25 -05:00
@app_commands.command ( name = " unmute " )
async def unmute ( self , interaction : discord . Interaction , target : discord . Member , reason : str = None , silent : bool = None ) :
""" Unmute a user.
Parameters
- - - - - - - - - - -
target : discord . user
Who are you unmuting ?
reason : str
Why are you unmuting this user ?
silent : bool
Should the user be messaged ? """
2023-12-17 02:16:44 -05:00
if not await check_moddable ( target , interaction , [ ' moderate_members ' ] ) :
2023-12-14 18:35:25 -05:00
return
2023-12-15 13:54:58 -05:00
2023-12-14 18:35:25 -05:00
if target . is_timed_out ( ) is False :
2024-01-05 04:21:05 -05:00
await interaction . response . send_message ( error ( f " { target . mention } is not muted! " ) , allowed_mentions = discord . AllowedMentions ( users = False ) , ephemeral = True )
2023-12-14 18:35:25 -05:00
return
2023-12-15 13:54:58 -05:00
2023-12-14 18:35:25 -05:00
if reason :
await target . timeout ( None , reason = f " Unmuted by { interaction . user . id } for: { reason } " )
else :
await target . timeout ( None , reason = f " Unbanned by { interaction . user . id } " )
reason = " No reason given. "
await interaction . response . send_message ( content = f " { target . mention } has been unmuted! \n **Reason** - ` { reason } ` " )
if silent is None :
2023-12-18 15:57:51 -05:00
silent = not await config . guild ( interaction . guild ) . dm_users ( )
2023-12-14 18:35:25 -05:00
if silent is False :
try :
2024-01-08 04:18:54 -05:00
embed = await message_factory ( await self . bot . get_embed_color ( interaction . channel ) , guild = interaction . guild , moderator = interaction . user , reason = reason , moderation_type = ' unmuted ' , response = await interaction . original_response ( ) )
2023-12-14 18:35:25 -05:00
await target . send ( embed = embed )
except discord . errors . HTTPException :
pass
2023-12-17 12:56:46 -05:00
moderation_id = await mysql_log ( interaction . guild . id , interaction . user . id , ' UNMUTE ' , ' USER ' , target . id , 0 , ' NULL ' , reason )
2023-12-16 21:28:09 -05:00
await interaction . edit_original_response ( content = f " { target . mention } has been unmuted! (Case `# { moderation_id : , } `) \n **Reason** - ` { reason } ` " )
2023-12-17 03:06:09 -05:00
await log ( interaction , moderation_id )
2023-12-14 18:35:25 -05:00
2023-12-18 18:33:37 -05:00
case = await fetch_case ( moderation_id , interaction . guild . id )
await send_evidenceformat ( interaction , case )
2023-12-14 18:35:25 -05:00
@app_commands.command ( name = " kick " )
async def kick ( self , interaction : discord . Interaction , target : discord . Member , reason : str , silent : bool = None ) :
""" Kick a user.
Parameters
- - - - - - - - - - -
target : discord . user
Who are you kicking ?
reason : str
Why are you kicking this user ?
silent : bool
Should the user be messaged ? """
2023-12-17 02:16:44 -05:00
if not await check_moddable ( target , interaction , [ ' kick_members ' ] ) :
2023-12-14 18:35:25 -05:00
return
await interaction . response . send_message ( content = f " { target . mention } has been kicked! \n **Reason** - ` { reason } ` " )
if silent is None :
2023-12-18 15:57:51 -05:00
silent = not await config . guild ( interaction . guild ) . dm_users ( )
2023-12-14 18:35:25 -05:00
if silent is False :
try :
2024-01-08 04:18:54 -05:00
embed = await message_factory ( await self . bot . get_embed_color ( interaction . channel ) , guild = interaction . guild , moderator = interaction . user , reason = reason , moderation_type = ' kicked ' , response = await interaction . original_response ( ) )
2023-12-14 18:35:25 -05:00
await target . send ( embed = embed )
except discord . errors . HTTPException :
pass
2023-12-16 20:29:54 -05:00
await target . kick ( reason = f " Kicked by { interaction . user . id } for: { reason } " )
2023-12-14 18:35:25 -05:00
2023-12-17 12:56:46 -05:00
moderation_id = await mysql_log ( interaction . guild . id , interaction . user . id , ' KICK ' , ' USER ' , target . id , 0 , ' NULL ' , reason )
2023-12-16 21:28:09 -05:00
await interaction . edit_original_response ( content = f " { target . mention } has been kicked! (Case `# { moderation_id : , } `) \n **Reason** - ` { reason } ` " )
2023-12-17 03:06:09 -05:00
await log ( interaction , moderation_id )
2023-12-14 18:35:25 -05:00
2023-12-18 18:33:37 -05:00
case = await fetch_case ( moderation_id , interaction . guild . id )
await send_evidenceformat ( interaction , case )
2023-12-14 18:35:25 -05:00
@app_commands.command ( name = " ban " )
@app_commands.choices ( delete_messages = [
Choice ( name = " None " , value = 0 ) ,
Choice ( name = ' 1 Hour ' , value = 3600 ) ,
Choice ( name = ' 12 Hours ' , value = 43200 ) ,
Choice ( name = ' 1 Day ' , value = 86400 ) ,
Choice ( name = ' 3 Days ' , value = 259200 ) ,
Choice ( name = ' 7 Days ' , value = 604800 ) ,
] )
2024-01-01 11:48:08 -05:00
async def ban ( self , interaction : discord . Interaction , target : discord . User , reason : str , duration : str = None , delete_messages : Choice [ int ] = None , silent : bool = None ) :
2023-12-14 18:35:25 -05:00
""" Ban a user.
Parameters
- - - - - - - - - - -
target : discord . user
Who are you banning ?
duration : str
How long are you banning this user for ?
reason : str
Why are you banning this user ?
delete_messages : Choices [ int ]
How many days of messages to delete ?
silent : bool
Should the user be messaged ? """
2023-12-17 02:16:44 -05:00
if not await check_moddable ( target , interaction , [ ' ban_members ' ] ) :
2023-12-14 18:35:25 -05:00
return
2023-12-15 13:54:58 -05:00
2024-01-01 11:48:08 -05:00
if delete_messages is None :
2024-01-01 12:14:53 -05:00
delete_messages_seconds = 0
2024-01-01 11:48:08 -05:00
else :
2024-01-01 12:28:55 -05:00
delete_messages_seconds = delete_messages . value
2024-01-01 11:48:08 -05:00
2023-12-14 18:35:25 -05:00
try :
await interaction . guild . fetch_ban ( target )
2024-01-05 04:21:05 -05:00
await interaction . response . send_message ( content = error ( f " { target . mention } is already banned! " ) , ephemeral = True )
2023-12-14 18:35:25 -05:00
return
except discord . errors . NotFound :
pass
if duration :
try :
parsed_time = parse ( sval = duration , as_timedelta = True , raise_exception = True )
except ValueError :
2024-01-05 04:21:05 -05:00
await interaction . response . send_message ( error ( " Please provide a valid duration! " ) , ephemeral = True )
2023-12-14 18:35:25 -05:00
return
await interaction . response . send_message ( content = f " { target . mention } has been banned for { humanize . precisedelta ( parsed_time ) } ! \n **Reason** - ` { reason } ` " )
try :
2024-01-08 04:18:54 -05:00
embed = await message_factory ( await self . bot . get_embed_color ( interaction . channel ) , guild = interaction . guild , moderator = interaction . user , reason = reason , moderation_type = ' tempbanned ' , response = await interaction . original_response ( ) , duration = parsed_time )
2023-12-14 18:35:25 -05:00
await target . send ( embed = embed )
except discord . errors . HTTPException :
pass
2024-01-01 12:14:53 -05:00
await interaction . guild . ban ( target , reason = f " Tempbanned by { interaction . user . id } for: { reason } (Duration: { parsed_time } ) " , delete_message_seconds = delete_messages_seconds )
2023-12-14 18:35:25 -05:00
2023-12-18 19:02:37 -05:00
moderation_id = await mysql_log ( interaction . guild . id , interaction . user . id , ' TEMPBAN ' , ' USER ' , target . id , 0 , parsed_time , reason )
2023-12-14 18:35:25 -05:00
await interaction . edit_original_response ( content = f " { target . mention } has been banned for { humanize . precisedelta ( parsed_time ) } ! (Case `# { moderation_id } `) \n **Reason** - ` { reason } ` " )
2023-12-17 03:06:09 -05:00
await log ( interaction , moderation_id )
2023-12-18 18:33:37 -05:00
case = await fetch_case ( moderation_id , interaction . guild . id )
await send_evidenceformat ( interaction , case )
2023-12-14 18:35:25 -05:00
else :
await interaction . response . send_message ( content = f " { target . mention } has been banned! \n **Reason** - ` { reason } ` " )
if silent is None :
2023-12-18 15:57:51 -05:00
silent = not await config . guild ( interaction . guild ) . dm_users ( )
2023-12-14 18:35:25 -05:00
if silent is False :
try :
2024-01-08 04:18:54 -05:00
embed = embed = await message_factory ( await self . bot . get_embed_color ( interaction . channel ) , guild = interaction . guild , moderator = interaction . user , reason = reason , moderation_type = ' banned ' , response = await interaction . original_response ( ) )
2023-12-14 18:35:25 -05:00
await target . send ( embed = embed )
except discord . errors . HTTPException :
pass
2024-01-01 12:14:53 -05:00
await interaction . guild . ban ( target , reason = f " Banned by { interaction . user . id } for: { reason } " , delete_message_seconds = delete_messages_seconds )
2023-12-14 18:35:25 -05:00
2023-12-17 12:56:46 -05:00
moderation_id = await mysql_log ( interaction . guild . id , interaction . user . id , ' BAN ' , ' USER ' , target . id , 0 , ' NULL ' , reason )
2023-12-16 21:28:09 -05:00
await interaction . edit_original_response ( content = f " { target . mention } has been banned! (Case `# { moderation_id : , } `) \n **Reason** - ` { reason } ` " )
2023-12-17 02:16:44 -05:00
await log ( interaction , moderation_id )
2023-12-14 18:35:25 -05:00
2023-12-18 18:33:37 -05:00
case = await fetch_case ( moderation_id , interaction . guild . id )
await send_evidenceformat ( interaction , case )
2023-12-14 18:35:25 -05:00
@app_commands.command ( name = " unban " )
async def unban ( self , interaction : discord . Interaction , target : discord . User , reason : str = None , silent : bool = None ) :
""" Unban a user.
Parameters
- - - - - - - - - - -
target : discord . user
Who are you unbanning ?
reason : str
Why are you unbanning this user ?
silent : bool
Should the user be messaged ? """
2023-12-17 02:16:44 -05:00
if not await check_moddable ( target , interaction , [ ' ban_members ' ] ) :
2023-12-14 18:35:25 -05:00
return
2023-12-15 13:54:58 -05:00
2023-12-14 18:35:25 -05:00
try :
await interaction . guild . fetch_ban ( target )
except discord . errors . NotFound :
2024-01-05 04:21:05 -05:00
await interaction . response . send_message ( content = error ( f " { target . mention } is not banned! " ) , ephemeral = True )
2023-12-14 18:35:25 -05:00
return
if reason :
await interaction . guild . unban ( target , reason = f " Unbanned by { interaction . user . id } for: { reason } " )
else :
await interaction . guild . unban ( target , reason = f " Unbanned by { interaction . user . id } " )
reason = " No reason given. "
await interaction . response . send_message ( content = f " { target . mention } has been unbanned! \n **Reason** - ` { reason } ` " )
if silent is None :
2023-12-18 15:57:51 -05:00
silent = not await config . guild ( interaction . guild ) . dm_users ( )
2023-12-14 18:35:25 -05:00
if silent is False :
try :
2024-01-08 04:18:54 -05:00
embed = await message_factory ( await self . bot . get_embed_color ( interaction . channel ) , guild = interaction . guild , moderator = interaction . user , reason = reason , moderation_type = ' unbanned ' , response = await interaction . original_response ( ) )
2023-12-14 18:35:25 -05:00
await target . send ( embed = embed )
except discord . errors . HTTPException :
pass
2023-12-17 12:56:46 -05:00
moderation_id = await mysql_log ( interaction . guild . id , interaction . user . id , ' UNBAN ' , ' USER ' , target . id , 0 , ' NULL ' , reason )
2023-12-16 21:28:09 -05:00
await interaction . edit_original_response ( content = f " { target . mention } has been unbanned! (Case `# { moderation_id : , } `) \n **Reason** - ` { reason } ` " )
2023-12-17 03:06:09 -05:00
await log ( interaction , moderation_id )
2023-12-14 18:35:25 -05:00
2023-12-18 18:33:37 -05:00
case = await fetch_case ( moderation_id , interaction . guild . id )
await send_evidenceformat ( interaction , case )
2023-12-14 18:35:25 -05:00
@app_commands.command ( name = " history " )
2023-12-15 22:28:42 -05:00
async def history ( self , interaction : discord . Interaction , target : discord . User = None , moderator : discord . User = None , pagesize : app_commands . Range [ int , 1 , 20 ] = None , page : int = 1 , ephemeral : bool = None , inline : bool = None , export : bool = False ) :
2023-12-14 18:35:25 -05:00
""" List previous infractions.
Parameters
- - - - - - - - - - -
target : discord . User
User whose infractions to query , overrides moderator if both are given
moderator : discord . User
Query by moderator
pagesize : app_commands . Range [ int , 1 , 25 ]
Amount of infractions to list per page
page : int
Page to select
ephemeral : bool
Hide the command response
inline : bool
Display infractions in a grid arrangement ( does not look very good )
export : bool
Exports the server ' s entire moderation history to a JSON file " " "
2023-12-15 10:10:22 -05:00
if ephemeral is None :
2023-12-18 15:57:51 -05:00
ephemeral = ( await config . user ( interaction . user ) . history_ephemeral ( )
or await config . guild ( interaction . guild ) . history_ephemeral ( )
2023-12-15 10:10:22 -05:00
or False )
2023-12-14 18:35:25 -05:00
2023-12-15 10:13:40 -05:00
if inline is None :
2023-12-18 15:57:51 -05:00
inline = ( await config . user ( interaction . user ) . history_inline ( )
or await config . guild ( interaction . guild ) . history_inline ( )
2023-12-15 10:13:40 -05:00
or False )
2023-12-14 18:35:25 -05:00
if pagesize is None :
if inline is True :
2023-12-18 15:57:51 -05:00
pagesize = ( await config . user ( interaction . user ) . history_inline_pagesize ( )
or await config . guild ( interaction . guild ) . history_inline_pagesize ( )
2023-12-14 18:35:25 -05:00
or 6 )
else :
2023-12-18 15:57:51 -05:00
pagesize = ( await config . user ( interaction . user ) . history_pagesize ( )
or await config . guild ( interaction . guild ) . history_pagesize ( )
2023-12-15 10:13:40 -05:00
or 5 )
2023-12-14 18:35:25 -05:00
2023-12-15 10:10:22 -05:00
await interaction . response . defer ( ephemeral = ephemeral )
2023-12-17 02:16:44 -05:00
permissions = check_permissions ( interaction . client . user , [ ' embed_links ' ] , interaction )
2023-12-15 10:10:22 -05:00
if permissions :
2024-01-05 04:21:05 -05:00
await interaction . followup . send ( error ( f " I do not have the ` { permissions } ` permission, required for this action. " ) , ephemeral = True )
2023-12-15 10:10:22 -05:00
return
2023-12-28 04:23:55 -05:00
database = connect ( )
2023-12-18 16:27:22 -05:00
if export :
2023-12-28 04:39:57 -05:00
database . row_factory = sqlite3 . Row
cursor = database . cursor ( )
2023-12-18 16:27:22 -05:00
2023-12-28 04:39:57 -05:00
query = f """ SELECT *
2023-12-28 05:13:13 -05:00
FROM moderation_ { interaction . guild . id }
2023-12-18 16:27:22 -05:00
ORDER BY moderation_id DESC ; """
2023-12-28 04:39:57 -05:00
cursor . execute ( query )
2023-12-18 16:27:22 -05:00
results = cursor . fetchall ( )
2024-01-12 02:07:24 -05:00
cases = [ ]
for result in results :
case = dict ( result )
cases . append ( case )
2023-12-18 16:27:22 -05:00
try :
2023-12-28 05:13:13 -05:00
filename = str ( data_manager . cog_data_path ( cog_instance = self ) ) + str ( os . sep ) + f " moderation_ { interaction . guild . id } .json "
2023-12-18 16:27:22 -05:00
with open ( filename , " w " , encoding = " utf-8 " ) as f :
2024-01-12 02:07:24 -05:00
json . dump ( cases , f , indent = 2 )
2023-12-18 16:27:22 -05:00
2023-12-28 05:13:13 -05:00
await interaction . followup . send ( file = discord . File ( filename , f " moderation_ { interaction . guild . id } .json " ) , ephemeral = ephemeral )
2023-12-18 16:27:22 -05:00
os . remove ( filename )
except json . JSONDecodeError as e :
2024-01-05 04:26:53 -05:00
await interaction . followup . send ( content = error ( " An error occured while exporting the moderation history. \n Error: \n " ) + box ( e , ' py ' ) , ephemeral = ephemeral )
2023-12-18 16:35:38 -05:00
cursor . close ( )
database . close ( )
return
2023-12-18 16:27:22 -05:00
2023-12-15 10:10:22 -05:00
cursor = database . cursor ( )
2023-12-14 18:35:25 -05:00
if target :
2023-12-28 04:39:57 -05:00
query = f """ SELECT *
2023-12-28 05:13:13 -05:00
FROM moderation_ { interaction . guild . id }
2023-12-28 04:39:57 -05:00
WHERE target_id = ?
2023-12-14 18:35:25 -05:00
ORDER BY moderation_id DESC ; """
2023-12-28 04:39:57 -05:00
cursor . execute ( query , ( target . id , ) )
2023-12-14 18:35:25 -05:00
elif moderator :
2023-12-28 04:39:57 -05:00
query = f """ SELECT *
2023-12-28 05:13:13 -05:00
FROM moderation_ { interaction . guild . id }
2023-12-28 04:39:57 -05:00
WHERE moderator_id = ?
2023-12-14 18:35:25 -05:00
ORDER BY moderation_id DESC ; """
2023-12-28 04:39:57 -05:00
cursor . execute ( query , ( moderator . id , ) )
2023-12-14 18:35:25 -05:00
else :
2023-12-28 04:39:57 -05:00
query = f """ SELECT *
2023-12-28 05:13:13 -05:00
FROM moderation_ { interaction . guild . id }
2023-12-14 18:35:25 -05:00
ORDER BY moderation_id DESC ; """
2023-12-28 04:39:57 -05:00
cursor . execute ( query )
2023-12-14 18:35:25 -05:00
results = cursor . fetchall ( )
result_dict_list = [ ]
2023-12-18 16:23:45 -05:00
for result in results :
case_dict = generate_dict ( result )
if case_dict [ ' moderation_id ' ] == 0 :
continue
result_dict_list . append ( case_dict )
2023-12-14 18:35:25 -05:00
case_quantity = len ( result_dict_list )
2023-12-21 17:47:13 -05:00
page_quantity = ceil ( case_quantity / pagesize )
2023-12-14 18:35:25 -05:00
start_index = ( page - 1 ) * pagesize
end_index = page * pagesize
2024-01-08 01:14:48 -05:00
embed = discord . Embed ( color = await self . bot . get_embed_color ( interaction . channel ) )
2023-12-14 18:35:25 -05:00
embed . set_author ( icon_url = interaction . guild . icon . url , name = ' Infraction History ' )
2023-12-16 21:28:09 -05:00
embed . set_footer ( text = f " Page { page : , } / { page_quantity : , } | { case_quantity : , } Results " )
2023-12-14 18:35:25 -05:00
memory_dict = { }
for case in result_dict_list [ start_index : end_index ] :
if case [ ' target_id ' ] not in memory_dict :
2023-12-17 12:54:41 -05:00
if case [ ' target_type ' ] == ' USER ' :
memory_dict [ str ( case [ ' target_id ' ] ) ] = await fetch_user_dict ( interaction , case [ ' target_id ' ] )
elif case [ ' target_type ' ] == ' CHANNEL ' :
memory_dict [ str ( case [ ' target_id ' ] ) ] = await fetch_channel_dict ( interaction , case [ ' target_id ' ] )
2023-12-14 18:35:25 -05:00
target_user = memory_dict [ str ( case [ ' target_id ' ] ) ]
2023-12-17 12:54:41 -05:00
if case [ ' target_type ' ] == ' USER ' :
target_name = f " ` { target_user [ ' name ' ] } ` " if target_user [ ' discriminator ' ] == " 0 " else f " ` { target_user [ ' name ' ] } # { target_user [ ' discriminator ' ] } ` "
elif case [ ' target_type ' ] == ' CHANNEL ' :
target_name = f " ` { target_user [ ' mention ' ] } ` "
2023-12-14 18:35:25 -05:00
if case [ ' moderator_id ' ] not in memory_dict :
2023-12-17 02:16:44 -05:00
memory_dict [ str ( case [ ' moderator_id ' ] ) ] = await fetch_user_dict ( interaction , case [ ' moderator_id ' ] )
2023-12-14 18:35:25 -05:00
moderator_user = memory_dict [ str ( case [ ' moderator_id ' ] ) ]
2023-12-17 12:54:41 -05:00
moderator_name = f " ` { moderator_user [ ' name ' ] } ` " if moderator_user [ ' discriminator ' ] == " 0 " else f " ` { moderator_user [ ' name ' ] } # { moderator_user [ ' discriminator ' ] } ` "
2023-12-14 18:35:25 -05:00
2023-12-16 21:28:09 -05:00
field_name = f " Case # { case [ ' moderation_id ' ] : , } ( { str . title ( case [ ' moderation_type ' ] ) } ) "
2023-12-17 12:54:41 -05:00
field_value = f " **Target:** { target_name } ( { target_user [ ' id ' ] } ) \n **Moderator:** { moderator_name } ( { moderator_user [ ' id ' ] } ) "
2023-12-14 18:35:25 -05:00
2023-12-15 22:24:29 -05:00
if len ( case [ ' reason ' ] ) > 125 :
field_value + = f " \n **Reason:** ` { str ( case [ ' reason ' ] ) [ : 125 ] } ...` "
2023-12-14 18:35:25 -05:00
else :
field_value + = f " \n **Reason:** ` { str ( case [ ' reason ' ] ) } ` "
if case [ ' duration ' ] != ' NULL ' :
td = timedelta ( * * { unit : int ( val ) for unit , val in zip ( [ " hours " , " minutes " , " seconds " ] , case [ " duration " ] . split ( " : " ) ) } )
duration_embed = f " { humanize . precisedelta ( td ) } | <t: { case [ ' end_timestamp ' ] } :R> " if bool ( case [ ' expired ' ] ) is False else f " { humanize . precisedelta ( td ) } | Expired "
field_value + = f " \n **Duration:** { duration_embed } "
field_value + = f " \n **Timestamp:** <t: { case [ ' timestamp ' ] } > | <t: { case [ ' timestamp ' ] } :R> "
if bool ( case [ ' resolved ' ] ) :
field_value + = " \n **Resolved:** True "
embed . add_field ( name = field_name , value = field_value , inline = inline )
await interaction . followup . send ( embed = embed , ephemeral = ephemeral )
@app_commands.command ( name = " resolve " )
2023-12-14 20:16:25 -05:00
async def resolve ( self , interaction : discord . Interaction , case : int , reason : str = None ) :
2023-12-14 18:35:25 -05:00
""" Resolve a specific case.
Parameters
- - - - - - - - - - -
2023-12-14 20:16:25 -05:00
case : int
2023-12-14 18:35:25 -05:00
Case number of the case you ' re trying to resolve
reason : str
Reason for resolving case """
2023-12-17 02:16:44 -05:00
permissions = check_permissions ( interaction . client . user , [ ' embed_links ' , ' moderate_members ' , ' ban_members ' ] , interaction )
2023-12-14 18:35:25 -05:00
if permissions :
2024-01-05 04:21:05 -05:00
await interaction . response . send_message ( error ( f " I do not have the ` { permissions } ` permission, required for this action. " ) , ephemeral = True )
2023-12-14 18:35:25 -05:00
return
2023-12-28 04:23:55 -05:00
database = connect ( )
2023-12-14 18:35:25 -05:00
cursor = database . cursor ( )
2023-12-28 05:13:13 -05:00
query_1 = f " SELECT * FROM moderation_ { interaction . guild . id } WHERE moderation_id = ?; "
2023-12-28 04:39:57 -05:00
cursor . execute ( query_1 , ( case , ) )
2023-12-14 18:35:25 -05:00
result_1 = cursor . fetchone ( )
2023-12-14 20:16:25 -05:00
if result_1 is None or case == 0 :
2024-01-05 04:21:05 -05:00
await interaction . response . send_message ( content = error ( f " There is no moderation with a case number of { case } . " ) , ephemeral = True )
2023-12-14 18:35:25 -05:00
return
2023-12-28 05:13:13 -05:00
query_2 = f " SELECT * FROM moderation_ { interaction . guild . id } WHERE moderation_id = ? AND resolved = 0; "
2023-12-28 04:39:57 -05:00
cursor . execute ( query_2 , ( case , ) )
2023-12-14 18:35:25 -05:00
result_2 = cursor . fetchone ( )
if result_2 is None :
2024-01-05 04:21:05 -05:00
await interaction . response . send_message ( content = error ( f " This moderation has already been resolved! \n Use `/case { case } ` for more information. " ) , ephemeral = True )
2023-12-14 18:35:25 -05:00
return
2023-12-17 02:16:44 -05:00
case_dict = generate_dict ( result_2 )
2023-12-14 18:35:25 -05:00
if reason is None :
reason = " No reason given. "
2023-12-14 20:16:25 -05:00
changes : list = case_dict [ ' changes ' ]
2023-12-14 19:57:43 -05:00
if len ( changes ) > 25 :
2024-01-05 04:21:05 -05:00
await interaction . response . send_message ( content = error ( " Due to limitations with Discord ' s embed system, you cannot edit a case more than 25 times. " ) , ephemeral = True )
2023-12-14 19:57:43 -05:00
return
if not changes :
changes . append (
{
' type ' : " ORIGINAL " ,
2023-12-14 20:16:25 -05:00
' timestamp ' : case_dict [ ' timestamp ' ] ,
' reason ' : case_dict [ ' reason ' ] ,
' user_id ' : case_dict [ ' moderator_id ' ]
2023-12-14 19:57:43 -05:00
}
)
changes . append (
{
' type ' : " RESOLVE " ,
' timestamp ' : int ( time . time ( ) ) ,
' reason ' : reason ,
' user_id ' : interaction . user . id
}
)
2023-12-14 20:16:25 -05:00
if case_dict [ ' moderation_type ' ] in [ ' UNMUTE ' , ' UNBAN ' ] :
2024-01-05 04:21:05 -05:00
await interaction . response . send_message ( content = error ( " You cannot resolve this type of moderation! " ) , ephemeral = True )
2024-01-14 01:41:11 -05:00
return
2023-12-14 18:35:25 -05:00
2023-12-14 20:16:25 -05:00
if case_dict [ ' moderation_type ' ] in [ ' MUTE ' , ' TEMPBAN ' , ' BAN ' ] :
if case_dict [ ' moderation_type ' ] == ' MUTE ' :
2023-12-14 18:35:25 -05:00
try :
2023-12-14 20:16:25 -05:00
member = await interaction . guild . fetch_member ( case_dict [ ' target_id ' ] )
2023-12-14 18:35:25 -05:00
2023-12-16 21:28:09 -05:00
await member . timeout ( None , reason = f " Case # { case : , } resolved by { interaction . user . id } " )
2023-12-14 18:35:25 -05:00
except discord . NotFound :
pass
2023-12-14 20:16:25 -05:00
if case_dict [ ' moderation_type ' ] in [ ' TEMPBAN ' , ' BAN ' ] :
2023-12-14 18:35:25 -05:00
try :
2023-12-14 20:16:25 -05:00
user = await interaction . client . fetch_user ( case_dict [ ' target_id ' ] )
2023-12-14 18:35:25 -05:00
2023-12-14 20:16:25 -05:00
await interaction . guild . unban ( user , reason = f " Case # { case } resolved by { interaction . user . id } " )
2023-12-14 18:35:25 -05:00
except discord . NotFound :
pass
2023-12-28 05:13:13 -05:00
resolve_query = f " UPDATE `moderation_ { interaction . guild . id } ` SET resolved = 1, changes = ?, resolved_by = ?, resolve_reason = ? WHERE moderation_id = ? "
2023-12-14 18:35:25 -05:00
else :
2023-12-28 05:13:13 -05:00
resolve_query = f " UPDATE `moderation_ { interaction . guild . id } ` SET resolved = 1, changes = ?, resolved_by = ?, resolve_reason = ? WHERE moderation_id = ? "
2023-12-14 18:35:25 -05:00
2023-12-15 11:22:25 -05:00
cursor . execute ( resolve_query , ( json . dumps ( changes ) , interaction . user . id , reason , case_dict [ ' moderation_id ' ] ) )
2023-12-14 18:35:25 -05:00
database . commit ( )
2024-01-08 04:18:54 -05:00
embed = await case_factory ( interaction = interaction , case_dict = await fetch_case ( case , interaction . guild . id ) )
2023-12-16 21:28:09 -05:00
await interaction . response . send_message ( content = f " ✅ Moderation # { case : , } resolved! " , embed = embed )
2023-12-18 15:20:16 -05:00
await log ( interaction , case , resolved = True )
2023-12-14 18:35:25 -05:00
cursor . close ( )
database . close ( )
@app_commands.command ( name = " case " )
2023-12-14 20:16:25 -05:00
@app_commands.choices ( export = [
2023-12-15 10:41:38 -05:00
Choice ( name = ' Export as File ' , value = ' file ' ) ,
Choice ( name = ' Export as Codeblock ' , value = ' codeblock ' )
2023-12-14 20:16:25 -05:00
] )
2023-12-18 17:41:58 -05:00
async def case ( self , interaction : discord . Interaction , case : int , ephemeral : bool = None , evidenceformat : bool = False , changes : bool = False , export : Choice [ str ] = None ) :
2023-12-14 18:35:25 -05:00
""" Check the details of a specific case.
Parameters
- - - - - - - - - - -
2023-12-14 20:16:25 -05:00
case : int
2023-12-14 18:35:25 -05:00
What case are you looking up ?
ephemeral : bool
2023-12-14 20:00:03 -05:00
Hide the command response
2023-12-14 20:16:25 -05:00
changes : bool
List the changes made to the case
2023-12-14 20:00:03 -05:00
export : bool
2023-12-15 10:41:38 -05:00
Export the case to a JSON file or codeblock """
2023-12-17 02:16:44 -05:00
permissions = check_permissions ( interaction . client . user , [ ' embed_links ' ] , interaction )
2023-12-14 18:35:25 -05:00
if permissions :
2024-01-05 04:21:05 -05:00
await interaction . response . send_message ( error ( f " I do not have the ` { permissions } ` permission, required for this action. " ) , ephemeral = True )
2023-12-14 18:35:25 -05:00
return
2023-12-15 10:02:54 -05:00
if ephemeral is None :
2023-12-18 15:57:51 -05:00
ephemeral = ( await config . user ( interaction . user ) . history_ephemeral ( )
or await config . guild ( interaction . guild ) . history_ephemeral ( )
2023-12-15 10:02:54 -05:00
or False )
2023-12-14 20:16:25 -05:00
if case != 0 :
2023-12-17 02:16:44 -05:00
case_dict = await fetch_case ( case , interaction . guild . id )
2023-12-14 20:16:25 -05:00
if case_dict :
2023-12-14 19:59:22 -05:00
if export :
2023-12-15 10:41:38 -05:00
if export . value == ' file ' or len ( str ( case_dict ) ) > 1800 :
2023-12-28 05:13:13 -05:00
filename = str ( data_manager . cog_data_path ( cog_instance = self ) ) + str ( os . sep ) + f " moderation_ { interaction . guild . id } _case_ { case } .json "
2023-12-14 19:59:22 -05:00
2023-12-14 20:06:57 -05:00
with open ( filename , " w " , encoding = " utf-8 " ) as f :
2023-12-14 20:16:25 -05:00
json . dump ( case_dict , f , indent = 2 )
2023-12-14 19:59:22 -05:00
2023-12-15 10:41:38 -05:00
if export . value == ' codeblock ' :
2024-01-05 04:21:05 -05:00
content = f " Case # { case : , } exported. \n " + warning ( " Case was too large to export as codeblock, so it has been uploaded as a `.json` file. " )
2023-12-14 20:16:25 -05:00
else :
2023-12-16 21:28:09 -05:00
content = f " Case # { case : , } exported. "
2023-12-14 20:16:25 -05:00
2023-12-28 05:13:13 -05:00
await interaction . response . send_message ( content = content , file = discord . File ( filename , f " moderation_ { interaction . guild . id } _case_ { case } .json " ) , ephemeral = ephemeral )
2023-12-14 19:59:22 -05:00
2023-12-14 20:06:57 -05:00
os . remove ( filename )
return
2024-01-05 01:00:45 -05:00
await interaction . response . send_message ( content = box ( { json . dumps ( case_dict , indent = 2 ) } ) , ephemeral = ephemeral )
2023-12-14 20:29:48 -05:00
return
2023-12-14 19:57:43 -05:00
if changes :
2024-01-08 04:18:54 -05:00
embed = await changes_factory ( interaction = interaction , case_dict = case_dict )
2023-12-18 17:41:58 -05:00
await interaction . response . send_message ( embed = embed , ephemeral = ephemeral )
elif evidenceformat :
2024-01-08 04:18:54 -05:00
content = await evidenceformat_factory ( interaction = interaction , case_dict = case_dict )
2023-12-18 17:41:58 -05:00
await interaction . response . send_message ( content = content , ephemeral = ephemeral )
2023-12-14 19:57:43 -05:00
else :
2024-01-08 04:18:54 -05:00
embed = await case_factory ( interaction = interaction , case_dict = case_dict )
2023-12-18 17:41:58 -05:00
await interaction . response . send_message ( embed = embed , ephemeral = ephemeral )
2023-12-14 18:35:25 -05:00
return
2023-12-14 20:16:25 -05:00
await interaction . response . send_message ( content = f " No case with case number ` { case } ` found. " , ephemeral = True )
2023-12-14 19:38:35 -05:00
@app_commands.command ( name = " edit " )
2023-12-16 16:59:48 -05:00
async def edit ( self , interaction : discord . Interaction , case : int , reason : str , duration : str = None ) :
2023-12-14 19:38:35 -05:00
""" Edit the reason of a specific case.
Parameters
- - - - - - - - - - -
2023-12-14 20:16:25 -05:00
case : int
2023-12-14 19:38:35 -05:00
What case are you editing ?
reason : str
2023-12-16 16:59:48 -05:00
What is the new reason ?
duration : str
What is the new duration ? Does not reapply the moderation if it has already expired . """
2023-12-17 02:16:44 -05:00
permissions = check_permissions ( interaction . client . user , [ ' embed_links ' ] , interaction )
2023-12-14 19:38:35 -05:00
if permissions :
2024-01-05 04:21:05 -05:00
await interaction . response . send_message ( error ( f " I do not have the ` { permissions } ` permission, required for this action. " ) , ephemeral = True )
2023-12-14 19:38:35 -05:00
return
2023-12-14 20:16:25 -05:00
if case != 0 :
2023-12-16 16:59:48 -05:00
parsed_time = None
2023-12-17 02:16:44 -05:00
case_dict = await fetch_case ( case , interaction . guild . id )
2023-12-14 20:16:25 -05:00
if case_dict :
2023-12-16 16:59:48 -05:00
if duration :
try :
parsed_time = parse ( sval = duration , as_timedelta = True , raise_exception = True )
except ValueError :
2024-01-05 04:21:05 -05:00
await interaction . response . send_message ( error ( " Please provide a valid duration! " ) , ephemeral = True )
2023-12-16 16:59:48 -05:00
return
end_timestamp = case_dict [ ' timestamp ' ] + parsed_time . total_seconds ( )
2023-12-30 04:04:31 -05:00
if case_dict [ ' moderation_type ' ] == ' MUTE ' :
2023-12-16 16:59:48 -05:00
if ( time . time ( ) - case_dict [ ' timestamp ' ] ) + parsed_time . total_seconds ( ) > 2419200 :
2024-01-05 04:21:05 -05:00
await interaction . response . send_message ( error ( " Please provide a duration that is less than 28 days from the initial moderation. " ) )
2023-12-16 16:59:48 -05:00
return
try :
member = await interaction . guild . fetch_member ( case_dict [ ' target_id ' ] )
2023-12-16 21:28:09 -05:00
await member . timeout ( parsed_time , reason = f " Case # { case : , } edited by { interaction . user . id } " )
2023-12-16 16:59:48 -05:00
except discord . NotFound :
pass
2023-12-14 20:16:25 -05:00
changes : list = case_dict [ ' changes ' ]
2023-12-14 19:45:27 -05:00
if len ( changes ) > 25 :
2024-01-05 04:21:05 -05:00
await interaction . response . send_message ( content = error ( " Due to limitations with Discord ' s embed system, you cannot edit a case more than 25 times. " ) , ephemeral = True )
2023-12-14 19:45:27 -05:00
return
2023-12-14 19:38:35 -05:00
if not changes :
2023-12-14 19:41:25 -05:00
changes . append (
{
2023-12-14 19:57:43 -05:00
' type ' : " ORIGINAL " ,
2023-12-14 20:16:25 -05:00
' timestamp ' : case_dict [ ' timestamp ' ] ,
' reason ' : case_dict [ ' reason ' ] ,
2023-12-16 16:59:48 -05:00
' user_id ' : case_dict [ ' moderator_id ' ] ,
' duration ' : case_dict [ ' duration ' ] ,
' end_timestamp ' : case_dict [ ' end_timestamp ' ]
}
)
if parsed_time :
changes . append (
{
' type ' : " EDIT " ,
' timestamp ' : int ( time . time ( ) ) ,
' reason ' : reason ,
' user_id ' : interaction . user . id ,
2023-12-30 04:10:25 -05:00
' duration ' : convert_timedelta_to_str ( parsed_time ) ,
2023-12-16 16:59:48 -05:00
' end_timestamp ' : end_timestamp
}
)
else :
changes . append (
{
' type ' : " EDIT " ,
' timestamp ' : int ( time . time ( ) ) ,
' reason ' : reason ,
' user_id ' : interaction . user . id ,
' duration ' : case_dict [ ' duration ' ] ,
' end_timestamp ' : case_dict [ ' end_timestamp ' ]
2023-12-14 19:41:25 -05:00
}
)
2023-12-14 19:38:35 -05:00
2023-12-28 04:23:55 -05:00
database = connect ( )
2023-12-14 19:38:35 -05:00
cursor = database . cursor ( )
2023-12-16 16:59:48 -05:00
if parsed_time :
2023-12-30 04:02:20 -05:00
update_query = f " UPDATE `moderation_ { interaction . guild . id } ` SET changes = ?, reason = ?, duration = ?, end_timestamp = ? WHERE moderation_id = ? "
2023-12-30 04:13:15 -05:00
cursor . execute ( update_query , ( json . dumps ( changes ) , reason , convert_timedelta_to_str ( parsed_time ) , end_timestamp , case ) )
2023-12-16 16:59:48 -05:00
else :
2023-12-30 04:02:20 -05:00
update_query = f " UPDATE `moderation_ { interaction . guild . id } ` SET changes = ?, reason = ? WHERE moderation_id = ? "
2023-12-16 16:59:48 -05:00
cursor . execute ( update_query , ( json . dumps ( changes ) , reason , case ) )
2023-12-14 19:38:35 -05:00
database . commit ( )
2023-12-17 02:16:44 -05:00
new_case = await fetch_case ( case , interaction . guild . id )
2024-01-08 04:18:54 -05:00
embed = await case_factory ( interaction = interaction , case_dict = new_case )
2023-12-14 19:38:35 -05:00
2023-12-16 21:28:09 -05:00
await interaction . response . send_message ( content = f " ✅ Moderation # { case : , } edited! " , embed = embed , ephemeral = True )
2023-12-17 03:06:09 -05:00
await log ( interaction , case )
2023-12-14 19:38:35 -05:00
cursor . close ( )
database . close ( )
return
2024-01-05 04:21:05 -05:00
await interaction . response . send_message ( content = error ( f " No case with case number ` { case } ` found. " ) , ephemeral = True )
2023-12-14 18:35:25 -05:00
@tasks.loop ( minutes = 1 )
async def handle_expiry ( self ) :
2024-01-01 12:14:53 -05:00
current_time = time . time ( )
2023-12-28 04:23:55 -05:00
database = connect ( )
2023-12-14 18:35:25 -05:00
cursor = database . cursor ( )
2024-01-02 03:34:14 -05:00
global_num = 0
2023-12-14 18:35:25 -05:00
guilds : list [ discord . Guild ] = self . bot . guilds
for guild in guilds :
if not await self . bot . cog_disabled_in_guild ( self , guild ) :
2024-01-01 12:14:53 -05:00
time_per_guild = time . time ( )
2023-12-14 18:35:25 -05:00
2023-12-28 05:13:13 -05:00
tempban_query = f " SELECT target_id, moderation_id FROM moderation_ { guild . id } WHERE end_timestamp != 0 AND end_timestamp <= ? AND moderation_type = ' TEMPBAN ' AND expired = 0 "
2023-12-14 18:35:25 -05:00
try :
cursor . execute ( tempban_query , ( time . time ( ) , ) )
result = cursor . fetchall ( )
2023-12-28 04:47:16 -05:00
except sqlite3 . OperationalError :
2023-12-14 18:35:25 -05:00
continue
target_ids = [ row [ 0 ] for row in result ]
moderation_ids = [ row [ 1 ] for row in result ]
2024-01-02 03:34:14 -05:00
num = 0
2023-12-14 18:35:25 -05:00
for target_id , moderation_id in zip ( target_ids , moderation_ids ) :
user : discord . User = await self . bot . fetch_user ( target_id )
2024-01-02 03:40:35 -05:00
name = f " { user . name } # { user . discriminator } " if user . discriminator != " 0 " else user . name
2023-12-14 18:35:25 -05:00
try :
await guild . unban ( user , reason = f " Automatic unban from case # { moderation_id } " )
2024-01-08 04:18:54 -05:00
embed = await message_factory ( await self . bot . get_embed_color ( guild . channels [ 0 ] ) , guild = guild , reason = f ' Automatic unban from case # { moderation_id } ' , moderation_type = ' unbanned ' )
2023-12-14 18:35:25 -05:00
try :
await user . send ( embed = embed )
except discord . errors . HTTPException :
pass
2024-01-02 03:34:14 -05:00
2024-01-02 03:40:35 -05:00
logger . debug ( " Unbanned %s ( %s ) from %s ( %s ) " , name , user . id , guild . name , guild . id )
2024-01-02 03:34:14 -05:00
num = num + 1
2024-01-01 12:17:27 -05:00
except ( discord . errors . NotFound , discord . errors . Forbidden , discord . errors . HTTPException ) as e :
2024-01-02 03:40:35 -05:00
logger . error ( " Failed to unban %s ( %s ) from %s ( %s ) \n %s " , name , user . id , guild . name , guild . id , e )
2023-12-14 18:35:25 -05:00
2023-12-28 05:13:13 -05:00
expiry_query = f " UPDATE `moderation_ { guild . id } ` SET expired = 1 WHERE (end_timestamp != 0 AND end_timestamp <= ? AND expired = 0 AND moderation_type != ' BLACKLIST ' ) OR (expired = 0 AND resolved = 1 AND moderation_type != ' BLACKLIST ' ) "
2023-12-14 18:35:25 -05:00
cursor . execute ( expiry_query , ( time . time ( ) , ) )
2023-12-28 05:13:13 -05:00
blacklist_query = f " SELECT target_id, moderation_id, role_id FROM moderation_ { guild . id } WHERE end_timestamp != 0 AND end_timestamp <= ? AND moderation_type = ' BLACKLIST ' AND expired = 0 "
2023-12-14 18:35:25 -05:00
try :
cursor . execute ( blacklist_query , ( time . time ( ) , ) )
result = cursor . fetchall ( )
2023-12-28 04:47:16 -05:00
except sqlite3 . OperationalError :
2023-12-14 18:35:25 -05:00
continue
target_ids = [ row [ 0 ] for row in result ]
moderation_ids = [ row [ 1 ] for row in result ]
role_ids = [ row [ 2 ] for row in result ]
for target_id , moderation_id , role_id in zip ( target_ids , moderation_ids , role_ids ) :
try :
2023-12-18 19:00:16 -05:00
# member: discord.Member = await guild.fetch_member(target_id)
2023-12-14 18:35:25 -05:00
role : discord . Role = guild . get_role ( role_id )
if role is None :
raise discord . errors . NotFound
2024-01-01 12:22:14 -05:00
except ( discord . errors . NotFound , discord . errors . Forbidden , discord . errors . HTTPException ) :
2023-12-14 18:35:25 -05:00
continue
2024-01-01 12:14:53 -05:00
per_guild_completion_time = ( time . time ( ) - time_per_guild ) * 1000
2024-01-02 03:34:55 -05:00
logger . debug ( " Completed expiry loop for %s ( %s ) in %s ms with %s users unbanned " , guild . name , guild . id , f " { per_guild_completion_time : .6f } " , num )
2024-01-02 03:34:14 -05:00
global_num = global_num + num
2024-01-01 12:14:53 -05:00
2023-12-14 18:35:25 -05:00
database . commit ( )
cursor . close ( )
database . close ( )
2024-01-01 12:14:53 -05:00
completion_time = ( time . time ( ) - current_time ) * 1000
2024-01-02 03:34:14 -05:00
logger . debug ( " Completed expiry loop in %s ms with %s users unbanned " , f " { completion_time : .6f } " , global_num )
2024-01-01 12:14:53 -05:00
2023-12-14 18:35:25 -05:00
@commands.command ( aliases = [ " tdc " ] )
async def timedeltaconvert ( self , ctx : commands . Context , * , duration : str ) :
""" This command converts a duration to a [`timedelta`](https://docs.python.org/3/library/datetime.html#datetime.timedelta) Python object.
* * Example usage * *
` [ p ] timedeltaconvert 1 day 15 hr 82 minutes 52 s `
* * Output * *
` 1 day , 16 : 22 : 52 ` """
try :
parsed_time = parse ( duration , as_timedelta = True , raise_exception = True )
await ctx . send ( f " ` { str ( parsed_time ) } ` " )
except ValueError :
2024-01-05 04:21:05 -05:00
await ctx . send ( error ( " Please provide a convertible value! " ) )