2020-06-04 19:59:09 +02:00
const { Util } = require ( '../../../util/' ) ;
const Constants = {
2020-07-20 00:42:21 +02:00
MaxCharacters : 1024 , // Max embed description is 2048 characters, however some of those description characters are going to usernames, types, filler text, etc.
RemovedInfractions : [ 'BAN' , 'SOFTBAN' , 'KICK' ]
2020-06-04 19:59:09 +02:00
} ;
2020-04-17 09:03:53 +02:00
class Infraction {
constructor ( client , opts = { } ) {
this . client = client ;
2020-07-04 12:23:10 +02:00
this . message = opts . message ; //NOT REQUIRED
this . arguments = opts . arguments || { } ;
2020-04-17 09:03:53 +02:00
2020-07-04 12:23:10 +02:00
this . target = opts . target ; //User or channel being targeted. User object, not GuildMember.
2020-04-17 22:18:29 +02:00
this . targetType = opts . targetType ; // 'user' or 'channel'.
2020-07-04 12:23:10 +02:00
this . executor = opts . executor ; //Whoever executed the command. User object, not GuildMember.
2020-04-17 09:03:53 +02:00
this . guild = opts . guild ;
this . channel = opts . channel ;
2020-07-04 12:23:10 +02:00
this . case = null ;
2020-04-17 09:03:53 +02:00
this . type = opts . type ; //What type of infraction (mute, kick, etc.)
2020-06-16 00:15:13 +02:00
this . timestamp = Date . now ( ) ;
this . duration = isNaN ( opts . duration ) ? null : opts . duration ; //How long the action will last. Must be in milliseconds.
2020-07-04 12:23:10 +02:00
this . callback = isNaN ( opts . duration ) ? null : Date . now ( ) + opts . duration * 1000 ; //At what epoch(?) time it will callback.
2020-06-04 19:59:09 +02:00
this . reason = opts . reason . length ? opts . reason : 'N/A' ;
this . silent = opts . silent ;
2020-06-02 12:09:28 +02:00
2020-07-16 09:54:39 +02:00
this . points = opts . points || 0 ;
2020-06-04 19:59:09 +02:00
this . totalPoints = 0 ;
2020-07-16 09:54:39 +02:00
this . expiration = opts . expiration || 0 ;
2020-04-17 09:03:53 +02:00
2020-07-04 12:23:10 +02:00
this . data = opts . data || { } ; //Miscellaneous data that may need to be saved for future use.
2020-04-17 09:03:53 +02:00
this . color = opts . color ; //Infraction-defined hexadecimal value to dictate what color the embed is.
2020-06-02 12:09:28 +02:00
this . dictionary = opts . dictionary || { } ; // { past: 'banned', present: 'ban' } Infraction-defined object for the correct spellings.
2020-07-04 12:23:10 +02:00
this . hyperlink = opts . hyperlink || null ; // To overwrite hyperlink (if it's from a callback)
2020-06-02 12:09:28 +02:00
2020-06-04 19:59:09 +02:00
this . _logMessage = null ; //The message embed sent in the moderation-log. Full message, not the ID.
2020-06-16 00:15:13 +02:00
this . _moderationLog = null ; //Moderation log channel
2020-04-17 09:03:53 +02:00
}
2020-07-04 12:23:10 +02:00
async handle ( ) {
2020-04-17 09:03:53 +02:00
2020-07-16 09:54:39 +02:00
const { moderationLog } = this . guild . _settings ;
2020-06-04 19:59:09 +02:00
this . guild . _settings . caseId ++ ;
this . case = this . guild . _settings . caseId ;
await this . guild . _updateSettings ( {
caseId : this . case
2020-07-04 12:23:10 +02:00
} ) ;
2020-06-04 19:59:09 +02:00
/* Logging */
2020-07-04 12:23:10 +02:00
2020-06-02 12:09:28 +02:00
if ( moderationLog . channel ) {
2020-07-04 12:23:10 +02:00
if ( moderationLog . infractions . includes ( this . type ) ) {
2020-06-19 15:38:56 +02:00
this . _moderationLog = await this . client . resolver . resolveChannel ( moderationLog . channel , true , this . guild ) ;
2020-06-16 00:15:13 +02:00
if ( ! this . _moderationLog ) return undefined ;
this . _logMessage = await this . _moderationLog . send ( '' , { embed : this . embed ( ) } ) ;
2020-06-02 12:09:28 +02:00
} else {
this . client . logger . debug ( ` Did not log infraction ${ this . type } because it is not in the infractions. ` ) ;
}
}
2020-06-04 19:59:09 +02:00
if ( this . guild . _settings . dmInfraction . enabled
2020-07-04 12:23:10 +02:00
&& this . targetType === 'user'
&& this . type !== 'NOTE' ) {
2020-06-04 19:59:09 +02:00
let message = this . guild . _settings . dmInfraction . custom [ this . type ] || this . guild . _settings . dmInfraction . custom . default ;
if ( ! message ) message = "" ;
message = message
. replace ( /\{(guild|server)\}/ugim , this . guild . name )
. replace ( /\{user\}/ugim , this . target . tag )
2020-07-20 00:42:21 +02:00
. replace ( /\{infraction\}/ugim , this . dictionary . past )
. replace ( /\{from\|on\}/ugim , Constants . RemovedInfractions . includes ( this . type ) ? 'from' : 'on' ) ; //add more if you want i should probably add a better system for this...
2020-06-02 12:09:28 +02:00
try {
2020-07-16 09:54:39 +02:00
this . target . send ( message , {
embed : this . embed ( true )
2020-06-02 12:09:28 +02:00
} ) ;
} catch ( e ) { } //eslint-disable-line no-empty
}
2020-07-04 12:23:10 +02:00
if ( this . duration ) {
await this . client . moderationManager . _handleExpirations ( [ this . json ] ) ;
}
2020-06-04 19:59:09 +02:00
2020-07-16 09:54:39 +02:00
/* LMAOOOO PLEASE DONT JUDGE ME */
if ( this . data . roles && this . data . roles . length > 0 ) {
this . data . roles = this . data . roles . map ( ( r ) => r . id ) ;
}
2020-06-04 19:59:09 +02:00
return this . save ( ) ;
2020-04-17 09:03:53 +02:00
}
2020-04-17 22:18:29 +02:00
async save ( ) {
2020-07-04 12:23:10 +02:00
return this . client . transactionHandler . send ( {
2020-04-17 22:18:29 +02:00
provider : 'mongodb' ,
request : {
type : 'insertOne' ,
2020-06-04 19:59:09 +02:00
collection : 'infractions' ,
2020-06-02 12:09:28 +02:00
data : this . json
2020-04-17 22:18:29 +02:00
}
2020-06-02 12:09:28 +02:00
} ) . catch ( ( error ) => {
this . client . logger . error ( ` There was an issue saving infraction data to the database. \n ${ error . stack || error } ` ) ;
} ) ;
}
2020-07-16 09:54:39 +02:00
embed ( dm = false ) {
2020-06-02 12:09:28 +02:00
2020-07-04 12:23:10 +02:00
let description = this . guild . format ( 'INFRACTION_DESCRIPTION' , {
2020-06-04 19:59:09 +02:00
type : this . dictionary . past . toUpperCase ( ) ,
moderator : ` ${ Util . escapeMarkdown ( this . executor . tag ) } ` ,
2020-07-16 09:54:39 +02:00
reason : Util . escapeMarkdown ( this . reason . length > Constants . MaxCharacters ? ` ${ this . reason . substring ( 0 , Constants . MaxCharacters - 3 ) } ... ` : this . reason , { italic : false , underline : false , strikethrough : false } )
2020-06-02 12:09:28 +02:00
} ) ;
if ( this . duration ) {
2020-07-04 12:23:10 +02:00
description += ` \n ${ this . guild . format ( 'INFRACTION_DESCRIPTIONDURATION' , { duration : Util . duration ( this . duration ) } )} ` ;
2020-06-02 12:09:28 +02:00
}
2020-07-16 09:54:39 +02:00
if ( this . points > 0 ) {
2020-07-04 12:23:10 +02:00
description += ` \n ${ this . guild . format ( 'INFRACTION_DESCRIPTIONPOINTS' , { points : this . points , total : this . totalPoints } )} ` ;
}
if ( this . description && this . description instanceof Function ) {
2020-07-16 09:54:39 +02:00
description += this . description ( dm ) ;
2020-06-04 19:59:09 +02:00
}
2020-07-20 00:42:21 +02:00
if ( ( ! this . silent && ( this . message || this . hyperlink ) ) && ( dm && ! Constants . RemovedInfractions . includes ( this . type ) ) ) {
2020-07-04 12:23:10 +02:00
description += ` \n ${ this . guild . format ( 'INFRACTION_DESCRIPTIONJUMPTO' , { name : this . hyperlink ? 'Case' : 'Message' , link : this . hyperlink ? this . hyperlink : ` https://discord.com/channels/ ${ this . guild . id } / ${ this . channel . id } / ${ this . message . id } ` } )} ` ;
2020-06-02 12:09:28 +02:00
}
return {
author : {
name : ` ${ this . targetName } ( ${ this . target . id } ) ` ,
icon _url : this . targetIcon //eslint-disable-line camelcase
} ,
timestamp : new Date ( ) ,
color : this . color ,
footer : {
text : ` 》 Case ${ this . case } `
} ,
description
} ;
2020-04-17 22:18:29 +02:00
}
2020-06-02 12:09:28 +02:00
get json ( ) {
2020-04-17 09:03:53 +02:00
return {
id : ` ${ this . guild . id } : ${ this . case } ` ,
guild : this . guild . id ,
channel : this . channel ? this . channel . id : null ,
message : this . message ? this . message . id : null ,
executor : this . executor . id ,
target : this . target . id ,
targetType : this . targetType ,
type : this . type ,
case : this . case ,
2020-07-04 12:23:10 +02:00
timestamp : this . timestamp ,
2020-04-17 09:03:53 +02:00
duration : this . duration ,
2020-07-04 12:23:10 +02:00
callback : this . callback ,
2020-04-17 09:03:53 +02:00
reason : this . reason ,
2020-06-04 19:59:09 +02:00
points : this . points ,
2020-06-16 00:15:13 +02:00
expiration : this . expiration ,
data : this . data ,
actions : this . actions ,
logMessage : this . _logMessage ? this . _logMessage . id : null , //the message id sent in modlog channel
moderationLog : this . _moderationLog ? this . _moderationLog . id : null ,
2020-07-04 12:23:10 +02:00
callbacked : false
2020-04-17 22:18:29 +02:00
} ;
2020-04-17 09:03:53 +02:00
}
get targetName ( ) {
return this . targetType === 'user'
? this . target . tag
2020-04-17 22:18:29 +02:00
: ` # ${ this . target . name } ` ;
2020-04-17 09:03:53 +02:00
}
2020-06-02 12:09:28 +02:00
get targetIcon ( ) {
return this . targetType === 'user'
? this . target . displayAvatarURL ( )
: this . guild . iconURL ( ) ;
}
2020-06-16 00:15:13 +02:00
get actions ( ) {
const actions = [ ] ;
for ( const argument of Object . values ( this . arguments ) ) {
if ( [ 'silent' , 'prune' , 'force' ] . includes ( argument ) ) actions . push ( argument ) ;
}
return actions ;
}
2020-06-04 19:59:09 +02:00
get _reason ( ) {
2020-07-04 12:23:10 +02:00
let str = ` [ ${ this . type } ][targetId: ${ this . target . id } ] Executed by ${ this . executor . tag } ( ${ this . executor . id } ) because: ${ this . reason } ` ;
2020-06-04 19:59:09 +02:00
if ( str . length > 512 ) str = ` ${ this . reason . substring ( 0 , 509 ) } ... ` ;
return str ;
}
2020-06-02 12:09:28 +02:00
//Super Functions
_succeed ( ) {
return {
error : false ,
infraction : this
} ;
}
2020-06-16 00:15:13 +02:00
_fail ( message , fatal = false ) {
2020-06-02 12:09:28 +02:00
return {
error : true ,
infraction : this ,
2020-06-16 00:15:13 +02:00
reason : message ,
fatal
2020-06-02 12:09:28 +02:00
} ;
}
2020-07-16 09:54:39 +02:00
verify ( ) {
return this . _verify ( ) ;
}
_verify ( ) {
2020-07-20 00:42:21 +02:00
if ( this . guild && this . guild . _settings . protection . enabled ) {
2020-07-16 09:54:39 +02:00
const executorHighest = this . executor . roles . highest ;
const targetHighest = this . member . roles . highest ;
if ( executorHighest . comparePositionTo ( targetHighest ) > 0 ) {
return this . _fail ( 'INFRACTION_PROTECTIONERROR' ) ;
}
}
return this . _succeed ( ) ;
}
2020-04-17 09:03:53 +02:00
}
module . exports = Infraction ;