2021-05-10 22:09:03 +02:00
const {
InfractionTargetTypes ,
InfractionDictionary ,
InfractionColors
} = require ( '../../../util/Constants.js' ) ;
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 {
2021-05-10 22:09:03 +02:00
constructor ( client , data = { } ) {
2020-04-17 09:03:53 +02:00
this . client = client ;
2021-05-10 22:09:03 +02:00
this . type = data . type || null ;
this . id = null ;
this . case = null ;
2020-04-17 09:03:53 +02:00
2021-05-10 22:09:03 +02:00
this . message = data . message ; //NOT REQUIRED
this . arguments = data . arguments || { } ;
2020-04-17 09:03:53 +02:00
2021-05-10 22:09:03 +02:00
this . guildId = data . guild ? data . guild . id : null ;
this . guild = data . guild || null ;
2020-04-17 09:03:53 +02:00
2021-05-10 22:09:03 +02:00
this . channelId = data . channel ? data . channel . id : null ;
this . channel = data . channel || null ;
this . messageId = data . message ? data . message . id : null ;
this . message = data . message || null ;
this . targetId = data . target ? data . target . id : null ;
2021-06-17 00:51:42 +02:00
this . target = data . target || null ;
2021-05-10 22:09:03 +02:00
this . executorId = data . executor ? data . executor . id : null ;
this . executor = data . executor || null ;
this . targetType = data . targetType && InfractionTargetTypes . includes ( data . targetType ) ? data . targetType : 'USER' ;
this . resolved = false ;
2020-06-04 19:59:09 +02:00
2021-05-10 22:09:03 +02:00
this . duration = isNaN ( data . duration ) ? null : data . duration ; //How long the action will last. Must be in milliseconds.
this . callback = isNaN ( data . duration ) ? null : Date . now ( ) + data . duration * 1000 ; //At what epoch(?) time it will callback.
2020-06-04 19:59:09 +02:00
2021-06-18 06:39:48 +02:00
this . reason = data . reason || 'N/A' ;
2020-06-02 12:09:28 +02:00
2021-05-10 22:09:03 +02:00
this . points = data . points || 0 ;
2021-06-21 09:48:11 +02:00
this . expiration = isNaN ( data . expiration ) ? null : Date . now ( ) + data . expiration * 1000 ;
2020-06-04 19:59:09 +02:00
this . totalPoints = 0 ;
2020-04-17 09:03:53 +02:00
2021-05-10 22:09:03 +02:00
this . data = data . data || { } ; //Miscellaneous data that may need to be saved for future use.
this . flags = data . arguments ? Object . keys ( data . arguments ) : [ ] ;
this . hyperlink = data . hyperlink || null ; // To overwrite hyperlink (if it's from a callback)
this . modlogMessageId = null ;
this . dmlogMessageId = null ;
this . modlogId = null ;
2020-06-02 12:09:28 +02:00
2020-08-09 19:06:04 +02:00
this . changes = [ ] ;
2020-04-17 09:03:53 +02:00
2021-05-10 22:09:03 +02:00
this . timestamp = Date . now ( ) ;
this . _callbacked = Boolean ( data . _callbacked ) ;
this . _fetched = Boolean ( data ) ;
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
2021-06-10 08:10:42 +02:00
//NOTE: Temporary logging, making sure there isn't any other issues.
if ( typeof this . reason !== 'string' ) this . client . logger . error ( ` Infraction type ${ this . type } was passed an invalid type to the reason. ` ) ;
2021-05-10 22:09:03 +02:00
const { moderationLog , dmInfraction } = await this . guild . settings ( ) ;
2020-06-04 19:59:09 +02:00
2021-05-10 22:09:03 +02:00
//Increment CaseId, should only be called if making a new infraction.
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
2021-05-10 22:09:03 +02:00
} ) ;
2020-06-04 19:59:09 +02:00
/* Logging */
2021-05-10 22:09:03 +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 ;
2021-05-10 22:09:03 +02:00
this . modlogId = this . _moderationLog . id ;
try {
this . _logMessage = await this . _moderationLog . send ( '' , { embed : this . _embed ( ) } ) ;
this . modlogMessageId = this . _logMessage . id ;
2021-06-09 00:43:00 +02:00
} catch ( e ) { } //eslint-disable-line no-empty
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. ` ) ;
}
}
2021-05-10 22:09:03 +02:00
if ( dmInfraction . enabled ) {
if ( this . targetType === 'USER' ) {
let message = dmInfraction . messages [ this . type ] || dmInfraction . messages . default ;
if ( ! message ) message = '' ;
message = message
. replace ( /\{(guild|server)\}/ugim , this . guild . name )
. replace ( /\{user\}/ugim , this . target . tag )
. 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...
try {
const logMessage = await this . target . send ( message , {
embed : this . _embed ( true )
} ) ;
this . dmlogMessageId = logMessage . id ;
} catch ( e ) { } //eslint-disable-line no-empty
}
2020-06-02 12:09:28 +02:00
}
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 */
2021-06-12 09:12:40 +02:00
if ( this . data . roles ) {
2021-05-10 22:09:03 +02:00
delete this . data . roles ;
2020-07-16 09:54:39 +02:00
}
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-08-08 00:21:28 +02:00
return this . client . storageManager . mongodb . infractions . insertOne ( this . json )
. catch ( ( error ) => {
this . client . logger . error ( ` There was an issue saving infraction data to the database. \n ${ error . stack || error } ` ) ;
} ) ;
2020-06-02 12:09:28 +02:00
}
2021-05-10 22:09:03 +02:00
hyperlink ( bool = false ) {
if ( bool ) return ` https://discord.com/channels/ ${ this . guildId } / ${ this . modlogId } / ${ this . modlogMessageId } ` ;
return ` https://discord.com/channels/ ${ this . guildId } / ${ this . channelId } / ${ this . messageId } ` ;
}
2020-06-02 12:09:28 +02:00
2021-05-10 22:09:03 +02:00
_embed ( dm ) {
let description = "" ;
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 } )
2021-05-10 22:09:03 +02:00
} ) } ` ;
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
}
2021-05-10 22:09:03 +02:00
if ( this . points && this . points > 0 ) { //TODO: Add expiration to INFRACTION_DESCRIPTIONPOINTS
2021-06-12 09:34:23 +02:00
description += ` \n ${ this . guild . format ( 'INFRACTION_DESCRIPTIONPOINTS' , { points : this . points , total : this . totalPoints , expires : Util . duration ( this . pointsExpiration ) } )} ` ;
2020-07-04 12:23:10 +02:00
}
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
}
2021-06-26 08:42:57 +02:00
if ( ! this . silent && ( this . message || this . hyperlink ) ) {
2021-05-10 22:09:03 +02:00
description += ` \n ${ this . guild . format ( 'INFRACTION_DESCRIPTIONJUMPTO' , { name : this . hyperlink ? 'Case' : 'Message' , link : this . hyperlink ? this . hyperlink : ` https://discord.com/channels/ ${ this . guildId } / ${ this . channelId } / ${ this . messageId } ` } )} ` ;
2020-06-02 12:09:28 +02:00
}
2021-05-10 22:09:03 +02:00
const blah = {
2020-06-02 12:09:28 +02:00
author : {
2021-05-10 22:09:03 +02:00
name : ` ${ this . target . display } ( ${ this . targetId } ) ` ,
2021-06-12 09:12:40 +02:00
icon _url : this . targetIcon //eslint-disable-line camelcase
2020-06-02 12:09:28 +02:00
} ,
2021-05-10 22:09:03 +02:00
timestamp : this . timestamp ,
2020-06-02 12:09:28 +02:00
color : this . color ,
footer : {
text : ` 》 Case ${ this . case } `
} ,
description
} ;
2021-05-10 22:09:03 +02:00
return blah ;
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 {
2021-05-10 22:09:03 +02:00
id : ` ${ this . guildId } : ${ this . case } ` ,
guild : this . guildId ,
channel : this . channelId ,
2021-06-18 06:39:48 +02:00
channelName : this . channel ? . display ,
2021-05-10 22:09:03 +02:00
message : this . messageId ,
executor : this . executorId ,
executorTag : this . executor . display ,
target : this . targetId ,
targetTag : this . target . display ,
2020-04-17 09:03:53 +02:00
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-16 00:15:13 +02:00
data : this . data ,
2021-05-10 22:09:03 +02:00
flags : this . flags ,
2021-06-12 09:34:23 +02:00
points : this . points ,
expiration : this . expiration ,
2021-06-10 08:10:42 +02:00
modLogMessage : this . modlogMessageId ,
2021-05-10 22:09:03 +02:00
dmLogMessage : this . dmlogMessageId ,
modLogChannel : this . modlogId ,
2020-08-09 19:06:04 +02:00
resolved : this . resolved ,
2021-05-10 22:09:03 +02:00
changes : this . changes ,
_callbacked : this . _callbacked || false
2020-04-17 22:18:29 +02:00
} ;
2020-04-17 09:03:53 +02:00
}
2020-06-02 12:09:28 +02:00
get targetIcon ( ) {
2021-05-10 22:09:03 +02:00
return this . targetType === 'USER'
2020-06-02 12:09:28 +02:00
? 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 ;
}
2021-05-10 22:09:03 +02:00
get dictionary ( ) {
return InfractionDictionary [ this . type ] ;
}
get color ( ) {
return InfractionColors [ this . type ] ;
}
2020-06-04 19:59:09 +02:00
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 ( ) ;
}
2020-07-28 13:11:48 +02:00
async _verify ( ) {
2021-05-10 22:25:35 +02:00
const { protection } = await this . guild . settings ( ) ;
2020-07-28 13:11:48 +02:00
if ( this . executor . id === this . guild . ownerID ) return this . _succeed ( ) ;
2021-05-10 22:25:35 +02:00
if ( this . guild && protection . enabled && this . targetType === 'USER' ) {
2021-06-11 07:08:41 +02:00
const executor = await this . guild . members . fetch ( this . executor . id ) . catch ( ( ) => { } ) ; //eslint-disable-line no-empty-function
const target = await this . guild . members . fetch ( this . target . id ) . catch ( ( ) => { } ) ; //eslint-disable-line no-empty-function
2020-07-28 13:11:48 +02:00
if ( ! target ) return this . _succeed ( ) ;
2021-05-10 22:25:35 +02:00
if ( protection . type === 'position' ) {
const executorHighest = executor . roles . highest ;
const targetHighest = target . roles . highest ;
if ( executorHighest . comparePositionTo ( targetHighest ) < 0 ) {
return this . _fail ( 'INFRACTION_PROTECTIONPOSITIONERROR' ) ;
}
} else if ( protection . type === 'role' ) {
const contains = target . roles . cache . some ( ( r ) => protection . roles . includes ( r . id ) ) ;
if ( contains ) {
return this . _fail ( 'INFRACTION_PROTECTIONROLEERROR' ) ;
}
2020-07-16 09:54:39 +02:00
}
}
return this . _succeed ( ) ;
}
2021-05-10 22:09:03 +02:00
// async fetch() { //Data from Mongodb (id-based data)
// const data = await this.client.storageManager.mongodb.infractions.findOne({ id: this.id });
// if(!data) {
// this.client.logger.error(`Case ${this.id} is missing infraction data in database.`);
// return null;
// }
// if(data.guild) {
// let guild = null;
// try {
// guild = await this.client.guilds.fetch(data.guild);
// } catch(error) {
// this.client.logger.error(`Unable to fetch guild: ${data.guild}\n${error.stack || error}`);
// guild = null;
// }
// if(!guild) return null;
// if(data.targets) {
// this.targetIds = data.targets;
// for(const target of data.targets) {
// const fetchedTarget = await this._fetchTarget(target);
// if(fetchedTarget) this.targets.push(fetchedTarget);
// }
// }
// if(data.executor) {
// this.executorId = data.executor;
// const fetchedExecutor = await this._fetchTarget(data.executor, 'USER');
// if(fetchedExecutor) this.executor = fetchedExecutor;
// }
// }
// this.type = data.type;
// this.timestamp = data.timestamp;
// this.duration = data.duration;
// this.reason = data.reason;
// this.channelId = data.channel;
// this.resolved = data.resolved;
// this._callbacked = data._callbacked;
// this.dictionary = InfractionDictionary[this.type];
// this.color = InfractionColors[this.type];
// this.modlogMessageId = data.modlogMessageId;
// this.dmlogMessageId = data.dmlogMessageId;
// this._fetched = Boolean(data._fetched);
// return this;
// }
// async _fetchTarget(target, type = null) {
// type = type || this.targetType;
// let fetched = null;
// if(type === 'CHANNEL') {
// fetched = await this.client.resolver.resolveChannel(target, true, this.guild);
// } else if (type) {
// fetched = await this.client.resolver.resolveMember(target, true, this.guild);
// if(!fetched) {
// fetched = await this.client.resolver.resolveUser(target, true);
// }
// }
// return fetched || null;
// }
2020-04-17 09:03:53 +02:00
}
module . exports = Infraction ;