forked from Galactic/galactic-bot
242 lines
9.1 KiB
JavaScript
242 lines
9.1 KiB
JavaScript
const { Util } = require('../../../util/');
|
|
|
|
const Constants = {
|
|
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']
|
|
};
|
|
|
|
class Infraction {
|
|
|
|
constructor(client, opts = {}) {
|
|
|
|
this.client = client;
|
|
this.message = opts.message; //NOT REQUIRED
|
|
this.arguments = opts.arguments || {};
|
|
|
|
this.target = opts.target; //User or channel being targeted. User object, not GuildMember.
|
|
this.targetType = opts.targetType; // 'user' or 'channel'.
|
|
this.executor = opts.executor; //Whoever executed the command. User object, not GuildMember.
|
|
|
|
this.guild = opts.guild;
|
|
this.channel = opts.channel;
|
|
|
|
this.case = null;
|
|
this.type = opts.type; //What type of infraction (mute, kick, etc.)
|
|
|
|
this.timestamp = Date.now();
|
|
this.duration = isNaN(opts.duration) ? null : opts.duration; //How long the action will last. Must be in milliseconds.
|
|
this.callback = isNaN(opts.duration) ? null : Date.now()+opts.duration*1000; //At what epoch(?) time it will callback.
|
|
|
|
this.reason = opts.reason.length ? opts.reason : 'N/A';
|
|
|
|
this.silent = opts.silent;
|
|
|
|
this.points = opts.points || 0;
|
|
this.totalPoints = 0;
|
|
this.expiration = opts.expiration || 0;
|
|
|
|
this.data = opts.data || {}; //Miscellaneous data that may need to be saved for future use.
|
|
this.color = opts.color; //Infraction-defined hexadecimal value to dictate what color the embed is.
|
|
this.dictionary = opts.dictionary || {}; // { past: 'banned', present: 'ban' } Infraction-defined object for the correct spellings.
|
|
this.hyperlink = opts.hyperlink || null; // To overwrite hyperlink (if it's from a callback)
|
|
|
|
this._logMessage = null; //The message embed sent in the moderation-log. Full message, not the ID.
|
|
this._moderationLog = null; //Moderation log channel
|
|
|
|
}
|
|
|
|
async handle() {
|
|
|
|
const { moderationLog } = this.guild._settings;
|
|
|
|
this.guild._settings.caseId++;
|
|
this.case = this.guild._settings.caseId;
|
|
await this.guild._updateSettings({
|
|
caseId: this.case
|
|
});
|
|
|
|
|
|
/* Logging */
|
|
|
|
if(moderationLog.channel) {
|
|
if(moderationLog.infractions.includes(this.type)) {
|
|
this._moderationLog = await this.client.resolver.resolveChannel(moderationLog.channel, true, this.guild);
|
|
if(!this._moderationLog) return undefined;
|
|
this._logMessage = await this._moderationLog.send('', { embed: this.embed() });
|
|
} else {
|
|
this.client.logger.debug(`Did not log infraction ${this.type} because it is not in the infractions.`);
|
|
}
|
|
}
|
|
|
|
if(this.guild._settings.dmInfraction.enabled
|
|
&& this.targetType === 'user'
|
|
&& this.type !== 'NOTE') {
|
|
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)
|
|
.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 {
|
|
this.target.send(message, {
|
|
embed: this.embed(true)
|
|
});
|
|
} catch(e) {} //eslint-disable-line no-empty
|
|
}
|
|
|
|
if(this.duration) {
|
|
await this.client.moderationManager._handleExpirations([this.json]);
|
|
}
|
|
|
|
/* LMAOOOO PLEASE DONT JUDGE ME */
|
|
if(this.data.roles && this.data.roles.length > 0) {
|
|
this.data.roles = this.data.roles.map((r) => r.id);
|
|
}
|
|
|
|
return this.save();
|
|
|
|
}
|
|
|
|
async save() {
|
|
return this.client.transactionHandler.send({
|
|
provider: 'mongodb',
|
|
request: {
|
|
type: 'insertOne',
|
|
collection: 'infractions',
|
|
data: this.json
|
|
}
|
|
}).catch((error) => {
|
|
this.client.logger.error(`There was an issue saving infraction data to the database.\n${error.stack || error}`);
|
|
});
|
|
}
|
|
|
|
embed(dm = false) {
|
|
|
|
let description = this.guild.format('INFRACTION_DESCRIPTION', {
|
|
type: this.dictionary.past.toUpperCase(),
|
|
moderator: `${Util.escapeMarkdown(this.executor.tag)}`,
|
|
reason: Util.escapeMarkdown(this.reason.length > Constants.MaxCharacters ? `${this.reason.substring(0, Constants.MaxCharacters-3)}...` : this.reason, { italic: false, underline: false, strikethrough: false })
|
|
});
|
|
|
|
if(this.duration) {
|
|
description += `\n${this.guild.format('INFRACTION_DESCRIPTIONDURATION', { duration: Util.duration(this.duration) })}`;
|
|
}
|
|
|
|
if(this.points > 0) {
|
|
description += `\n${this.guild.format('INFRACTION_DESCRIPTIONPOINTS', { points: this.points, total: this.totalPoints })}`;
|
|
}
|
|
|
|
if(this.description && this.description instanceof Function) {
|
|
description += this.description(dm);
|
|
}
|
|
|
|
if((!this.silent && (this.message || this.hyperlink)) && (dm && !Constants.RemovedInfractions.includes(this.type))) {
|
|
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}` })}`;
|
|
}
|
|
|
|
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
|
|
};
|
|
}
|
|
|
|
get json() {
|
|
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,
|
|
timestamp: this.timestamp,
|
|
duration: this.duration,
|
|
callback: this.callback,
|
|
reason: this.reason,
|
|
points: this.points,
|
|
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,
|
|
callbacked: false
|
|
};
|
|
}
|
|
|
|
get targetName() {
|
|
return this.targetType === 'user'
|
|
? this.target.tag
|
|
: `#${this.target.name}`;
|
|
}
|
|
|
|
get targetIcon() {
|
|
return this.targetType === 'user'
|
|
? this.target.displayAvatarURL()
|
|
: this.guild.iconURL();
|
|
}
|
|
|
|
get actions() {
|
|
const actions = [];
|
|
for(const argument of Object.values(this.arguments)) {
|
|
if(['silent', 'prune', 'force'].includes(argument)) actions.push(argument);
|
|
}
|
|
return actions;
|
|
}
|
|
|
|
get _reason() {
|
|
let str = `[${this.type}][targetId:${this.target.id}] Executed by ${this.executor.tag} (${this.executor.id}) because: ${this.reason}`;
|
|
if(str.length > 512) str = `${this.reason.substring(0, 509)}...`;
|
|
return str;
|
|
}
|
|
|
|
//Super Functions
|
|
_succeed() {
|
|
return {
|
|
error: false,
|
|
infraction: this
|
|
};
|
|
}
|
|
|
|
_fail(message, fatal = false) {
|
|
return {
|
|
error: true,
|
|
infraction: this,
|
|
reason: message,
|
|
fatal
|
|
};
|
|
}
|
|
|
|
verify() {
|
|
return this._verify();
|
|
}
|
|
|
|
async _verify() {
|
|
if (this.executor.id === this.guild.ownerID) return this._succeed();
|
|
if (this.guild && this.guild._settings.protection.enabled && this.targetType === 'user') {
|
|
//Idk what the thought process here has been, but the user object does not have roles, and the executor is a user object
|
|
const executor = await this.guild.members.fetch(this.executor.id).catch();
|
|
const target = await this.guild.members.fetch(this.target.id).catch();
|
|
if (!target) return this._succeed();
|
|
const executorHighest = executor.roles.highest; //this.executor.roles.highest;
|
|
const targetHighest = target.roles.highest; //this.member.roles.highest;
|
|
if(executorHighest.comparePositionTo(targetHighest) > 0) {
|
|
return this._fail('INFRACTION_PROTECTIONERROR');
|
|
}
|
|
}
|
|
return this._succeed();
|
|
}
|
|
|
|
}
|
|
|
|
module.exports = Infraction; |