2023-06-20 20:07:13 -04:00
import os
2023-07-01 11:58:30 -04:00
import time
2023-07-01 12:00:03 -04:00
from datetime import datetime
2023-06-20 20:07:13 -04:00
import dotenv
import mysql . connector
2023-06-20 12:28:40 -04:00
import revolt
2023-06-20 20:07:13 -04:00
from pytimeparse2 import disable_dateutil , parse
2023-06-23 10:42:48 -04:00
from revolt import utils
2023-06-20 12:28:40 -04:00
from revolt . ext import commands
2023-06-21 17:41:12 -04:00
from utils . embed import CustomEmbed
2023-06-20 12:28:40 -04:00
2023-06-20 20:07:13 -04:00
# This code reads the variables set in the bot's '.env' file.
env = dotenv . find_dotenv ( )
2023-06-30 16:22:45 -04:00
dotenv . load_dotenv ( env )
2023-06-20 20:07:13 -04:00
prefix = os . getenv ( ' PREFIX ' )
db_host = os . getenv ( ' DB_HOST ' )
db_user = os . getenv ( ' DB_USER ' )
db_password = os . getenv ( ' DB_PASSWORD ' )
db = os . getenv ( ' DB ' )
2023-06-23 10:42:48 -04:00
required_role_id = os . getenv ( ' REQUIRED_ROLE_ID ' )
2023-06-20 20:07:13 -04:00
2023-06-20 12:28:40 -04:00
class Moderation ( commands . Cog ) :
2023-09-24 16:20:33 -04:00
def __init__ ( self , client ) :
self . client : revolt . Client = client
2023-06-20 20:07:13 -04:00
disable_dateutil ( )
2023-06-21 12:11:06 -04:00
def mysql_connect ( self ) :
2023-06-21 12:06:24 -04:00
connection = mysql . connector . connect ( host = db_host , user = db_user , password = db_password , database = db )
return connection
2023-09-24 16:33:13 -04:00
def create_server_table ( self , server : revolt . Server ) :
2023-07-01 13:52:01 -04:00
database = Moderation . mysql_connect ( self )
cursor = database . cursor ( )
try :
2023-09-24 08:09:25 -04:00
cursor . execute ( f " SELECT * FROM ` { server . id . lower ( ) } _moderation` " )
print ( f " MySQL Table exists for server { server . name } ( { server . id } ) " )
2023-07-01 13:52:01 -04:00
except mysql . connector . errors . ProgrammingError :
query = f """
2023-09-24 08:09:25 -04:00
CREATE TABLE ` { server . id . lower ( ) } _moderation ` (
2023-07-01 13:52:01 -04:00
moderation_id INT UNIQUE PRIMARY KEY NOT NULL ,
timestamp INT NOT NULL ,
moderation_type LONGTEXT NOT NULL ,
target_id LONGTEXT NOT NULL ,
moderator_id LONGTEXT NOT NULL ,
duration LONGTEXT ,
end_timestamp INT ,
reason LONGTEXT ,
resolved BOOL NOT NULL ,
2023-09-24 08:09:25 -04:00
resolve_reason LONGTEXT ,
expired BOOL NOT NULL
2023-07-01 13:52:01 -04:00
)
"""
cursor . execute ( query )
insert_query = f """
2023-09-24 08:09:25 -04:00
INSERT INTO ` { server . id . lower ( ) } _moderation `
( moderation_id , timestamp , moderation_type , target_id , moderator_id , duration , end_timestamp , reason , resolved , resolve_reason , expired )
VALUES ( % s , % s , % s , % s , % s , % s , % s , % s , % s , % s , % s )
2023-07-01 13:52:01 -04:00
"""
2023-09-24 08:09:25 -04:00
insert_values = ( 0 , 0 , " NULL " , 0 , 0 , " NULL " , 0 , " NULL " , 0 , " NULL " , 0 )
2023-07-01 13:52:01 -04:00
cursor . execute ( insert_query , insert_values )
database . commit ( )
database . close ( )
2023-09-24 08:09:25 -04:00
print ( f " MySQL Table created for { server . name } ( { server . id } ) \n { server . id . lower ( ) } _moderation " )
2023-07-01 13:52:01 -04:00
else :
database . close ( )
return
2023-09-24 08:09:25 -04:00
async def tempban_handler ( self ) :
2023-09-24 16:20:33 -04:00
for server in self . client . servers :
2023-09-24 08:09:25 -04:00
database = Moderation . mysql_connect ( self )
cursor = database . cursor ( )
bans = await server . fetch_bans ( )
for ServerBan in bans :
target_id = ServerBan . user_id
cursor . execute ( f " SELECT moderation_id FROM ` { server . id . lower ( ) } _moderation` WHERE target_id = { target_id } AND moderation_type = ' Temporary Ban ' AND end_timestamp < NOW() AND expired = 0 " )
result = cursor . fetchone ( )
if result is None :
continue
ServerBan . unban ( )
cursor . execute ( f " UPDATE ` { server . id . lower ( ) } _moderation` SET expired = 1 WHERE moderation_id = { result [ 1 ] } " )
database . commit ( )
cursor . close ( )
print ( " Tempban Handler run successful! " )
2023-09-24 16:33:13 -04:00
def mysql_log ( self , ctx : commands . Context , moderation_type : str , target_id : str , duration , reason : str ) :
2023-07-01 11:58:30 -04:00
timestamp = int ( time . time ( ) )
if duration != " NULL " :
end_timedelta = datetime . fromtimestamp ( timestamp ) + duration
end_timestamp = int ( end_timedelta . timestamp ( ) )
else :
2023-09-24 08:09:25 -04:00
end_timestamp = 0
2023-06-21 12:11:06 -04:00
database = Moderation . mysql_connect ( self )
cursor = database . cursor ( )
2023-06-21 12:06:24 -04:00
cursor . execute ( f " SELECT moderation_id FROM ` { ctx . server . id . lower ( ) } _moderation` ORDER BY moderation_id DESC LIMIT 1 " )
2023-06-21 09:20:16 -04:00
moderation_id = cursor . fetchone ( ) [ 0 ] + 1
2023-09-24 08:09:25 -04:00
sql = f " INSERT INTO ` { ctx . server . id . lower ( ) } _moderation` (moderation_id, timestamp, moderation_type, target_id, moderator_id, duration, end_timestamp, reason, resolved, resolve_reason, expired) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) "
val = ( moderation_id , timestamp , moderation_type , target_id , ctx . author . id , duration , end_timestamp , f " { reason } " , 0 , " NULL " , 0 )
2023-06-21 09:20:16 -04:00
cursor . execute ( sql , val )
2023-06-21 12:11:06 -04:00
database . commit ( )
database . close ( )
2023-09-24 08:09:25 -04:00
print ( f " MySQL row inserted into { ctx . server . id . lower ( ) } _moderation! \n { moderation_id } , { timestamp } , { moderation_type } , { target_id } , { ctx . author . id } , { duration } , { end_timestamp } , { reason } , 0, NULL " )
2023-07-01 13:52:01 -04:00
2023-06-21 09:20:16 -04:00
@commands.command ( name = " timeout " , aliases = [ " mute " ] )
async def timeout ( self , ctx : commands . Context , target : commands . MemberConverter , duration : str , * , reason : str ) :
2023-06-23 10:42:48 -04:00
required_role = utils . get ( ctx . server . roles , id = required_role_id )
if required_role not in ctx . author . roles :
await ctx . message . reply ( " You do not have permission to use this command! " )
return
2023-06-20 20:07:13 -04:00
try :
2023-06-21 09:20:16 -04:00
parsed_time = parse ( sval = duration , as_timedelta = True , raise_exception = True )
2023-06-20 20:07:13 -04:00
except ValueError :
await ctx . message . reply ( f " Please provide a valid duration! \n See ` { prefix } tdc` " )
return
2023-06-21 15:33:19 -04:00
if not reason :
await ctx . message . reply ( " Please provide a reason! " )
return
2023-06-21 12:06:24 -04:00
await target . timeout ( parsed_time )
response = await ctx . message . reply ( f " { target . mention } has been timed out for { str ( parsed_time ) } ! \n **Reason** - ` { reason } ` " )
try :
2023-09-24 08:09:25 -04:00
embed = CustomEmbed ( title = " Timed Out " , description = f " You have been timed out for ` { str ( parsed_time ) } ` in { ctx . server . name } . \n ### Reason \n ` { reason } ` " , color = " #5d82d1 " )
await target . send ( embed = embed )
2023-06-21 12:11:06 -04:00
except revolt . errors . HTTPError :
2023-06-21 12:06:24 -04:00
await response . edit ( content = f " { response . content } \n *Failed to send DM, user likely has the bot blocked.* " )
Moderation . mysql_log ( self , ctx , moderation_type = ' Timeout ' , target_id = target . id , duration = parsed_time , reason = reason )
2023-06-21 09:20:16 -04:00
2023-06-21 15:33:19 -04:00
@commands.command ( name = " untimeout " , aliases = [ " unmute " ] )
2023-09-24 08:09:25 -04:00
async def untimeout ( self , ctx : commands . Context , target : commands . MemberConverter , * , reason : str = " No reason provided. " ) :
2023-06-23 10:42:48 -04:00
required_role = utils . get ( ctx . server . roles , id = required_role_id )
if required_role not in ctx . author . roles :
await ctx . message . reply ( " You do not have permission to use this command! " )
return
2023-06-21 15:33:19 -04:00
parsed_time = parse ( sval = " 0s " , as_timedelta = True , raise_exception = True )
await target . timeout ( parsed_time )
response = await ctx . message . reply ( f " { target . mention } has had their timeout removed! \n **Reason** - ` { reason } ` " )
try :
2023-09-24 08:09:25 -04:00
embed = CustomEmbed ( title = " Timeout Removed " , description = f " Your timeout has been removed in { ctx . server . name } . \n ### Reason \n ` { reason } ` " , color = " #5d82d1 " )
await target . send ( embed = embed )
2023-06-21 15:33:19 -04:00
except revolt . errors . HTTPError :
await response . edit ( content = f " { response . content } \n *Failed to send DM, user likely has the bot blocked.* " )
2023-09-24 08:09:25 -04:00
Moderation . mysql_log ( self , ctx , moderation_type = ' Untimeout ' , target_id = target . id , duration = ' NULL ' , reason = reason )
2023-06-21 15:33:19 -04:00
2023-06-21 09:20:16 -04:00
@commands.command ( )
async def warn ( self , ctx : commands . Context , target : commands . MemberConverter , * , reason : str ) :
2023-06-23 10:42:48 -04:00
required_role = utils . get ( ctx . server . roles , id = required_role_id )
if required_role not in ctx . author . roles :
await ctx . message . reply ( " You do not have permission to use this command! " )
return
2023-06-21 09:20:16 -04:00
if not reason :
await ctx . message . reply ( " Please include a reason! " )
return
2023-06-21 12:06:24 -04:00
response = await ctx . message . reply ( f " { target . mention } has been warned! \n **Reason** - ` { reason } ` " )
try :
2023-09-24 08:09:25 -04:00
embed = CustomEmbed ( title = " Warned " , description = f " You have been warned in { ctx . server . name } ! \n ### Reason \n ` { reason } ` " , color = " #5d82d1 " )
await target . send ( embed = embed )
2023-06-21 12:11:06 -04:00
except revolt . errors . HTTPError :
2023-06-21 12:06:24 -04:00
await response . edit ( content = f " { response . content } \n *Failed to send DM, user likely has the bot blocked.* " )
Moderation . mysql_log ( self , ctx , moderation_type = ' Warning ' , target_id = target . id , duration = ' NULL ' , reason = reason )
2023-06-21 09:20:16 -04:00
@commands.command ( )
2023-06-21 13:33:45 -04:00
async def kick ( self , ctx : commands . Context , target : commands . MemberConverter , * , reason : str ) :
2023-06-23 10:42:48 -04:00
required_role = utils . get ( ctx . server . roles , id = required_role_id )
if required_role not in ctx . author . roles :
await ctx . message . reply ( " You do not have permission to use this command! " )
return
2023-06-21 13:33:45 -04:00
if not reason :
await ctx . message . reply ( " Please include a reason! " )
return
2023-07-01 10:17:33 -04:00
try :
await target . kick ( )
except revolt . errors . HTTPError :
await ctx . message . reply ( " User is not in the server! " )
return
2023-06-21 13:33:45 -04:00
response = await ctx . message . reply ( f " { target . mention } has been kicked! \n **Reason** - ` { reason } ` " )
try :
2023-09-24 08:09:25 -04:00
embed = CustomEmbed ( title = " Warned " , description = f " You have been kicked from { ctx . server . name } ! \n ### Reason \n ` { reason } ` " , color = " #5d82d1 " )
await target . send ( embed = embed )
2023-06-21 13:33:45 -04:00
except revolt . errors . HTTPError :
await response . edit ( content = f " { response . content } \n *Failed to send DM, user likely has the bot blocked.* " )
Moderation . mysql_log ( self , ctx , moderation_type = ' Kick ' , target_id = target . id , duration = ' NULL ' , reason = reason )
@commands.command ( )
2023-06-21 09:20:16 -04:00
async def ban ( self , ctx : commands . Context , target : commands . MemberConverter , * , reason : str ) :
2023-06-23 10:42:48 -04:00
required_role = utils . get ( ctx . server . roles , id = required_role_id )
if required_role not in ctx . author . roles :
await ctx . message . reply ( " You do not have permission to use this command! " )
return
2023-06-21 09:20:16 -04:00
if not reason :
await ctx . message . reply ( " Please include a reason! " )
return
try :
await target . ban ( reason = reason )
2023-06-21 12:06:24 -04:00
response = await ctx . message . reply ( f " { target . mention } has been banned! \n **Reason** - ` { reason } ` " )
try :
2023-09-24 08:09:25 -04:00
embed = CustomEmbed ( title = " Banned " , description = f " You have been banned from ` { ctx . server . name } `. \n ### Reason \n ` { reason } ` " , color = " #5d82d1 " )
await target . send ( embed = embed )
2023-06-21 12:11:06 -04:00
except revolt . errors . HTTPError :
2023-06-21 12:06:24 -04:00
await response . edit ( content = f " { response . content } \n *Failed to send DM, user likely has the bot blocked.* " )
Moderation . mysql_log ( self , ctx , moderation_type = ' Ban ' , target_id = target . id , duration = ' NULL ' , reason = reason )
2023-06-21 09:20:16 -04:00
except revolt . errors . HTTPError :
await ctx . message . reply ( f " { target . mention } is already banned! " )
@commands.command ( )
2023-06-21 12:06:24 -04:00
async def unban ( self , ctx : commands . Context , target : commands . UserConverter , * , reason : str ) :
2023-06-23 10:42:48 -04:00
required_role = utils . get ( ctx . server . roles , id = required_role_id )
if required_role not in ctx . author . roles :
await ctx . message . reply ( " You do not have permission to use this command! " )
return
2023-06-21 12:06:24 -04:00
if ctx . channel . channel_type is not revolt . ChannelType . text_channel :
await ctx . message . reply ( " You cannot use moderation commands in direct messages! " )
return
if not reason :
await ctx . message . reply ( " Please include a reason! " )
2023-06-21 09:20:16 -04:00
return
2023-06-21 12:06:24 -04:00
bans = await ctx . server . fetch_bans ( )
for ban in bans :
if ban . user_id == target . id :
await ban . unban ( )
response = await ctx . message . reply ( f " { target . mention } has been unbanned! \n **Reason** - ` { reason } ` " )
try :
2023-09-24 08:09:25 -04:00
embed = CustomEmbed ( title = " Unbanned " , description = f " You have been unbanned from ` { ctx . server . name } `. \n ### Reason \n ` { reason } ` " , color = " #5d82d1 " )
await target . send ( embed = embed )
2023-06-21 12:11:06 -04:00
except revolt . errors . HTTPError :
2023-06-21 12:06:24 -04:00
await response . edit ( content = f " { response . content } \n *Failed to send DM, user likely has the bot blocked.* " )
Moderation . mysql_log ( self , ctx , moderation_type = ' Unban ' , target_id = target . id , duration = ' NULL ' , reason = str ( reason ) )
return
await ctx . message . reply ( f " { target . mention } is not banned! " )
2023-06-21 09:20:16 -04:00
2023-06-20 20:07:13 -04:00
@commands.command ( aliases = [ " tdc " ] )
2023-09-24 16:33:13 -04:00
async def timedeltaconvert ( self , ctx : commands . Context , * , duration : str ) :
2023-06-23 10:42:48 -04:00
required_role = utils . get ( ctx . server . roles , id = required_role_id )
if required_role not in ctx . author . roles :
await ctx . message . reply ( " You do not have permission to use this command! " )
return
2023-06-20 20:07:13 -04:00
if not duration :
2023-06-21 17:41:12 -04:00
embeds = [ CustomEmbed ( description = f " ## timedeltaconvert \n This command converts a duration to a `timedelta` Python object. \n ### Example Usage \n ` { prefix } timedeltaconvert 1 day 15hr 82 minutes 52 s` \n ### Output \n `1 day, 16:22:52` " , color = " #5d82d1 " ) ]
2023-06-20 20:07:13 -04:00
await ctx . message . reply ( embeds = embeds )
else :
try :
parsed_time = parse ( duration , as_timedelta = True , raise_exception = True )
await ctx . message . reply ( f " ` { str ( parsed_time ) } ` " )
except ValueError :
await ctx . message . reply ( " Please provide a convertible value! " )