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. }; class Infraction { constructor(client, opts = {}) { this.client = client; this.message = opts.message; this.arguments = opts.arguments; this.target = opts.target; //User or channel being targeted. this.targetType = opts.targetType; // 'user' or 'channel'. this.executor = opts.executor; //Whoever executed the command. this.guild = opts.guild; this.channel = opts.channel; this.case = null; this.type = opts.type; //What type of infraction (mute, kick, etc.) this.timestamp = new Date().getTime(); this.duration = opts.duration || null; //How long the action will last. Must be in milliseconds. this.reason = opts.reason.length ? opts.reason : 'N/A'; this.silent = opts.silent; this.points = 0; this.totalPoints = 0; this.expiration = 0; 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._logMessage = null; //The message embed sent in the moderation-log. Full message, not the ID. this._dmMessage = null; //The message embed sent in the target's direct messages. Full message, not the ID. } async log() { if(this.silent) { try { await this.message.delete(); } catch(e) {} //eslint-disable-line no-empty } const { moderationLog, moderationPoints } = this.guild._settings; /* Handling */ if(moderationPoints.enabled) { if(this.arguments.points) { this.points = parseInt(this.arguments.points.value); } else { this.points = moderationPoints.points[this.type] || 0; } if(this.arguments.expiration) { this.expiration = parseInt(this.arguments.expiration.value); } else { this.expiration = moderationPoints.expirations[this.type] || 0; } if(this.targetType === 'user') { this.totalPoints = await this.target.totalPoints(this.guild, this.points); } } this.guild._settings.caseId++; this.case = this.guild._settings.caseId; await this.guild._updateSettings({ caseId: this.case }); console.log(`points: ${this.points}, total points: ${this.totalPoints}, expiration: ${this.expiration}`); /* Logging */ if(moderationLog.channel) { if(moderationLog.infractions.includes(this.type.toLowerCase())) { const channel = this.client.resolver.resolveChannel(moderationLog.channel, true, this.guild); if(!channel) return undefined; this._logMessage = await channel.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') { 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); //add more if you want i should probably add a better system for this... try { this._dmMessage = await this.target.send(message, { embed: this.embed() }); } catch(e) {} //eslint-disable-line no-empty } //might be useful for automod related stuff // this.client.emit('infractionUpdate', { // action: 'new', // infraction: this // }); return this.save(); } async save() { await 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() { let description = this.message.format('INFRACTION_DESCRIPTION', { type: this.dictionary.past.toUpperCase(), moderator: `${Util.escapeMarkdown(this.executor.tag)}`, reason: this.reason.length > Constants.MaxCharacters ? `${this.reason.substring(0, Constants.MaxCharacters-3)}...` : this.reason }); if(this.duration) { description += `\n${this.message.format('INFRACTION_DESCRIPTIONDURATION', { duration: this._duration() })}`; } if(this.points) { description += `\n${this.message.format('INFRACTION_DESCRIPTIONPOINTS', { points: this.points, total: this.totalPoints })}`; } if(!this.silent) { description += `\n${this.message.format('INFRACTION_DESCRIPTIONJUMPTO', { name: 'Message', link: `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, duration: this.duration, reason: this.reason, timestamp: this.timestamp, points: this.points, expiration: this.expiration }; } 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 _reason() { let str = `[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, opts) { return { error: true, infraction: this, reason: this.message.format(message, opts) }; } } module.exports = Infraction;