forked from Galactic/galactic-bot
infraction editing
This commit is contained in:
parent
9d36ed4981
commit
7a2fa4df3c
@ -171,6 +171,8 @@ exports.InfractionOpposites = {
|
||||
REMOVEROLE: 'ADDROLE'
|
||||
};
|
||||
|
||||
exports.TimedInfractions = Object.keys(exports.InfractionOpposites);
|
||||
|
||||
exports.InfractionColors = {
|
||||
NOTE: 0xEBEBEB,
|
||||
WARN: 0xffe15c,
|
||||
|
@ -433,5 +433,22 @@ No changes have been made to the case.
|
||||
Please respond with the new reason.
|
||||
Timeout in {time} seconds
|
||||
|
||||
[COMMAND_EDIT_NO_ARGS]
|
||||
Must specify at least one property to edit.
|
||||
|
||||
[COMMAND_EDIT_NOT_FOUND]
|
||||
Could not find the infraction.
|
||||
Could not find the infraction.
|
||||
|
||||
[COMMAND_EDIT_ISSUES]
|
||||
The command ran into the following errors:
|
||||
{issues}
|
||||
|
||||
*Note that some of the edits were successful*
|
||||
|
||||
[COMMAND_EDIT_FAILURE]
|
||||
Failed to edit the infraction.
|
||||
The following errors occurred:
|
||||
- {issues}
|
||||
|
||||
[COMMAND_EDIT_SUCCESS]
|
||||
Successfully edited the infraction.
|
@ -39,6 +39,24 @@ Failed to {infraction} {targetType} {target} because {reason}.
|
||||
[INFRACTION_DESCRIPTIONJUMPTO]
|
||||
**[Jump To {name}]({link})**
|
||||
|
||||
[INFRACTION_EDIT_DURATION_RESOLVED]
|
||||
Cannot edit duration of a resolved infraction.
|
||||
|
||||
[INFRACTION_EDIT_DURATION_CALLEDBACK]
|
||||
Cannot edit duration of an infraction whose timer expired.
|
||||
|
||||
[INFRACTION_EDIT_INVALID_TARGETTYPE]
|
||||
Cannot edit expiration of an infraction with non-user target.
|
||||
|
||||
[INFRACTION_EDIT_MODPOINTS_NOT_ON]
|
||||
Cannot edit modpoints as they are not enabled.
|
||||
|
||||
[INFRACTION_EDIT_EXPIRATION_NOT_ON]
|
||||
Cannot edit expiration as modpoints are not enabled.
|
||||
|
||||
[INFRACTION_EDIT_DURATION_NOTTIMED]
|
||||
Cannot edit duration of a non-timed infraction.
|
||||
|
||||
//Prune Command
|
||||
[INFRACTION_DESCRIPTIONAMOUNT]
|
||||
**Amount:** {amount}
|
||||
|
77
src/structure/components/commands/moderation/Edit.js
Normal file
77
src/structure/components/commands/moderation/Edit.js
Normal file
@ -0,0 +1,77 @@
|
||||
const { SlashCommand, Infraction } = require("../../../interfaces");
|
||||
|
||||
class EditCommand extends SlashCommand {
|
||||
|
||||
constructor(client) {
|
||||
super(client, {
|
||||
name: 'edit',
|
||||
description: 'Edit case data',
|
||||
module: 'moderation',
|
||||
showUsage: true,
|
||||
guildOnly: true,
|
||||
memberPermissions: ['MANAGE_MESSAGES'],
|
||||
options: [{
|
||||
name: 'case',
|
||||
type: 'INTEGER',
|
||||
description: 'Case ID (Integer)',
|
||||
required: true,
|
||||
minimum: 0
|
||||
}, {
|
||||
name: 'reason',
|
||||
description: 'Give "long" as a reason to instead enter through a prompt to bypass length limit',
|
||||
type: 'STRING'
|
||||
}, {
|
||||
name: 'points',
|
||||
type: 'INTEGER',
|
||||
description: 'New point value for case',
|
||||
minimum: 0, maximum: 100
|
||||
}, {
|
||||
name: ['expiration', 'duration'],
|
||||
type: 'TIME',
|
||||
description: [
|
||||
'New expiration for points, starts from the time the infraction was issued',
|
||||
'Duration if the infraction is timed'
|
||||
]
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
async execute(invoker, { case: caseId, reason, points, expiration, duration }) {
|
||||
|
||||
if (!(reason || points || expiration || duration)) return { emoji: 'failure', index: 'COMMAND_EDIT_NO_ARGS' };
|
||||
const { guild, member } = invoker;
|
||||
// const data = await this.client.storageManager.mongodb.infractions.findOne({ id: `${guild.id}:${caseId.value}` });
|
||||
const infraction = await new Infraction(this.client, { guild, case: caseId.value }).fetch().catch(() => null);
|
||||
if(!infraction) return { emoji: 'failure', index: 'COMMAND_EDIT_NOT_FOUND' };
|
||||
|
||||
const time = 120;
|
||||
if (reason?.value === 'long') reason = await invoker.promptMessage(invoker.format('COMMAND_EDIT_LONG', { time }), { time });
|
||||
else if (reason) reason = reason.value;
|
||||
|
||||
const results = [];
|
||||
if (reason) results.push(await infraction.editReason(member, reason));
|
||||
if (points) results.push(await infraction.editPoints(member, points.value));
|
||||
if (expiration) results.push(await infraction.editExpiration(member, expiration.value * 1000));
|
||||
if (duration) results.push(await infraction.editDuration(member, duration.value * 1000));
|
||||
|
||||
const successful = results.filter((result) => !result).length;
|
||||
const set = new Set(results.filter((result) => Boolean(result)).map((result) => result.index));
|
||||
const messages = [];
|
||||
for (const index of set) {
|
||||
messages.push(invoker.format(index));
|
||||
}
|
||||
|
||||
if (successful) {
|
||||
await infraction.updateMessages();
|
||||
await infraction.save();
|
||||
}
|
||||
|
||||
if (messages.length && successful) return { emoji: 'warning', index: 'COMMAND_EDIT_ISSUES', params: { issues: messages.join('\n') } };
|
||||
else if (!successful) return { emoji: 'failure', index: 'COMMAND_EDIT_FAILURE', params: { issues: messages.join('\n- ') } };
|
||||
return { emoji: 'success', index: 'COMMAND_EDIT_SUCCESS' };
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = EditCommand;
|
@ -1,8 +1,10 @@
|
||||
const { ObjectId } = require('mongodb');
|
||||
const {
|
||||
Constants: {
|
||||
InfractionTargetTypes,
|
||||
InfractionDictionary,
|
||||
InfractionColors
|
||||
InfractionColors,
|
||||
TimedInfractions
|
||||
}
|
||||
} = require('../../constants');
|
||||
|
||||
@ -19,8 +21,8 @@ class Infraction {
|
||||
|
||||
this.client = client;
|
||||
this.type = data.type || null;
|
||||
this.id = null;
|
||||
this.case = null;
|
||||
// this.id = null;
|
||||
this.case = data.case || null;
|
||||
|
||||
this.arguments = data.arguments || {};
|
||||
|
||||
@ -44,20 +46,20 @@ class Infraction {
|
||||
this.resolved = false;
|
||||
|
||||
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.
|
||||
// this.callback = isNaN(data.duration) ? null : Date.now() + data.duration; //At what time it will callback.
|
||||
|
||||
this.reason = data.reason || 'N/A';
|
||||
|
||||
this.points = data.points || 0;
|
||||
this.expiration = isNaN(data.expiration) ? null : Date.now() + data.expiration * 1000;
|
||||
this.expiration = data.expiration > 0 ? Date.now() + data.expiration : null; // Time when the points expire in milliseconds
|
||||
this.totalPoints = 0;
|
||||
|
||||
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.modLogMessageId = null;
|
||||
this.dmLogMessageId = null;
|
||||
this.modlogId = null;
|
||||
|
||||
this.changes = [];
|
||||
@ -65,12 +67,15 @@ class Infraction {
|
||||
this.timestamp = Date.now();
|
||||
|
||||
this._callbacked = Boolean(data._callbacked);
|
||||
this._fetched = Boolean(data);
|
||||
this._fetched = false; //Boolean(data);
|
||||
this._mongoId = null;
|
||||
|
||||
}
|
||||
|
||||
async handle() {
|
||||
|
||||
// Infraction was fetched from database, i.e. was already executed previously
|
||||
if (this._fetched) throw new Error(`Cannot execute a fetched Infraction`);
|
||||
//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.`);
|
||||
@ -94,7 +99,7 @@ class Infraction {
|
||||
this.modlogId = this._moderationLog.id;
|
||||
try {
|
||||
this._logMessage = await this._moderationLog.send({ embeds: [this._embed()] });
|
||||
this.modlogMessageId = this._logMessage.id;
|
||||
this.modLogMessageId = this._logMessage.id;
|
||||
} catch (e) { } //eslint-disable-line no-empty
|
||||
} else {
|
||||
this.client.logger.debug(`Did not log infraction ${this.type} because it is not in the infractions.`);
|
||||
@ -111,16 +116,17 @@ class Infraction {
|
||||
.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, {
|
||||
const logMessage = await this.target.send({
|
||||
content: message,
|
||||
embeds: [this._embed(true)]
|
||||
});
|
||||
this.dmlogMessageId = logMessage.id;
|
||||
this.dmLogMessageId = logMessage.id;
|
||||
} catch (e) { } //eslint-disable-line no-empty
|
||||
}
|
||||
}
|
||||
|
||||
if (this.duration) {
|
||||
await this.client.moderationManager._handleExpirations([this.json]);
|
||||
await this.client.moderationManager.handleCallbacks([this.json]);
|
||||
}
|
||||
|
||||
/* LMAOOOO PLEASE DONT JUDGE ME */
|
||||
@ -133,14 +139,16 @@ class Infraction {
|
||||
}
|
||||
|
||||
async save() {
|
||||
return this.client.storageManager.mongodb.infractions.insertOne(this.json)
|
||||
const filter = { id: this.id };
|
||||
if(this._mongoId) filter._id = this._mongoId;
|
||||
return this.client.storageManager.mongodb.infractions.updateOne(filter, this.json)
|
||||
.catch((error) => {
|
||||
this.client.logger.error(`There was an issue saving infraction data to the database.\n${error.stack || error}`);
|
||||
});
|
||||
}
|
||||
|
||||
hyperlink(bool = false) {
|
||||
if (bool) return `https://discord.com/channels/${this.guildId}/${this.modlogId}/${this.modlogMessageId}`;
|
||||
if (bool) return `https://discord.com/channels/${this.guildId}/${this.modlogId}/${this.modLogMessageId}`;
|
||||
return `https://discord.com/channels/${this.guildId}/${this.channelId}/${this.messageId}`;
|
||||
}
|
||||
|
||||
@ -156,7 +164,7 @@ class Infraction {
|
||||
})}`;
|
||||
|
||||
if (this.duration) {
|
||||
description += `\n${this.guild.format('INFRACTION_DESCRIPTIONDURATION', { duration: Util.duration(this.duration) })}`;
|
||||
description += `\n${this.guild.format('INFRACTION_DESCRIPTIONDURATION', { duration: Util.humanise(Math.floor(this.duration / 1000)) })}`;
|
||||
}
|
||||
|
||||
if (this.points && this.points > 0) { //TODO: Add expiration to INFRACTION_DESCRIPTIONPOINTS
|
||||
@ -195,17 +203,22 @@ class Infraction {
|
||||
|
||||
}
|
||||
|
||||
get callback() {
|
||||
if (isNaN(this.duration)) return null;
|
||||
return this.duration + this.timestamp;
|
||||
}
|
||||
|
||||
get json() {
|
||||
return {
|
||||
id: `${this.guildId}:${this.case}`,
|
||||
id: this.id,
|
||||
guild: this.guildId,
|
||||
channel: this.channelId,
|
||||
channelName: this.channel?.display,
|
||||
channelName: this.channel?.name,
|
||||
message: this.messageId,
|
||||
executor: this.executorId,
|
||||
executorTag: this.executor.display,
|
||||
executorTag: this.executor.tag || this.executor.user?.tag,
|
||||
target: this.targetId,
|
||||
targetTag: this.target.display || this.target.username || this.target.name,
|
||||
targetTag: this.target.tag || this.target.user?.tag || this.target.name,
|
||||
targetType: this.targetType,
|
||||
type: this.type,
|
||||
case: this.case,
|
||||
@ -217,8 +230,8 @@ class Infraction {
|
||||
flags: this.flags,
|
||||
points: this.points,
|
||||
expiration: this.expiration,
|
||||
modLogMessage: this.modlogMessageId,
|
||||
dmLogMessage: this.dmlogMessageId,
|
||||
modLogMessage: this.modLogMessageId,
|
||||
dmLogMessage: this.dmLogMessageId,
|
||||
modLogChannel: this.modlogId,
|
||||
resolved: this.resolved,
|
||||
changes: this.changes,
|
||||
@ -226,6 +239,10 @@ class Infraction {
|
||||
};
|
||||
}
|
||||
|
||||
get id() {
|
||||
return `${this.guildId}:${this.case}`;
|
||||
}
|
||||
|
||||
get targetIcon() {
|
||||
return this.targetType === 'USER'
|
||||
? this.target.displayAvatarURL()
|
||||
@ -299,68 +316,148 @@ class Infraction {
|
||||
return this._succeed();
|
||||
}
|
||||
|
||||
// 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;
|
||||
// }
|
||||
_error(reason) {
|
||||
if (!this._fetched) throw new Error(reason || `Cannot edit an unfetched infraction`);
|
||||
}
|
||||
|
||||
// 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;
|
||||
// }
|
||||
// }
|
||||
async updateMessages() {
|
||||
this._error(`Cannot update messages for unfetched infractions`);
|
||||
if (this._dmLogMessage) await this._dmLogMessage.edit({ embeds: [this._embed(true)] });
|
||||
if (this._modLogMessage) await this._modLogMessage.edit({ embeds: [this._embed()] });
|
||||
}
|
||||
|
||||
// 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;
|
||||
async editReason(staff, reason) {
|
||||
this._error();
|
||||
const log = {
|
||||
reason: this.reason,
|
||||
staff: staff.id,
|
||||
timestamp: Date.now(),
|
||||
type: 'REASON'
|
||||
};
|
||||
this.reason = reason;
|
||||
this.changes.push(log);
|
||||
}
|
||||
|
||||
// this.dictionary = InfractionDictionary[this.type];
|
||||
// this.color = InfractionColors[this.type];
|
||||
async editPoints(staff, points) {
|
||||
this._error();
|
||||
if (this.targetType !== 'USER') return { error: true, index: 'INFRACTION_EDIT_INVALID_TARGETTYPE' };
|
||||
if (!this.guild._settings.modpoints.enabled) return { error: true, index: 'INFRACTION_EDIT_MODPOINTS_NOT_ON' };
|
||||
const log = {
|
||||
points: this.points,
|
||||
staff: staff.id,
|
||||
timestamp: Date.now(),
|
||||
type: 'POINTS'
|
||||
};
|
||||
const diff = points - this.points;
|
||||
this.points = points;
|
||||
const userWrapper = await this.client.getUserWrapper(this.target.id);
|
||||
this.totalPoints = await userWrapper.editPoints(this.guild, { diff, id: this.id, expiration: this.expiration });
|
||||
this.changes.push(log);
|
||||
}
|
||||
|
||||
// this.modlogMessageId = data.modlogMessageId;
|
||||
// this.dmlogMessageId = data.dmlogMessageId;
|
||||
async editExpiration(staff, expiration) {
|
||||
this._error();
|
||||
if (this.targetType !== 'USER') return { error: true, index: 'INFRACTION_EDIT_INVALID_TARGETTYPE' };
|
||||
if (!this.guild._settings.modpoints.enabled) return { error: true, index: 'INFRACTION_EDIT_EXPIRATION_NOT_ON' };
|
||||
// const now = Date.now();
|
||||
const log = {
|
||||
type: 'EXPIRATION',
|
||||
staff: staff.id,
|
||||
timestamp: Date.now(),
|
||||
expiration: this.expiration ? this.expiration - this.timestamp : null
|
||||
};
|
||||
this.expiration = expiration + this.timestamp;
|
||||
const userWrapper = await this.client.getUserWrapper(this.target.id);
|
||||
this.totalPoints = await userWrapper.editExpiration(this.guild, {
|
||||
id: this.id,
|
||||
expiration: this.expiration,
|
||||
points: this.points
|
||||
});
|
||||
this.changes.push(log);
|
||||
}
|
||||
|
||||
// this._fetched = Boolean(data._fetched);
|
||||
// return this;
|
||||
async editDuration(staff, duration) {
|
||||
this._error();
|
||||
if (this.resolved) return { error: true, index: 'INFRACTION_EDIT_DURATION_RESOLVED' };
|
||||
if (this._callbacked) return { error: true, index: 'INFRACTION_EDIT_DURATION_CALLEDBACK' };
|
||||
if (!TimedInfractions.includes(this.type)) return { error: true, index: 'INFRACTION_EDIT_DURATION_NOTTIMED' };
|
||||
const now = Date.now();
|
||||
const log = {
|
||||
type: 'DURATION',
|
||||
staff: staff.id,
|
||||
timestamp: now,
|
||||
duration: this.duration
|
||||
};
|
||||
const member = await this.guild.memberWrapper(this.target);
|
||||
const callback = await member.getCallback(this.type, true);
|
||||
|
||||
// }
|
||||
this.duration = duration;
|
||||
if(callback) await this.client.moderationManager.removeCallback(callback);
|
||||
await this.client.moderationManager.handleCallbacks([this.json]);
|
||||
|
||||
// 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;
|
||||
// }
|
||||
this.changes.push(log);
|
||||
}
|
||||
|
||||
async fetch() { //Data from Mongodb (id-based data)
|
||||
const data = await this.client.storageManager.mongodb.infractions.findOne({ id: this.id });
|
||||
if(!data) throw new Error('No such case');
|
||||
|
||||
this._mongoId = ObjectId(data._id);
|
||||
this._callbacked = data._callbacked;
|
||||
|
||||
this.targetType = data.targetType;
|
||||
this.targetId = data.target;
|
||||
this.target = await this._fetchTarget(data.target);
|
||||
|
||||
this.executorId = data.executor;
|
||||
this.executor = await this.client.users.fetch(data.executor).catch(() => null);
|
||||
|
||||
this.type = data.type;
|
||||
|
||||
this.timestamp = data.timestamp;
|
||||
this.duration = data.duration;
|
||||
// this.callback = data.callback;
|
||||
|
||||
this.expiration = data.expiration;
|
||||
this.points = data.points;
|
||||
|
||||
this.reason = data.reason;
|
||||
this.resolved = data.resolved;
|
||||
this.changes = data.changes;
|
||||
|
||||
this.channelId = data.channel;
|
||||
this.channel = { name: data.channelName };
|
||||
|
||||
this.data = data.data;
|
||||
this.flags = data.flags;
|
||||
|
||||
this.modLogMessageId = data.modLogMessage;
|
||||
this.dmLogMessageId = data.dmLogMessage;
|
||||
this.messageId = data.message;
|
||||
this.modlogId = data.modLogChannel;
|
||||
const logChannel = await this.guild.resolveChannel(data.modLogChannel).catch(() => null);
|
||||
if (logChannel) this._modLogMessage = await logChannel.messages.fetch(data.modLogMessage).catch(() => null);
|
||||
const dm = await this.target.createDM().catch(() => null);
|
||||
if (dm) this._dmLogMessage = await dm.messages.fetch(data.dmLogMessage).catch(() => null);
|
||||
|
||||
this._fetched = true;
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user