2023-12-14 18:55:35 -05:00
# _____ _
# / ____| (_)
# | (___ ___ __ _ _____ ___ _ __ ___ _ __ ___ ___ _ __
# \___ \ / _ \/ _` / __\ \ /\ / / | '_ ` _ \| '_ ` _ \ / _ \ '__|
# ____) | __/ (_| \__ \\ V V /| | | | | | | | | | | | __/ |
# |_____/ \___|\__,_|___/ \_/\_/ |_|_| |_| |_|_| |_| |_|\___|_|
2023-12-14 18:35:25 -05:00
import json
import os
2023-12-28 04:23:55 -05:00
import sqlite3
2024-02-02 11:21:56 -05:00
import time
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
from discord . ext import tasks
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-03-08 14:19:48 -05:00
from redbot . core . commands . converter import parse_relativedelta , parse_timedelta
2024-05-03 20:32:02 -04:00
from redbot . core . utils . chat_formatting import box , error , humanize_list , humanize_timedelta , warning
2023-12-18 15:57:51 -05:00
2024-01-16 09:23:45 -05:00
from aurora . importers . aurora import ImportAuroraView
from aurora . importers . galacticbot import ImportGalacticBotView
from aurora . menus . addrole import Addrole
from aurora . menus . guild import Guild
from aurora . menus . immune import Immune
from aurora . menus . overrides import Overrides
from aurora . utilities . config import config , register_config
2024-05-03 20:32:02 -04:00
from aurora . utilities . database import connect , create_guild_table , fetch_case , mysql_log
from aurora . utilities . factory import addrole_embed , case_factory , changes_factory , evidenceformat_factory , guild_embed , immune_embed , message_factory , overrides_embed
2024-01-16 09:23:45 -05:00
from aurora . utilities . logger import logger
2024-05-03 20:32:02 -04:00
from aurora . utilities . utils import check_moddable , check_permissions , convert_timedelta_to_str , fetch_channel_dict , fetch_user_dict , generate_dict , log , send_evidenceformat , timedelta_from_relativedelta
2024-01-16 09:23:45 -05:00
class Aurora ( commands . Cog ) :
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 . """
2024-03-07 03:38:34 -05:00
__author__ = [ " SeaswimmerTheFsh " ]
2024-04-05 10:43:58 -04:00
__version__ = " 2.1.2 "
2024-03-29 07:17:39 -04:00
__documentation__ = " https://seacogs.coastalcommits.com/aurora/ "
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 :
2024-02-02 11:21:56 -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 09:06:58 -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 )
2024-02-14 11:04:26 -05:00
self . handle_expiry . start ( )
2023-12-14 18:35:25 -05:00
2024-03-07 03:38:34 -05:00
def format_help_for_context ( self , ctx : commands . Context ) - > str :
pre_processed = super ( ) . format_help_for_context ( ctx ) or " "
n = " \n " if " \n \n " not in pre_processed else " "
text = [
f " { pre_processed } { n } " ,
f " Cog Version: ** { self . __version__ } ** " ,
f " Author: { humanize_list ( self . __author__ ) } " ,
2024-03-29 07:17:39 -04:00
f " Documentation: { self . __documentation__ } " ,
2024-03-07 03:38:34 -05:00
]
return " \n " . join ( text )
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-02-14 11:04:26 -05:00
self . handle_expiry . cancel ( )
2023-12-14 18:35:25 -05:00
2024-02-02 11:21:56 -05:00
@commands.Cog.listener ( " on_guild_join " )
2023-12-14 18:35:25 -05:00
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
2024-05-03 20:10:26 -04:00
@commands.Cog.listener ( " on_member_join " )
async def addrole_on_member_join ( self , member : discord . Member ) :
""" This method automatically adds roles to users when they join the server. """
if not await self . bot . cog_disabled_in_guild ( self , member . guild ) :
query = f """ SELECT moderation_id, role_id, reason FROM moderation_ { member . guild . id } WHERE target_id = ? AND action = ' ADDROLE ' AND expired = 0 AND resolved = 0; """
database = connect ( )
cursor = database . cursor ( )
cursor . execute ( query , ( member . id , ) )
results = cursor . fetchall ( )
for result in results :
role = member . guild . get_role ( result [ 1 ] )
reason = result [ 2 ]
await member . add_roles ( role , reason = f " Role automatically added on member rejoin for: { reason } (Case # { result [ 0 ] : , } ) " )
2024-02-02 11:21:56 -05:00
@commands.Cog.listener ( " on_audit_log_entry_create " )
2023-12-14 18:35:25 -05:00
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 :
2024-02-02 11:21:56 -05:00
moderation_type = " KICK "
2023-12-14 18:35:25 -05:00
elif entry . action == discord . AuditLogAction . ban :
2024-02-02 11:21:56 -05:00
moderation_type = " BAN "
2023-12-14 18:35:25 -05:00
elif entry . action == discord . AuditLogAction . unban :
2024-02-02 11:21:56 -05:00
moderation_type = " UNBAN "
2023-12-14 18:35:25 -05:00
elif entry . action == discord . AuditLogAction . member_update :
if entry . after . timed_out_until is not None :
2024-02-02 11:21:56 -05:00
timed_out_until_aware = entry . after . timed_out_until . replace (
tzinfo = timezone . utc
)
duration_datetime = timed_out_until_aware - datetime . now (
tz = timezone . utc
)
2023-12-14 18:35:25 -05:00
minutes = round ( duration_datetime . total_seconds ( ) / 60 )
duration = timedelta ( minutes = minutes )
2024-02-02 11:21:56 -05:00
moderation_type = " MUTE "
2023-12-14 18:35:25 -05:00
else :
2024-02-02 11:21:56 -05:00
moderation_type = " UNMUTE "
2023-12-14 18:35:25 -05:00
else :
return
2024-02-02 11:21:56 -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 " )
2024-02-02 11:21:56 -05:00
async def note (
self ,
interaction : discord . Interaction ,
target : discord . User ,
reason : str ,
silent : bool = None ,
) :
2023-12-14 18:35:25 -05:00
""" 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 ? """
2024-02-02 11:21:56 -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
2024-02-02 11:21:56 -05:00
await interaction . response . send_message (
content = f " { target . mention } has recieved a note! \n **Reason** - ` { reason } ` "
)
2023-12-14 18:35:25 -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-14 18:35:25 -05:00
if silent is False :
try :
2024-02-02 11:21:56 -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
2024-02-02 11:21:56 -05:00
moderation_id = await mysql_log (
interaction . guild . id ,
interaction . user . id ,
" NOTE " ,
" USER " ,
target . id ,
0 ,
" NULL " ,
reason ,
)
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 " )
2024-02-02 11:21:56 -05:00
async def warn (
self ,
interaction : discord . Interaction ,
target : discord . Member ,
reason : str ,
silent : bool = None ,
) :
2023-12-14 18:35:25 -05:00
""" 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 ? """
2024-02-02 11:21:56 -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
2024-02-02 11:21:56 -05:00
await interaction . response . send_message (
content = f " { target . mention } has been warned! \n **Reason** - ` { reason } ` "
)
2023-12-14 18:35:25 -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-14 18:35:25 -05:00
if silent is False :
try :
2024-02-02 11:21:56 -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
2024-02-02 11:21:56 -05:00
moderation_id = await mysql_log (
interaction . guild . id ,
interaction . user . id ,
" WARN " ,
" USER " ,
target . id ,
0 ,
" NULL " ,
reason ,
)
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 )
2024-02-02 11:21:56 -05:00
@app_commands.command ( name = " addrole " )
async def addrole (
self ,
interaction : discord . Interaction ,
target : discord . Member ,
2024-02-02 11:33:17 -05:00
role : discord . Role ,
reason : str ,
2024-02-02 11:21:56 -05:00
duration : str = None ,
silent : bool = None ,
) :
""" Add a role to a user.
2023-12-16 21:02:54 -05:00
Parameters
- - - - - - - - - - -
target : discord . Member
2024-02-02 11:21:56 -05:00
Who are you adding a role to ?
2024-02-02 11:32:20 -05:00
role : discord . Role
2024-02-02 11:21:56 -05:00
What role are you adding to the target ?
2023-12-16 21:02:54 -05:00
reason : str
2024-02-02 11:21:56 -05:00
Why are you adding a role to this user ?
2024-01-06 13:31:59 -05:00
duration : str
2024-02-02 11:21:56 -05:00
How long are you adding this role for ?
2023-12-16 21:02:54 -05:00
silent : bool
Should the user be messaged ? """
2024-02-02 11:21:56 -05:00
addrole_whitelist = await config . guild ( interaction . guild ) . addrole_whitelist ( )
2023-12-16 21:02:54 -05:00
2024-02-02 11:21:56 -05:00
if not addrole_whitelist :
await interaction . response . send_message (
content = error ( " There are no whitelisted roles set for this server! " ) ,
ephemeral = True ,
)
2023-12-16 21:02:54 -05:00
return
2024-02-02 11:21:56 -05:00
if duration is not None :
2024-03-08 14:19:48 -05:00
parsed_time = parse_timedelta ( duration )
if parsed_time is None :
2024-02-02 11:21:56 -05:00
await interaction . response . send_message (
2024-03-08 14:19:48 -05:00
content = error ( " Please provide a valid duration! " ) , ephemeral = True
2024-02-02 11:21:56 -05:00
)
return
else :
parsed_time = " NULL "
2024-02-14 10:39:26 -05:00
if role . id not in addrole_whitelist :
2024-02-02 11:21:56 -05:00
await interaction . response . send_message (
content = error ( " That role isn ' t whitelisted! " ) , ephemeral = True
)
2023-12-16 21:02:54 -05:00
return
2024-02-02 11:21:56 -05:00
if not await check_moddable (
target , interaction , [ " moderate_members " , " manage_roles " ]
) :
2023-12-16 21:02:54 -05:00
return
2024-02-02 11:43:27 -05:00
if role . id in [ user_role . id for user_role in target . roles ] :
2024-02-02 11:21:56 -05:00
await interaction . response . send_message (
content = error ( f " { target . mention } already has this role! " ) ,
ephemeral = True ,
)
2023-12-16 21:02:54 -05:00
return
2024-02-02 11:51:44 -05:00
await interaction . response . defer ( )
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-02-02 11:21:56 -05:00
embed = await message_factory (
await self . bot . get_embed_color ( interaction . channel ) ,
guild = interaction . guild ,
moderator = interaction . user ,
reason = reason ,
2024-02-02 12:55:34 -05:00
moderation_type = " addrole " ,
2024-02-02 11:21:56 -05:00
response = await interaction . original_response ( ) ,
duration = parsed_time ,
2024-02-02 12:09:45 -05:00
role = role ,
2024-02-02 11:21:56 -05:00
)
2023-12-17 02:36:18 -05:00
await target . send ( embed = embed )
except discord . errors . HTTPException :
pass
2024-02-02 11:21:56 -05:00
await target . add_roles (
role ,
2024-05-03 20:21:30 -04:00
reason = f " Role added by { interaction . user . id } { ' for ' + humanize_timedelta ( timedelta = parsed_time ) if parsed_time != ' NULL ' else ' ' } for: { reason } " ,
2024-02-02 11:21:56 -05:00
)
2024-02-02 11:47:51 -05:00
response : discord . WebhookMessage = await interaction . followup . send (
2024-05-03 20:21:30 -04:00
content = f " { target . mention } has been given the { role . mention } role { ' for ' + humanize_timedelta ( timedelta = parsed_time ) if parsed_time != ' NULL ' else ' ' } ! \n **Reason** - ` { reason } ` "
2024-02-02 11:21:56 -05:00
)
2023-12-16 21:02:54 -05:00
2024-02-02 11:21:56 -05:00
moderation_id = await mysql_log (
interaction . guild . id ,
interaction . user . id ,
" ADDROLE " ,
" USER " ,
target . id ,
2024-02-02 11:54:01 -05:00
role . id ,
2024-02-02 11:53:08 -05:00
parsed_time ,
2024-02-02 11:21:56 -05:00
reason ,
)
2024-02-02 11:47:51 -05:00
await response . edit (
2024-05-03 20:21:30 -04:00
content = f " { target . mention } has been given the { role . mention } role { ' for ' + humanize_timedelta ( timedelta = parsed_time ) if parsed_time != ' NULL ' else ' ' } ! (Case `# { moderation_id : , } `) \n **Reason** - ` { reason } ` " ,
2024-02-02 11:21:56 -05:00
)
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 )
2024-05-03 20:08:57 -04:00
@app_commands.command ( name = " removerole " )
async def removerole (
self ,
interaction : discord . Interaction ,
target : discord . Member ,
role : discord . Role ,
reason : str ,
duration : str = None ,
silent : bool = None ,
) :
""" Add a role to a user.
Parameters
- - - - - - - - - - -
target : discord . Member
Who are you removing a role from ?
role : discord . Role
What role are you removing from the target ?
reason : str
Why are you removing a role from this user ?
duration : str
How long are you removing this role for ?
silent : bool
Should the user be messaged ? """
addrole_whitelist = await config . guild ( interaction . guild ) . addrole_whitelist ( )
if not addrole_whitelist :
await interaction . response . send_message (
content = error ( " There are no whitelisted roles set for this server! " ) ,
ephemeral = True ,
)
return
if duration is not None :
parsed_time = parse_timedelta ( duration )
if parsed_time is None :
await interaction . response . send_message (
content = error ( " Please provide a valid duration! " ) , ephemeral = True
)
return
else :
parsed_time = " NULL "
if role . id not in addrole_whitelist :
await interaction . response . send_message (
content = error ( " That role isn ' t whitelisted! " ) , ephemeral = True
)
return
if not await check_moddable (
target , interaction , [ " moderate_members " , " manage_roles " ]
) :
return
if role . id not in [ user_role . id for user_role in target . roles ] :
await interaction . response . send_message (
content = error ( f " { target . mention } does not have this role! " ) ,
ephemeral = True ,
)
return
await interaction . response . defer ( )
if silent is None :
silent = not await config . guild ( interaction . guild ) . dm_users ( )
if silent is False :
try :
embed = await message_factory (
await self . bot . get_embed_color ( interaction . channel ) ,
guild = interaction . guild ,
moderator = interaction . user ,
reason = reason ,
moderation_type = " addrole " ,
response = await interaction . original_response ( ) ,
duration = parsed_time ,
role = role ,
)
await target . send ( embed = embed )
except discord . errors . HTTPException :
pass
await target . add_roles (
role ,
2024-05-03 20:21:30 -04:00
reason = f " Role removed by { interaction . user . id } { ' for ' + humanize_timedelta ( timedelta = parsed_time ) if parsed_time != ' NULL ' else ' ' } for: { reason } " ,
2024-05-03 20:08:57 -04:00
)
response : discord . WebhookMessage = await interaction . followup . send (
2024-05-03 20:21:30 -04:00
content = f " { target . mention } has had the { role . mention } role removed { ' for ' + humanize_timedelta ( timedelta = parsed_time ) if parsed_time != ' NULL ' else ' ' } ! \n **Reason** - ` { reason } ` "
2024-05-03 20:08:57 -04:00
)
moderation_id = await mysql_log (
interaction . guild . id ,
interaction . user . id ,
" REMOVEROLE " ,
" USER " ,
target . id ,
role . id ,
parsed_time ,
reason ,
)
await response . edit (
2024-05-03 20:21:30 -04:00
content = f " { target . mention } has had the { role . mention } role removed { ' for ' + humanize_timedelta ( timedelta = parsed_time ) if parsed_time != ' NULL ' else ' ' } ! (Case `# { moderation_id : , } `) \n **Reason** - ` { reason } ` " ,
2024-05-03 20:08:57 -04:00
)
await log ( interaction , moderation_id )
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 " )
2024-02-02 11:21:56 -05:00
async def mute (
self ,
interaction : discord . Interaction ,
target : discord . Member ,
duration : str ,
reason : str ,
silent : bool = None ,
) :
2023-12-14 18:35:25 -05:00
""" 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 ? """
2024-02-02 11:21:56 -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-02-02 11:21:56 -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 :
2024-03-08 14:19:48 -05:00
parsed_time = parse_timedelta ( duration , maximum = timedelta ( days = 28 ) )
if parsed_time is None :
await interaction . response . send_message (
error ( " Please provide a valid duration! " ) , ephemeral = True
)
return
except commands . BadArgument :
2024-02-02 11:21:56 -05:00
await interaction . response . send_message (
2024-03-08 14:19:48 -05:00
error ( " Please provide a duration that is less than 28 days. " ) , ephemeral = True
2024-02-02 11:21:56 -05:00
)
2023-12-14 18:35:25 -05:00
return
2024-02-02 11:21:56 -05:00
await target . timeout (
parsed_time , reason = f " Muted by { interaction . user . id } for: { reason } "
)
2023-12-14 18:35:25 -05:00
2024-02-02 11:21:56 -05:00
await interaction . response . send_message (
2024-03-08 14:56:50 -05:00
content = f " { target . mention } has been muted for { humanize_timedelta ( timedelta = parsed_time ) } ! \n **Reason** - ` { reason } ` "
2024-02-02 11:21:56 -05:00
)
2023-12-14 18:35:25 -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-14 18:35:25 -05:00
if silent is False :
try :
2024-02-02 11:21:56 -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
2024-02-02 11:21:56 -05:00
moderation_id = await mysql_log (
interaction . guild . id ,
interaction . user . id ,
" MUTE " ,
" USER " ,
target . id ,
0 ,
parsed_time ,
reason ,
)
await interaction . edit_original_response (
2024-03-08 14:56:50 -05:00
content = f " { target . mention } has been muted for { humanize_timedelta ( timedelta = parsed_time ) } ! (Case `# { moderation_id : , } `) \n **Reason** - ` { reason } ` "
2024-02-02 11:21:56 -05:00
)
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 " )
2024-02-02 11:21:56 -05:00
async def unmute (
self ,
interaction : discord . Interaction ,
target : discord . Member ,
reason : str = None ,
silent : bool = None ,
) :
2023-12-14 18:35:25 -05:00
""" 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 ? """
2024-02-02 11:21:56 -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-02-02 11:21:56 -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 :
2024-02-02 11:21:56 -05:00
await target . timeout (
None , reason = f " Unmuted by { interaction . user . id } for: { reason } "
)
2023-12-14 18:35:25 -05:00
else :
await target . timeout ( None , reason = f " Unbanned by { interaction . user . id } " )
reason = " No reason given. "
2024-02-02 11:21:56 -05:00
await interaction . response . send_message (
content = f " { target . mention } has been unmuted! \n **Reason** - ` { reason } ` "
)
2023-12-14 18:35:25 -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-14 18:35:25 -05:00
if silent is False :
try :
2024-02-02 11:21:56 -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
2024-02-02 11:21:56 -05:00
moderation_id = await mysql_log (
interaction . guild . id ,
interaction . user . id ,
" UNMUTE " ,
" USER " ,
target . id ,
0 ,
" NULL " ,
reason ,
)
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 " )
2024-02-02 11:21:56 -05:00
async def kick (
self ,
interaction : discord . Interaction ,
target : discord . Member ,
reason : str ,
silent : bool = None ,
) :
2023-12-14 18:35:25 -05:00
""" 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 ? """
2024-02-02 11:21:56 -05:00
if not await check_moddable ( target , interaction , [ " kick_members " ] ) :
2023-12-14 18:35:25 -05:00
return
2024-02-02 11:21:56 -05:00
await interaction . response . send_message (
content = f " { target . mention } has been kicked! \n **Reason** - ` { reason } ` "
)
2023-12-14 18:35:25 -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-14 18:35:25 -05:00
if silent is False :
try :
2024-02-02 11:21:56 -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
2024-02-02 11:21:56 -05:00
moderation_id = await mysql_log (
interaction . guild . id ,
interaction . user . id ,
" KICK " ,
" USER " ,
target . id ,
0 ,
" NULL " ,
reason ,
)
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 " )
2024-02-02 11:21:56 -05:00
@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 ) ,
]
)
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 ? """
2024-02-02 11:21:56 -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-02-02 11:21:56 -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 :
2024-03-08 14:19:48 -05:00
parsed_time = parse_relativedelta ( duration )
if parsed_time is None :
2024-02-02 11:21:56 -05:00
await interaction . response . send_message (
2024-03-08 14:19:48 -05:00
content = error ( " Please provide a valid duration! " ) , ephemeral = True
2024-02-02 11:21:56 -05:00
)
2023-12-14 18:35:25 -05:00
return
2024-03-08 14:30:57 -05:00
try :
parsed_time = timedelta_from_relativedelta ( parsed_time )
except ValueError :
await interaction . response . send_message (
content = error ( " Please provide a valid duration! " ) , ephemeral = True
)
return
2023-12-14 18:35:25 -05:00
2024-02-02 11:21:56 -05:00
await interaction . response . send_message (
2024-03-08 14:56:50 -05:00
content = f " { target . mention } has been banned for { humanize_timedelta ( timedelta = parsed_time ) } ! \n **Reason** - ` { reason } ` "
2024-02-02 11:21:56 -05:00
)
2023-12-14 18:35:25 -05:00
try :
2024-02-02 11:21:56 -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-02-02 11:21:56 -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
2024-02-02 11:21:56 -05:00
moderation_id = await mysql_log (
interaction . guild . id ,
interaction . user . id ,
" TEMPBAN " ,
" USER " ,
target . id ,
0 ,
parsed_time ,
reason ,
)
await interaction . edit_original_response (
2024-03-08 14:56:50 -05:00
content = f " { target . mention } has been banned for { humanize_timedelta ( timedelta = parsed_time ) } ! (Case `# { moderation_id } `) \n **Reason** - ` { reason } ` "
2024-02-02 11:21:56 -05:00
)
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 :
2024-02-02 11:21:56 -05:00
await interaction . response . send_message (
content = f " { target . mention } has been banned! \n **Reason** - ` { reason } ` "
)
2023-12-14 18:35:25 -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-14 18:35:25 -05:00
if silent is False :
try :
2024-02-02 11:21:56 -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-02-02 11:21:56 -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
2024-02-02 11:21:56 -05:00
moderation_id = await mysql_log (
interaction . guild . id ,
interaction . user . id ,
" BAN " ,
" USER " ,
target . id ,
0 ,
" NULL " ,
reason ,
)
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 " )
2024-02-02 11:21:56 -05:00
async def unban (
self ,
interaction : discord . Interaction ,
target : discord . User ,
reason : str = None ,
silent : bool = None ,
) :
2023-12-14 18:35:25 -05:00
""" 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 ? """
2024-02-02 11:21:56 -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-02-02 11:21:56 -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 :
2024-02-02 11:21:56 -05:00
await interaction . guild . unban (
target , reason = f " Unbanned by { interaction . user . id } for: { reason } "
)
2023-12-14 18:35:25 -05:00
else :
2024-02-02 11:21:56 -05:00
await interaction . guild . unban (
target , reason = f " Unbanned by { interaction . user . id } "
)
2023-12-14 18:35:25 -05:00
reason = " No reason given. "
2024-02-02 11:21:56 -05:00
await interaction . response . send_message (
content = f " { target . mention } has been unbanned! \n **Reason** - ` { reason } ` "
)
2023-12-14 18:35:25 -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-14 18:35:25 -05:00
if silent is False :
try :
2024-02-02 11:21:56 -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
2024-02-02 11:21:56 -05:00
moderation_id = await mysql_log (
interaction . guild . id ,
interaction . user . id ,
" UNBAN " ,
" USER " ,
target . id ,
0 ,
" NULL " ,
reason ,
)
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 " )
2024-02-02 11:21:56 -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 :
2024-02-02 11:21:56 -05:00
ephemeral = (
await config . user ( interaction . user ) . history_ephemeral ( )
2023-12-18 15:57:51 -05:00
or await config . guild ( interaction . guild ) . history_ephemeral ( )
2024-02-02 11:21:56 -05:00
or False
)
2023-12-14 18:35:25 -05:00
2023-12-15 10:13:40 -05:00
if inline is None :
2024-02-02 11:21:56 -05:00
inline = (
await config . user ( interaction . user ) . history_inline ( )
2023-12-18 15:57:51 -05:00
or await config . guild ( interaction . guild ) . history_inline ( )
2024-02-02 11:21:56 -05:00
or False
)
2023-12-15 10:13:40 -05:00
2023-12-14 18:35:25 -05:00
if pagesize is None :
if inline is True :
2024-02-02 11:21:56 -05:00
pagesize = (
await config . user ( interaction . user ) . history_inline_pagesize ( )
2023-12-18 15:57:51 -05:00
or await config . guild ( interaction . guild ) . history_inline_pagesize ( )
2024-02-02 11:21:56 -05:00
or 6
)
2023-12-14 18:35:25 -05:00
else :
2024-02-02 11:21:56 -05:00
pagesize = (
await config . user ( interaction . user ) . history_pagesize ( )
2023-12-18 15:57:51 -05:00
or await config . guild ( interaction . guild ) . history_pagesize ( )
2024-02-02 11:21:56 -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 )
2024-02-02 11:21:56 -05:00
permissions = check_permissions (
interaction . client . user , [ " embed_links " ] , interaction
)
2023-12-15 10:10:22 -05:00
if permissions :
2024-02-02 11:21:56 -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 :
2024-02-02 11:21:56 -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
2024-02-02 11:21:56 -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-02-02 11:21:56 -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 )
2024-02-02 11:21:56 -05:00
if case_dict [ " moderation_id " ] == 0 :
2023-12-18 16:23:45 -05:00
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 ) )
2024-02-02 11:21:56 -05:00
embed . set_author ( icon_url = interaction . guild . icon . url , name = " Infraction History " )
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 ] :
2024-02-02 11:21:56 -05:00
if case [ " target_id " ] not in memory_dict :
if case [ " target_type " ] == " USER " :
memory_dict [ str ( case [ " target_id " ] ) ] = await fetch_user_dict (
2024-02-13 18:18:44 -05:00
interaction . client , case [ " target_id " ]
2024-02-02 11:21:56 -05:00
)
elif case [ " target_type " ] == " CHANNEL " :
memory_dict [ str ( case [ " target_id " ] ) ] = await fetch_channel_dict (
2024-02-13 18:18:44 -05:00
interaction . guild , case [ " target_id " ]
2024-02-02 11:21:56 -05:00
)
target_user = memory_dict [ str ( case [ " target_id " ] ) ]
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 " :
2023-12-17 12:54:41 -05:00
target_name = f " ` { target_user [ ' mention ' ] } ` "
2024-02-02 11:21:56 -05:00
if case [ " moderator_id " ] not in memory_dict :
memory_dict [ str ( case [ " moderator_id " ] ) ] = await fetch_user_dict (
2024-02-13 18:18:44 -05:00
interaction . client , case [ " moderator_id " ]
2024-02-02 11:21:56 -05:00
)
moderator_user = memory_dict [ str ( case [ " moderator_id " ] ) ]
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
2024-02-02 11:21:56 -05:00
if len ( case [ " reason " ] ) > 125 :
2023-12-15 22:24:29 -05:00
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 ' ] ) } ` "
2024-02-02 11:21:56 -05:00
if case [ " duration " ] != " NULL " :
td = timedelta (
* * {
unit : int ( val )
for unit , val in zip (
[ " hours " , " minutes " , " seconds " ] , case [ " duration " ] . split ( " : " )
)
}
)
duration_embed = (
2024-03-08 14:56:50 -05:00
f " { humanize_timedelta ( timedelta = td ) } | <t: { case [ ' end_timestamp ' ] } :R> "
2024-02-02 11:21:56 -05:00
if bool ( case [ " expired " ] ) is False
2024-03-08 14:56:50 -05:00
else f " { humanize_timedelta ( timedelta = td ) } | Expired "
2024-02-02 11:21:56 -05:00
)
2023-12-14 18:35:25 -05:00
field_value + = f " \n **Duration:** { duration_embed } "
2024-02-02 11:21:56 -05:00
field_value + = (
f " \n **Timestamp:** <t: { case [ ' timestamp ' ] } > | <t: { case [ ' timestamp ' ] } :R> "
)
2023-12-14 18:35:25 -05:00
2024-02-02 12:16:41 -05:00
if case [ " role_id " ] != " 0 " :
role = interaction . guild . get_role ( int ( case [ " role_id " ] ) )
2024-02-02 12:09:45 -05:00
if role is not None :
2024-02-02 12:05:05 -05:00
field_value + = f " \n **Role:** { role . mention } "
2024-02-02 12:09:45 -05:00
else :
field_value + = f " \n **Role:** Deleted Role ( { case [ ' role_id ' ] } ) "
2024-02-02 12:05:05 -05:00
2024-02-02 11:21:56 -05:00
if bool ( case [ " resolved " ] ) :
2023-12-14 18:35:25 -05:00
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 " )
2024-02-02 11:21:56 -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 """
2024-02-02 11:21:56 -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-02-02 11:21:56 -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 ( )
2024-02-02 11:21:56 -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-02-02 11:21:56 -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-02-02 11:21:56 -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. "
2024-02-02 11:21:56 -05:00
changes : list = case_dict [ " changes " ]
2023-12-14 19:57:43 -05:00
if len ( changes ) > 25 :
2024-02-02 11:21:56 -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 (
{
2024-02-02 11:21:56 -05:00
" type " : " ORIGINAL " ,
" timestamp " : case_dict [ " timestamp " ] ,
" reason " : case_dict [ " reason " ] ,
" user_id " : case_dict [ " moderator_id " ] ,
2023-12-14 19:57:43 -05:00
}
)
changes . append (
{
2024-02-02 11:21:56 -05:00
" type " : " RESOLVE " ,
" timestamp " : int ( time . time ( ) ) ,
" reason " : reason ,
" user_id " : interaction . user . id ,
2023-12-14 19:57:43 -05:00
}
)
2024-02-02 11:21:56 -05:00
if case_dict [ " moderation_type " ] in [ " UNMUTE " , " UNBAN " ] :
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
2024-02-02 11:21:56 -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 :
2024-02-02 11:21:56 -05:00
member = await interaction . guild . fetch_member (
case_dict [ " target_id " ]
)
2023-12-14 18:35:25 -05:00
2024-02-02 11:21:56 -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
2024-02-02 11:21:56 -05:00
if case_dict [ " moderation_type " ] in [ " TEMPBAN " , " BAN " ] :
2023-12-14 18:35:25 -05:00
try :
2024-02-02 11:21:56 -05:00
user = await interaction . client . fetch_user ( case_dict [ " target_id " ] )
2023-12-14 18:35:25 -05:00
2024-02-02 11:21:56 -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
2024-02-02 11:21:56 -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-02-02 11:21:56 -05:00
embed = await case_factory (
interaction = interaction ,
case_dict = await fetch_case ( case , interaction . guild . id ) ,
)
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 " )
2024-02-02 11:21:56 -05:00
@app_commands.choices (
export = [
Choice ( name = " Export as File " , value = " file " ) ,
Choice ( name = " Export as Codeblock " , value = " codeblock " ) ,
]
)
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 """
2024-02-02 11:21:56 -05:00
permissions = check_permissions (
interaction . client . user , [ " embed_links " ] , interaction
)
2023-12-14 18:35:25 -05:00
if permissions :
2024-02-02 11:21:56 -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 :
2024-02-02 11:21:56 -05:00
ephemeral = (
await config . user ( interaction . user ) . history_ephemeral ( )
2023-12-18 15:57:51 -05:00
or await config . guild ( interaction . guild ) . history_ephemeral ( )
2024-02-02 11:21:56 -05:00
or False
)
2023-12-15 10:02:54 -05:00
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 :
2024-02-02 11:21:56 -05:00
if export . value == " file " or len ( str ( case_dict ) ) > 1800 :
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
2024-02-02 11:21:56 -05:00
if export . value == " codeblock " :
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
2024-02-02 11:21:56 -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-02-02 11:21:56 -05:00
await interaction . response . send_message (
2024-05-03 20:09:23 -04:00
content = box ( json . dumps ( case_dict , indent = 2 ) , ' json ' ) ,
2024-02-02 11:21:56 -05:00
ephemeral = ephemeral ,
)
2023-12-14 20:29:48 -05:00
return
2023-12-14 19:57:43 -05:00
if changes :
2024-02-02 11:21:56 -05:00
embed = await changes_factory (
interaction = interaction , case_dict = case_dict
)
await interaction . response . send_message (
embed = embed , ephemeral = ephemeral
)
2023-12-18 17:41:58 -05:00
elif evidenceformat :
2024-02-02 11:21:56 -05:00
content = await evidenceformat_factory (
interaction = interaction , case_dict = case_dict
)
await interaction . response . send_message (
content = content , ephemeral = ephemeral
)
2023-12-14 19:57:43 -05:00
else :
2024-02-02 11:21:56 -05:00
embed = await case_factory (
interaction = interaction , case_dict = case_dict
)
await interaction . response . send_message (
embed = embed , ephemeral = ephemeral
)
2023-12-14 18:35:25 -05:00
return
2024-02-02 11:21:56 -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 " )
2024-02-02 11:21:56 -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
2024-02-02 11:21:56 -05:00
What is the new duration ? Does not reapply the moderation if it has already expired .
"""
permissions = check_permissions (
interaction . client . user , [ " embed_links " ] , interaction
)
2023-12-14 19:38:35 -05:00
if permissions :
2024-02-02 11:21:56 -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 :
2024-03-08 14:19:48 -05:00
parsed_time = parse_timedelta ( duration )
if parsed_time is None :
2024-02-02 11:21:56 -05:00
await interaction . response . send_message (
error ( " Please provide a valid duration! " ) , ephemeral = True
)
2023-12-16 16:59:48 -05:00
return
2024-02-02 11:21:56 -05:00
end_timestamp = case_dict [ " timestamp " ] + parsed_time . total_seconds ( )
if case_dict [ " moderation_type " ] == " MUTE " :
if (
time . time ( ) - case_dict [ " timestamp " ]
) + parsed_time . total_seconds ( ) > 2419200 :
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 :
2024-02-02 11:21:56 -05:00
member = await interaction . guild . fetch_member (
case_dict [ " target_id " ]
)
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
2024-02-02 11:21:56 -05:00
changes : list = case_dict [ " changes " ]
2023-12-14 19:45:27 -05:00
if len ( changes ) > 25 :
2024-02-02 11:21:56 -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 (
{
2024-02-02 11:21:56 -05:00
" type " : " ORIGINAL " ,
" timestamp " : case_dict [ " timestamp " ] ,
" reason " : case_dict [ " reason " ] ,
" user_id " : case_dict [ " moderator_id " ] ,
" duration " : case_dict [ " duration " ] ,
" end_timestamp " : case_dict [ " end_timestamp " ] ,
2023-12-16 16:59:48 -05:00
}
)
if parsed_time :
changes . append (
{
2024-02-02 11:21:56 -05:00
" type " : " EDIT " ,
" timestamp " : int ( time . time ( ) ) ,
" reason " : reason ,
" user_id " : interaction . user . id ,
" duration " : convert_timedelta_to_str ( parsed_time ) ,
" end_timestamp " : end_timestamp ,
2023-12-16 16:59:48 -05:00
}
)
else :
changes . append (
{
2024-02-02 11:21:56 -05:00
" 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 = ? "
2024-02-02 11:21:56 -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
2024-02-02 11:21:56 -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-02-02 11:21:56 -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-04-08 06:24:34 -04:00
await self . bot . wait_until_red_ready ( )
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-05-03 20:38:07 -04:00
global_unban_num = 0
global_addrole_num = 0
global_removerole_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-05-03 20:37:01 -04:00
unban_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-02-02 11:21:56 -05:00
name = (
f " { user . name } # { user . discriminator } "
if user . discriminator != " 0 "
else user . name
)
2023-12-14 18:35:25 -05:00
try :
2024-02-02 11:21:56 -05:00
await guild . unban (
user , reason = f " Automatic unban from case # { moderation_id } "
)
2023-12-14 18:35:25 -05:00
2024-02-02 11:21:56 -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-02-02 11:21:56 -05:00
logger . debug (
" Unbanned %s ( %s ) from %s ( %s ) " ,
name ,
user . id ,
guild . name ,
guild . id ,
)
2024-05-03 20:37:01 -04:00
unban_num = unban_num + 1
2024-02-02 11:21:56 -05:00
except (
discord . errors . NotFound ,
discord . errors . Forbidden ,
discord . errors . HTTPException ,
) as e :
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
2024-05-03 20:37:01 -04:00
removerole_num = 0
2024-05-03 20:09:48 -04:00
addrole_query = f " SELECT target_id, moderation_id, role_id FROM moderation_ { guild . id } WHERE end_timestamp != 0 AND end_timestamp <= ? AND moderation_type = ' ADDROLE ' AND expired = 0 "
try :
cursor . execute ( addrole_query , ( time . time ( ) , ) )
result = cursor . fetchall ( )
except sqlite3 . OperationalError :
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 :
member = await guild . fetch_member ( target_id )
role = guild . get_role ( role_id )
if role is None :
2024-05-03 20:32:02 -04:00
logger . warning ( " Role %s does not exist in %s , unable to remove role from user %s " , role_id , guild . id , target_id )
2024-05-03 20:28:26 -04:00
continue
2024-05-03 20:09:48 -04:00
await member . remove_roles (
role , reason = f " Automatic role removal from case # { moderation_id } "
)
2024-05-03 20:37:01 -04:00
removerole_num = removerole_num + 1
2024-05-03 20:09:48 -04:00
except (
discord . errors . NotFound ,
discord . errors . Forbidden ,
discord . errors . HTTPException ,
) :
continue
2023-12-14 18:35:25 -05:00
2024-05-03 20:37:01 -04:00
addrole_num = 0
2024-05-03 20:09:48 -04:00
removerole_query = f " SELECT target_id, moderation_id, role_id FROM moderation_ { guild . id } WHERE end_timestamp != 0 AND end_timestamp <= ? AND moderation_type = ' REMOVEROLE ' AND expired = 0 "
2023-12-14 18:35:25 -05:00
try :
2024-05-03 20:09:48 -04:00
cursor . execute ( removerole_query , ( time . time ( ) , ) )
2023-12-14 18:35:25 -05:00
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 ]
2024-02-02 11:21:56 -05:00
for target_id , moderation_id , role_id in zip (
target_ids , moderation_ids , role_ids
) :
2023-12-14 18:35:25 -05:00
try :
2024-05-03 20:09:48 -04:00
member = await guild . fetch_member ( target_id )
2023-12-14 18:35:25 -05:00
2024-05-03 20:09:48 -04:00
role = guild . get_role ( role_id )
2023-12-14 18:35:25 -05:00
if role is None :
2024-05-03 20:32:02 -04:00
logger . warning ( " Role %s does not exist in %s , unable to add role to user %s " , role_id , guild . id , target_id )
2024-05-03 20:28:26 -04:00
continue
2024-05-03 20:09:48 -04:00
await member . add_roles (
role , reason = f " Automatic role addition from case # { moderation_id } "
)
2024-05-03 20:37:01 -04:00
addrole_num = addrole_num + 1
2024-02-02 11:21:56 -05:00
except (
discord . errors . NotFound ,
discord . errors . Forbidden ,
discord . errors . HTTPException ,
) :
2023-12-14 18:35:25 -05:00
continue
2024-05-03 20:25:42 -04:00
expiry_query = f " UPDATE `moderation_ { guild . id } ` SET expired = 1 WHERE (end_timestamp != 0 AND end_timestamp <= ? AND expired = 0) OR (expired = 0 AND resolved = 1); "
2024-05-03 20:09:48 -04:00
cursor . execute ( expiry_query , ( time . time ( ) , ) )
2024-01-01 12:14:53 -05:00
per_guild_completion_time = ( time . time ( ) - time_per_guild ) * 1000
2024-02-02 11:21:56 -05:00
logger . debug (
2024-05-03 20:37:01 -04:00
" Completed expiry loop for %s ( %s ) in %s ms with %s users unbanned, %s roles added, and %s roles removed " ,
2024-02-02 11:21:56 -05:00
guild . name ,
guild . id ,
f " { per_guild_completion_time : .6f } " ,
2024-05-03 20:37:01 -04:00
unban_num ,
addrole_num ,
removerole_num ,
2024-02-02 11:21:56 -05:00
)
2024-05-03 20:38:07 -04:00
global_unban_num = global_unban_num + unban_num
global_addrole_num = global_addrole_num + addrole_num
global_removerole_num = global_removerole_num + removerole_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-02-02 11:21:56 -05:00
logger . debug (
2024-05-03 20:37:01 -04:00
" Completed expiry loop in %s ms with %s users unbanned, %s roles added, and %s roles removed " ,
2024-02-02 11:21:56 -05:00
f " { completion_time : .6f } " ,
2024-05-03 20:38:07 -04:00
global_unban_num ,
global_addrole_num ,
global_removerole_num ,
2024-02-02 11:21:56 -05:00
)
2024-01-01 12:14:53 -05:00
2024-02-02 11:21:56 -05:00
########################################################################################################################
### Configuration Commands #
########################################################################################################################
2024-01-16 09:23:45 -05:00
@commands.group ( autohelp = True , aliases = [ " moderation " , " mod " ] )
async def aurora ( self , ctx : commands . Context ) :
""" Settings and miscellaneous commands for Aurora. """
@aurora.group ( autohelp = True , name = " settings " , aliases = [ " config " , " options " , " set " ] )
async def aurora_settings ( self , ctx : commands . Context ) :
""" Configure Aurora ' s settings. """
@aurora_settings.command ( name = " overrides " , aliases = [ " override " , " user " ] )
async def aurora_settings_overrides ( self , ctx : commands . Context ) :
""" Manage Aurora ' s user overriddable settings. """
2024-01-16 09:26:54 -05:00
await ctx . send ( embed = await overrides_embed ( ctx ) , view = Overrides ( ctx ) )
2024-01-16 09:23:45 -05:00
@aurora_settings.command ( name = " guild " , aliases = [ " server " ] )
@commands.admin_or_permissions ( manage_guild = True )
@commands.guild_only ( )
async def aurora_settings_guild ( self , ctx : commands . Context ) :
""" Manage Aurora ' s guild settings. """
2024-01-16 09:26:54 -05:00
await ctx . send ( embed = await guild_embed ( ctx ) , view = Guild ( ctx ) )
2024-01-16 09:23:45 -05:00
@aurora_settings.command ( name = " addrole " , aliases = [ " removerole " ] )
@commands.admin_or_permissions ( manage_guild = True )
@commands.guild_only ( )
async def aurora_settings_addrole ( self , ctx : commands . Context ) :
""" Manage the addrole whitelist.
Roles added to this list are also applied to ` / removerole ` . """
2024-01-16 09:26:54 -05:00
await ctx . send ( embed = await addrole_embed ( ctx ) , view = Addrole ( ctx ) )
2024-01-16 09:23:45 -05:00
@aurora_settings.command ( name = " immunity " )
@commands.admin_or_permissions ( manage_guild = True )
@commands.guild_only ( )
async def aurora_settings_immunity ( self , ctx : commands . Context ) :
""" Manage the immunity whitelist. """
2024-01-16 09:26:54 -05:00
await ctx . send ( embed = await immune_embed ( ctx ) , view = Immune ( ctx ) )
2024-01-16 09:23:45 -05:00
@aurora.group ( autohelp = True , name = " import " )
@commands.admin ( )
@commands.guild_only ( )
async def aurora_import ( self , ctx : commands . Context ) :
""" Import moderation history from other bots. """
@aurora_import.command ( name = " aurora " )
@commands.admin ( )
async def aurora_import_aurora ( self , ctx : commands . Context ) :
""" Import moderation history from another bot using Aurora. """
if (
ctx . message . attachments
and ctx . message . attachments [ 0 ] . content_type
== " application/json; charset=utf-8 "
) :
message = await ctx . send (
warning (
" Are you sure you want to import moderations from another bot? \n **This will overwrite any moderations that already exist in this guild ' s moderation table.** \n *The import process will block the rest of your bot until it is complete.* "
)
)
await message . edit ( view = ImportAuroraView ( 60 , ctx , message ) )
else :
await ctx . send ( error ( " Please provide a valid Aurora export file. " ) )
@aurora_import.command ( name = " galacticbot " )
@commands.admin ( )
async def aurora_import_galacticbot ( self , ctx : commands . Context ) :
""" Import moderation history from GalacticBot. """
if (
ctx . message . attachments
and ctx . message . attachments [ 0 ] . content_type
== " application/json; charset=utf-8 "
) :
message = await ctx . send (
warning (
" Are you sure you want to import GalacticBot moderations? \n **This will overwrite any moderations that already exist in this guild ' s moderation table.** \n *The import process will block the rest of your bot until it is complete.* "
)
)
await message . edit ( view = ImportGalacticBotView ( 60 , ctx , message ) )
else :
await ctx . send (
error ( " Please provide a valid GalacticBot moderation export file. " )
)
2024-02-02 11:21:56 -05:00
@aurora.command ( aliases = [ " tdc " , " td " , " timedeltaconvert " ] )
2024-03-08 14:19:48 -05:00
async def timedelta ( self , ctx : commands . Context , * , duration : str ) - > None :
2024-03-08 14:21:26 -05:00
""" Convert a string to a timedelta.
This command converts a duration to a [ ` timedelta ` ] ( https : / / docs . python . org / 3 / library / datetime . html #datetime.timedelta) Python object.
2024-03-08 14:19:48 -05:00
You cannot convert years or months as they are not fixed units . Use ` [ p ] aurora relativedelta ` for that .
2023-12-14 18:35:25 -05:00
* * Example usage * *
2024-01-16 09:29:57 -05:00
` [ p ] aurora timedelta 1 day 15 hr 82 minutes 52 s `
2023-12-14 18:35:25 -05:00
* * Output * *
` 1 day , 16 : 22 : 52 ` """
2024-03-08 14:19:48 -05:00
parsed_time = parse_timedelta ( duration )
if parsed_time is None :
await ctx . send ( error ( " Please provide a convertible value! " ) )
2024-03-08 14:27:50 -05:00
return
2024-03-08 14:19:48 -05:00
await ctx . send ( f " ` { parsed_time } ` " )
@aurora.command ( aliases = [ " rdc " , " rd " , " relativedeltaconvert " ] )
async def relativedelta ( self , ctx : commands . Context , * , duration : str ) - > None :
2024-03-08 14:21:26 -05:00
""" Convert a string to a relativedelta.
This command converts a duration to a [ ` relativedelta ` ] ( https : / / dateutil . readthedocs . io / en / stable / relativedelta . html ) Python object .
2024-03-08 14:19:48 -05:00
* * Example usage * *
` [ p ] aurora relativedelta 3 years 1 day 15 hr 82 minutes 52 s `
* * Output * *
` relativedelta ( years = + 3 , days = + 1 , hours = + 15 , minutes = + 82 , seconds = + 52 ) ` """
parsed_time = parse_relativedelta ( duration )
if parsed_time is None :
2024-01-05 04:21:05 -05:00
await ctx . send ( error ( " Please provide a convertible value! " ) )
2024-03-08 14:27:50 -05:00
return
2024-03-08 14:19:48 -05:00
await ctx . send ( f " ` { parsed_time } ` " )