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
2024-05-06 15:59:43 -04:00
from typing import List
2023-12-18 15:57:51 -05:00
2023-12-14 18:35:25 -05:00
import discord
2024-05-03 20:51:02 -04:00
from discord import Object
2023-12-14 18:35:25 -05:00
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-24 03:46:20 -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-05-06 21:39:43 -04:00
from . importers . aurora import ImportAuroraView
from . importers . galacticbot import ImportGalacticBotView
from . menus . addrole import Addrole
from . menus . guild import Guild
from . menus . immune import Immune
from . menus . overrides import Overrides
from . models . change import Change
from . models . moderation import Moderation
from . utilities . config import config , register_config
from . utilities . database import connect , create_guild_table
2024-05-24 03:46:20 -04:00
from . utilities . factory import addrole_embed , case_factory , changes_factory , evidenceformat_factory , guild_embed , immune_embed , message_factory , overrides_embed
2024-05-06 21:39:43 -04:00
from . utilities . json import dump
from . utilities . logger import logger
2024-05-24 03:46:20 -04:00
from . utilities . utils import check_moddable , check_permissions , get_footer_image , log , send_evidenceformat , timedelta_from_relativedelta
2024-05-06 20:45:22 -04:00
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-05-04 15:25:05 -04:00
__version__ = " 2.2.0 "
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 ) :
2024-05-03 20:44:04 -04:00
query = f """ SELECT moderation_id, role_id, reason FROM moderation_ { member . guild . id } WHERE target_id = ? AND moderation_type = ' ADDROLE ' AND expired = 0 AND resolved = 0; """
2024-05-03 20:10:26 -04:00
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
2024-05-06 16:34:08 -04:00
duration = None
2023-12-14 18:35:25 -05:00
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-05-06 16:34:08 -04:00
Moderation . log (
self . bot ,
2024-02-02 11:21:56 -05:00
entry . guild . id ,
entry . user . id ,
moderation_type ,
" USER " ,
entry . target . id ,
2024-05-06 16:34:08 -04:00
None ,
2024-02-02 11:21:56 -05:00
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 ,
2024-05-06 21:04:08 -04:00
silent : bool | None = None ,
2024-02-02 11:21:56 -05:00
) :
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 ( ) ,
)
2024-05-03 21:35:29 -04:00
await target . send ( embed = embed , file = get_footer_image ( self ) )
2023-12-14 18:35:25 -05:00
except discord . errors . HTTPException :
pass
2024-05-06 16:34:08 -04:00
moderation = Moderation . log (
interaction . client ,
2024-02-02 11:21:56 -05:00
interaction . guild . id ,
interaction . user . id ,
" NOTE " ,
" USER " ,
target . id ,
2024-05-06 16:34:08 -04:00
None ,
None ,
2024-02-02 11:21:56 -05:00
reason ,
)
await interaction . edit_original_response (
2024-05-06 16:34:08 -04:00
content = f " { target . mention } has received a note! (Case `# { moderation . id : , } `) \n **Reason** - ` { reason } ` "
2024-02-02 11:21:56 -05:00
)
2024-05-06 16:34:08 -04:00
await log ( interaction , moderation . id )
await send_evidenceformat ( interaction , moderation . id )
2023-12-18 18:33:37 -05:00
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 ,
2024-05-06 21:04:08 -04:00
silent : bool | None = None ,
2024-02-02 11:21:56 -05:00
) :
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 ( ) ,
)
2024-05-03 21:35:29 -04:00
await target . send ( embed = embed , file = get_footer_image ( self ) )
2023-12-14 18:35:25 -05:00
except discord . errors . HTTPException :
pass
2024-05-06 16:34:08 -04:00
moderation = Moderation . log (
interaction . client ,
2024-02-02 11:21:56 -05:00
interaction . guild . id ,
interaction . user . id ,
" WARN " ,
" USER " ,
target . id ,
2024-05-06 16:34:08 -04:00
None ,
None ,
2024-02-02 11:21:56 -05:00
reason ,
)
await interaction . edit_original_response (
2024-05-06 16:34:08 -04:00
content = f " { target . mention } has been warned! (Case `# { moderation . id : , } `) \n **Reason** - ` { reason } ` "
2024-02-02 11:21:56 -05:00
)
2024-05-06 16:34:08 -04:00
await log ( interaction , moderation . id )
await send_evidenceformat ( interaction , moderation . id )
2023-12-18 18:33:37 -05:00
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-05-06 21:04:08 -04:00
duration : str | None = None ,
silent : bool | None = None ,
2024-02-02 11:21:56 -05:00
) :
""" 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 :
2024-05-06 16:34:08 -04:00
parsed_time = None
2024-02-02 11:21:56 -05:00
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
)
2024-05-03 21:35:29 -04:00
await target . send ( embed = embed , file = get_footer_image ( self ) )
2023-12-17 02:36:18 -05:00
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-05-06 16:34:08 -04:00
moderation = Moderation . log (
interaction . client ,
2024-02-02 11:21:56 -05:00
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-06 16:34:08 -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
)
2024-05-06 16:34:08 -04:00
await log ( interaction , moderation . id )
await send_evidenceformat ( interaction , moderation . id )
2023-12-18 18:33:37 -05:00
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 ,
2024-05-06 21:04:08 -04:00
duration : str | None = None ,
silent : bool | None = None ,
2024-05-03 20:08:57 -04:00
) :
2024-05-03 20:56:07 -04:00
""" Remove a role from a user.
2024-05-03 20:08:57 -04:00
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 :
2024-05-06 16:34:08 -04:00
parsed_time = None
2024-05-03 20:08:57 -04:00
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 ,
2024-05-03 21:02:16 -04:00
moderation_type = " removerole " ,
2024-05-03 20:08:57 -04:00
response = await interaction . original_response ( ) ,
duration = parsed_time ,
role = role ,
)
2024-05-03 21:35:29 -04:00
await target . send ( embed = embed , file = get_footer_image ( self ) )
2024-05-03 20:08:57 -04:00
except discord . errors . HTTPException :
pass
2024-05-03 20:56:07 -04:00
await target . remove_roles (
2024-05-03 20:08:57 -04:00
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
)
2024-05-06 16:34:08 -04:00
moderation = Moderation . log (
interaction . client ,
2024-05-03 20:08:57 -04:00
interaction . guild . id ,
interaction . user . id ,
" REMOVEROLE " ,
" USER " ,
target . id ,
role . id ,
parsed_time ,
reason ,
)
await response . edit (
2024-05-06 16:34:08 -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
)
2024-05-06 16:34:08 -04:00
await log ( interaction , moderation . id )
await send_evidenceformat ( interaction , moderation . id )
2024-05-03 20:08:57 -04:00
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 ,
2024-05-06 21:04:08 -04:00
silent : bool | None = None ,
2024-02-02 11:21:56 -05:00
) :
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 ,
)
2024-05-03 21:35:29 -04:00
await target . send ( embed = embed , file = get_footer_image ( self ) )
2023-12-14 18:35:25 -05:00
except discord . errors . HTTPException :
pass
2024-05-06 16:34:08 -04:00
moderation = Moderation . log (
interaction . client ,
2024-02-02 11:21:56 -05:00
interaction . guild . id ,
interaction . user . id ,
" MUTE " ,
" USER " ,
target . id ,
2024-05-06 16:34:08 -04:00
None ,
2024-02-02 11:21:56 -05:00
parsed_time ,
reason ,
)
await interaction . edit_original_response (
2024-05-06 16:34:08 -04: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
)
2024-05-06 16:34:08 -04:00
await log ( interaction , moderation . id )
await send_evidenceformat ( interaction , moderation . id )
2023-12-18 18:33:37 -05:00
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 ,
2024-05-06 21:04:08 -04:00
reason : str | None = None ,
silent : bool | None = None ,
2024-02-02 11:21:56 -05:00
) :
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 ( ) ,
)
2024-05-03 21:35:29 -04:00
await target . send ( embed = embed , file = get_footer_image ( self ) )
2023-12-14 18:35:25 -05:00
except discord . errors . HTTPException :
pass
2024-05-06 16:34:08 -04:00
moderation = await Moderation . log (
interaction . client ,
2024-02-02 11:21:56 -05:00
interaction . guild . id ,
interaction . user . id ,
" UNMUTE " ,
" USER " ,
target . id ,
2024-05-06 16:34:08 -04:00
None ,
None ,
2024-02-02 11:21:56 -05:00
reason ,
)
await interaction . edit_original_response (
2024-05-06 16:34:08 -04:00
content = f " { target . mention } has been unmuted! (Case `# { moderation . id : , } `) \n **Reason** - ` { reason } ` "
2024-02-02 11:21:56 -05:00
)
2024-05-06 16:34:08 -04:00
await log ( interaction , moderation . id )
await send_evidenceformat ( interaction , moderation . id )
2023-12-18 18:33:37 -05:00
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 ,
2024-05-06 21:04:08 -04:00
silent : bool | None = None ,
2024-02-02 11:21:56 -05:00
) :
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 ( ) ,
)
2024-05-03 21:35:29 -04:00
await target . send ( embed = embed , file = get_footer_image ( self ) )
2023-12-14 18:35:25 -05:00
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-05-06 16:34:08 -04:00
moderation = Moderation . log (
interaction . client ,
2024-02-02 11:21:56 -05:00
interaction . guild . id ,
interaction . user . id ,
" KICK " ,
" USER " ,
target . id ,
2024-05-06 16:34:08 -04:00
None ,
None ,
2024-02-02 11:21:56 -05:00
reason ,
)
await interaction . edit_original_response (
2024-05-06 16:34:08 -04:00
content = f " { target . mention } has been kicked! (Case `# { moderation . id : , } `) \n **Reason** - ` { reason } ` "
2024-02-02 11:21:56 -05:00
)
2024-05-06 16:34:08 -04:00
await log ( interaction , moderation . id )
await send_evidenceformat ( interaction , moderation . id )
2023-12-18 18:33:37 -05:00
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 ,
2024-05-06 21:04:08 -04:00
duration : str | None = None ,
delete_messages : Choice [ int ] | None = None ,
silent : bool | None = None ,
2024-02-02 11:21:56 -05:00
) :
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 ,
)
2024-05-03 21:35:29 -04:00
await target . send ( embed = embed , file = get_footer_image ( self ) )
2023-12-14 18:35:25 -05:00
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-05-06 16:34:08 -04:00
moderation = Moderation . log (
interaction . client ,
2024-02-02 11:21:56 -05:00
interaction . guild . id ,
interaction . user . id ,
" TEMPBAN " ,
" USER " ,
target . id ,
2024-05-06 16:34:08 -04:00
None ,
2024-02-02 11:21:56 -05:00
parsed_time ,
reason ,
)
await interaction . edit_original_response (
2024-05-06 16:34:08 -04: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
)
2024-05-06 16:34:08 -04:00
await log ( interaction , moderation . id )
await send_evidenceformat ( interaction , moderation . id )
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 ( ) ,
)
2024-05-03 21:35:29 -04:00
await target . send ( embed = embed , file = get_footer_image ( self ) )
2023-12-14 18:35:25 -05:00
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-05-06 16:34:08 -04:00
moderation = Moderation . log (
interaction . client ,
2024-02-02 11:21:56 -05:00
interaction . guild . id ,
interaction . user . id ,
" BAN " ,
" USER " ,
target . id ,
0 ,
" NULL " ,
reason ,
)
await interaction . edit_original_response (
2024-05-06 16:34:08 -04:00
content = f " { target . mention } has been banned! (Case `# { moderation . id : , } `) \n **Reason** - ` { reason } ` "
2024-02-02 11:21:56 -05:00
)
2024-05-06 16:34:08 -04:00
await log ( interaction , moderation . id )
await send_evidenceformat ( interaction , moderation . id )
2023-12-18 18:33:37 -05:00
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 ,
2024-05-06 21:04:08 -04:00
reason : str | None = None ,
silent : bool | None = None ,
2024-02-02 11:21:56 -05:00
) :
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 ( ) ,
)
2024-05-03 21:35:29 -04:00
await target . send ( embed = embed , file = get_footer_image ( self ) )
2023-12-14 18:35:25 -05:00
except discord . errors . HTTPException :
pass
2024-05-06 16:34:08 -04:00
moderation = Moderation . log (
interaction . client ,
2024-02-02 11:21:56 -05:00
interaction . guild . id ,
interaction . user . id ,
" UNBAN " ,
" USER " ,
target . id ,
2024-05-06 16:34:08 -04:00
None ,
None ,
2024-02-02 11:21:56 -05:00
reason ,
)
await interaction . edit_original_response (
2024-05-06 16:34:08 -04:00
content = f " { target . mention } has been unbanned! (Case `# { moderation . id : , } `) \n **Reason** - ` { reason } ` "
2024-02-02 11:21:56 -05:00
)
2024-05-06 16:34:08 -04:00
await log ( interaction , moderation . id )
await send_evidenceformat ( interaction , moderation . id )
2023-12-18 18:33:37 -05:00
2024-05-24 03:46:20 -04:00
@app_commands.command ( name = " slowmode " )
async def slowmode (
self ,
interaction : discord . Interaction ,
interval : int ,
channel : discord . TextChannel | None = None ,
reason : str | None = None ,
) :
""" Set the slowmode of a channel.
Parameters
- - - - - - - - - - -
interval : int
The slowmode interval in seconds
channel : discord . TextChannel
The channel to set the slowmode in
reason : str
Why are you setting the slowmode ? """
if channel is None :
channel = interaction . channel
if not await check_moddable ( channel , interaction , [ " manage_channels " ] ) :
return
await channel . edit ( slowmode_delay = interval )
await interaction . response . send_message ( f " Slowmode in { channel . mention } has been set to { interval } seconds! \n **Reason** - ` { reason } ` " )
moderation = Moderation . log (
interaction . client ,
interaction . guild . id ,
interaction . user . id ,
" SLOWMODE " ,
" CHANNEL " ,
channel . id ,
None ,
None ,
reason ,
2024-05-24 04:16:48 -04:00
metadata = { " interval " : f " { interval } seconds " }
2024-05-24 03:46:20 -04:00
)
await interaction . edit_original_response ( content = f " Slowmode in { channel . mention } has been set to { interval } seconds! (Case `# { moderation . id : , } `) \n **Reason** - ` { reason } ` " )
await log ( interaction , moderation . id )
await send_evidenceformat ( interaction , moderation . id )
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 ,
2024-05-06 21:04:08 -04:00
target : discord . User | None = None ,
moderator : discord . User | None = None ,
pagesize : app_commands . Range [ int , 1 , 20 ] | None = None ,
2024-02-02 11:21:56 -05:00
page : int = 1 ,
2024-05-06 21:04:08 -04:00
ephemeral : bool | None = None ,
inline : bool | None = None ,
2024-02-02 11:21:56 -05:00
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 :
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
2024-06-03 00:47:56 -04:00
cases = Moderation . get_all_cases ( bot = interaction . client , guild_id = interaction . guild . id )
2024-06-03 00:22:32 -04:00
2023-12-18 16:27:22 -05:00
with open ( filename , " w " , encoding = " utf-8 " ) as f :
2024-06-03 00:22:32 -04:00
dump ( obj = cases , fp = 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
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 }
2024-05-06 16:02:00 -04:00
WHERE target_id = ? AND moderation_id != 0
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 }
2024-05-06 16:02:00 -04:00
WHERE moderator_id = ? AND moderation_id != 0
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 }
2024-05-06 16:02:00 -04:00
WHERE moderation_id != 0
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 ( )
2024-05-06 15:59:43 -04:00
moderation_list : List [ Moderation ] = [ ]
2023-12-14 18:35:25 -05:00
2023-12-18 16:23:45 -05:00
for result in results :
2024-05-06 16:34:08 -04:00
moderation = Moderation . from_result ( interaction . client , result , interaction . guild . id )
2024-05-06 16:02:00 -04:00
moderation_list . append ( moderation )
2023-12-18 16:23:45 -05:00
2024-05-06 15:59:43 -04:00
case_quantity = len ( moderation_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 = { }
2024-05-06 15:59:43 -04:00
for mod in moderation_list [ start_index : end_index ] :
if mod . target_id not in memory_dict :
memory_dict . update ( {
str ( mod . target_id ) : await mod . get_target ( )
} )
target = memory_dict [ str ( mod . target_id ) ]
2024-02-02 11:21:56 -05:00
2024-05-06 15:59:43 -04:00
if mod . moderator_id not in memory_dict :
memory_dict . update ( {
str ( mod . moderator_id ) : await mod . get_moderator ( )
} )
moderator = memory_dict [ str ( mod . moderator_id ) ]
2023-12-14 18:35:25 -05:00
2024-05-06 16:37:52 -04:00
field_name = f " Case # { mod . id : , } ( { str . title ( mod . type ) } ) "
2024-05-06 15:59:43 -04:00
field_value = f " **Target:** ` { target . name } ` ( { target . id } ) \n **Moderator:** ` { moderator . name } ` ( { moderator . id } ) "
2023-12-14 18:35:25 -05:00
2024-05-24 04:26:58 -04:00
if len ( str ( mod . reason ) ) > 125 :
2024-05-06 15:59:43 -04:00
field_value + = f " \n **Reason:** ` { str ( mod . reason ) [ : 125 ] } ...` "
2023-12-14 18:35:25 -05:00
else :
2024-05-06 15:59:43 -04:00
field_value + = f " \n **Reason:** ` { str ( mod . reason ) } ` "
if mod . duration :
2024-02-02 11:21:56 -05:00
duration_embed = (
2024-05-06 15:59:43 -04:00
f " { humanize_timedelta ( timedelta = mod . duration ) } | <t: { int ( mod . end_timestamp . timestamp ( ) ) } :R> "
if mod . expired is False
else f " { humanize_timedelta ( timedelta = mod . duration ) } | 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 + = (
2024-05-06 15:59:43 -04:00
f " \n **Timestamp:** <t: { int ( mod . timestamp . timestamp ( ) ) } > | <t: { int ( mod . timestamp . timestamp ( ) ) } :R> "
2024-02-02 11:21:56 -05:00
)
2023-12-14 18:35:25 -05:00
2024-05-06 15:59:43 -04:00
if mod . role_id :
role = await mod . get_role ( )
field_value + = f " \n **Role:** { role . mention } ( { role . id } ) "
2024-02-02 12:05:05 -05:00
2024-05-06 15:59:43 -04:00
if mod . 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 (
2024-05-06 21:04:08 -04:00
self , interaction : discord . Interaction , case : int , reason : str | None = None
2024-02-02 11:21:56 -05:00
) :
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
2024-05-06 15:59:43 -04:00
try :
moderation = Moderation . from_sql ( interaction . client , case , interaction . guild . id )
except ValueError :
2024-02-02 11:21:56 -05:00
await interaction . response . send_message (
2024-05-06 15:59:43 -04:00
content = error ( f " Case # { case : , } does not exist! " ) , ephemeral = True
2024-02-02 11:21:56 -05:00
)
2023-12-14 18:35:25 -05:00
return
2024-05-06 15:59:43 -04:00
if len ( moderation . 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
2023-12-14 18:35:25 -05:00
2024-05-06 15:59:43 -04:00
try :
await moderation . resolve ( interaction . user . id , reason )
except ( ValueError , TypeError ) as e :
if e == ValueError :
await interaction . response . send_message (
content = error ( " This case has already been resolved! " ) , ephemeral = True
)
elif e == TypeError :
await interaction . response . send_message (
content = error ( " This case type cannot be resolved! " ) , ephemeral = True
)
2023-12-14 18:35:25 -05:00
2024-02-02 11:21:56 -05:00
embed = await case_factory (
interaction = interaction ,
2024-05-06 16:34:08 -04:00
moderation = moderation ,
2024-02-02 11:21:56 -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
@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 ,
2024-05-06 21:04:08 -04:00
ephemeral : bool | None = None ,
2024-02-02 11:21:56 -05:00
evidenceformat : bool = False ,
changes : bool = False ,
2024-05-06 21:04:08 -04:00
export : Choice [ str ] | None = None ,
2024-02-02 11:21:56 -05:00
) :
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
2024-05-06 15:59:43 -04:00
try :
2024-05-04 16:54:12 -04:00
mod = Moderation . from_sql ( interaction . client , case , interaction . guild . id )
2024-05-06 15:59:43 -04:00
except ValueError :
await interaction . response . send_message (
content = error ( f " Case # { case : , } does not exist! " ) , ephemeral = True
)
return
2023-12-14 19:59:22 -05:00
2024-05-06 15:59:43 -04:00
if export :
if export . value == " file " or len ( mod . to_json ( 2 ) ) > 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
2024-05-06 15:59:43 -04:00
with open ( filename , " w " , encoding = " utf-8 " ) as f :
mod . to_json ( 2 , f )
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. "
2024-02-02 11:21:56 -05:00
)
2023-12-14 19:57:43 -05:00
else :
2024-05-06 15:59:43 -04:00
content = f " Case # { case : , } exported. "
await interaction . response . send_message (
content = content ,
file = discord . File (
filename ,
f " moderation_ { interaction . guild . id } _case_ { case } .json " ,
) ,
ephemeral = ephemeral ,
)
os . remove ( filename )
2023-12-14 18:35:25 -05:00
return
2024-05-06 15:59:43 -04:00
await interaction . response . send_message (
content = box ( mod . to_json ( 2 ) , ' json ' ) ,
ephemeral = ephemeral ,
)
return
if changes :
embed = await changes_factory (
interaction = interaction , moderation = mod
)
await interaction . response . send_message (
embed = embed , ephemeral = ephemeral
)
elif evidenceformat :
2024-05-06 16:47:21 -04:00
content = await evidenceformat_factory ( moderation = mod )
2024-05-06 15:59:43 -04:00
await interaction . response . send_message (
content = content , ephemeral = ephemeral
)
else :
embed = await case_factory (
interaction = interaction , moderation = mod
)
await interaction . response . send_message (
embed = embed , ephemeral = ephemeral
)
return
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 ,
2024-05-06 21:04:08 -04:00
duration : str | None = None ,
2024-02-02 11:21:56 -05:00
) :
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
2024-05-06 15:59:43 -04:00
try :
2024-05-04 22:01:32 -04:00
moderation = Moderation . from_sql ( interaction . client , case , interaction . guild . id )
old_moderation = moderation
2024-05-06 15:59:43 -04:00
except ValueError :
await interaction . response . send_message (
content = error ( f " Case # { case : , } does not exist! " ) , ephemeral = True
)
return
if len ( moderation . changes ) > 25 :
return 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 ,
)
if duration :
moderation . duration = parse_timedelta ( duration )
if moderation . duration is None :
return await interaction . response . send_message (
error ( " Please provide a valid duration! " ) , ephemeral = True
)
moderation . end_timestamp = moderation . timestamp + moderation . duration . total_seconds ( )
if moderation . type == " MUTE " :
if (
time . time ( ) - moderation . unix_timestamp
) + moderation . duration . total_seconds ( ) > 2419200 :
2024-05-04 22:01:32 -04:00
return await interaction . response . send_message (
2024-05-06 15:59:43 -04:00
error (
" Please provide a duration that is less than 28 days from the initial moderation. "
2024-02-02 11:21:56 -05:00
)
2024-05-06 15:59:43 -04:00
)
2023-12-16 16:59:48 -05:00
2024-05-06 15:59:43 -04:00
try :
member = await interaction . guild . fetch_member (
moderation . target_id
)
2023-12-14 19:38:35 -05:00
2024-05-06 15:59:43 -04:00
await member . timeout (
moderation . duration ,
reason = f " Case # { case : , } edited by { interaction . user . id } " ,
)
except discord . NotFound :
pass
if reason :
moderation . reason = reason
if not moderation . changes :
moderation . changes . append ( Change . from_dict ( interaction . client , {
" type " : " ORIGINAL " ,
" timestamp " : old_moderation . timestamp ,
" reason " : old_moderation . reason ,
" user_id " : old_moderation . moderator_id ,
" duration " : old_moderation . duration ,
" end_timestamp " : old_moderation . end_timestamp ,
} ) )
if duration :
moderation . changes . append ( Change . from_dict ( interaction . client , {
" type " : " EDIT " ,
" timestamp " : int ( time . time ( ) ) ,
" reason " : reason ,
" user_id " : interaction . user . id ,
" duration " : moderation . duration ,
" end_timestamp " : moderation . end_timestamp ,
} ) )
else :
moderation . changes . append ( Change . from_dict ( interaction . client , {
" type " : " EDIT " ,
" timestamp " : int ( time . time ( ) ) ,
" reason " : reason ,
" user_id " : interaction . user . id ,
" duration " : moderation . duration ,
" end_timestamp " : moderation . end_timestamp ,
} ) )
moderation . update ( )
embed = await case_factory ( interaction = interaction , moderation = moderation )
2023-12-14 19:38:35 -05:00
2024-02-02 11:21:56 -05:00
await interaction . response . send_message (
2024-05-06 15:59:43 -04:00
content = f " ✅ Moderation # { case : , } edited! " ,
embed = embed ,
ephemeral = True ,
2024-02-02 11:21:56 -05:00
)
2024-05-06 15:59:43 -04:00
await log ( interaction , case )
return
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
2024-05-04 17:15:21 -04:00
tempban_query = f " SELECT target_id, moderation_id FROM moderation_ { guild . id } WHERE end_timestamp IS NOT NULL 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 :
2024-05-03 21:45:04 -04:00
await user . send ( embed = embed , file = get_footer_image ( self ) )
2023-12-14 18:35:25 -05:00
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-04 17:15:21 -04:00
addrole_query = f " SELECT target_id, moderation_id, role_id FROM moderation_ { guild . id } WHERE end_timestamp IS NOT NULL AND end_timestamp <= ? AND moderation_type = ' ADDROLE ' AND expired = 0 "
2024-05-03 20:09:48 -04:00
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 )
await member . remove_roles (
2024-05-03 20:52:29 -04:00
Object ( role_id ) , reason = f " Automatic role removal from case # { moderation_id } "
2024-05-03 20:09:48 -04:00
)
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 ,
2024-05-03 20:47:19 -04:00
) as e :
logger . error (
" Removing the role %s from user %s failed due to: \n %s " ,
role_id ,
target_id ,
e ,
)
2024-05-03 20:09:48 -04:00
continue
2023-12-14 18:35:25 -05:00
2024-05-03 20:37:01 -04:00
addrole_num = 0
2024-05-04 17:15:21 -04:00
removerole_query = f " SELECT target_id, moderation_id, role_id FROM moderation_ { guild . id } WHERE end_timestamp IS NOT NULL 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
await member . add_roles (
2024-05-03 20:52:29 -04:00
Object ( role_id ) , reason = f " Automatic role addition from case # { moderation_id } "
2024-05-03 20:09:48 -04:00
)
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 ,
2024-05-03 20:47:19 -04:00
) as e :
logger . error ( " Adding the role %s to user %s failed due to: \n %s " , role_id , target_id , e )
2023-12-14 18:35:25 -05:00
continue
2024-05-04 17:15:21 -04:00
expiry_query = f " UPDATE `moderation_ { guild . id } ` SET expired = 1 WHERE (end_timestamp IS NOT NULL 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-05-03 22:20:53 -04:00
msg = await ctx . send ( embed = await overrides_embed ( ctx ) )
2024-05-03 22:21:28 -04:00
await msg . edit ( view = Overrides ( ctx , msg , 60 ) )
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-05-03 22:20:53 -04:00
msg = await ctx . send ( embed = await guild_embed ( ctx ) )
2024-05-03 22:21:28 -04:00
await msg . edit ( view = Guild ( ctx , msg , 60 ) )
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-05-03 22:20:53 -04:00
msg = await ctx . send ( embed = await addrole_embed ( ctx ) )
2024-05-03 22:21:28 -04:00
await msg . edit ( view = Addrole ( ctx , msg , 60 ) )
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-05-03 22:20:53 -04:00
msg = await ctx . send ( embed = await immune_embed ( ctx ) )
2024-05-03 22:21:28 -04:00
await msg . edit ( view = Immune ( ctx , msg , 60 ) )
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 } ` " )