- Fixed member requirement for Mute command

- Fixing other random shit
- Limit TIME to Max32BitInt
- Adding case delete commandoption
This commit is contained in:
D3vision 2023-12-07 23:29:00 +01:00
parent c8fb823b05
commit 427f07c5bb
8 changed files with 602 additions and 569 deletions

View File

@ -6,6 +6,7 @@ import { inspect } from 'util';
import Util from '../../../../utilities/Util.js'; import Util from '../../../../utilities/Util.js';
import { EmbedDefaultColor, InfractionColors } from '../../../../constants/Constants.js'; import { EmbedDefaultColor, InfractionColors } from '../../../../constants/Constants.js';
import { APIEmbed, GuildChannel, User } from 'discord.js'; import { APIEmbed, GuildChannel, User } from 'discord.js';
import Infraction from '../../../interfaces/Infraction.js';
class CaseCommand extends SlashCommand class CaseCommand extends SlashCommand
{ {
@ -22,12 +23,13 @@ class CaseCommand extends SlashCommand
minimum: 0, minimum: 0,
required: true required: true
}, { }, {
name: [ 'export', 'verbose', 'changes' ], // name: [ 'export', 'verbose', 'changes', 'delete' ], //
type: CommandOptionType.BOOLEAN, type: CommandOptionType.BOOLEAN,
description: [ description: [
'Print out raw infraction data in JSON form', 'Print out raw infraction data in JSON form',
'Print out more detailed information about the case', 'Print out more detailed information about the case',
'List changes to the case' 'List changes to the case',
'Delete the case'
], ],
dependsOn: [ 'id' ], dependsOn: [ 'id' ],
flag: true, flag: true,
@ -41,24 +43,32 @@ class CaseCommand extends SlashCommand
}); });
} }
async execute (invoker: InvokerWrapper, opts: CommandParams) async execute (invoker: InvokerWrapper<true>, opts: CommandParams)
{ {
const { id, verbose, export: exp, changes } = opts; const { id, verbose, export: exp, changes, delete: remove } = opts;
const guild = invoker.guild!; const guild = invoker.guild!;
const infraction = await this.client.storageManager.mongodb.infractions.findOne({ const infraction = await new Infraction(this.client, this.logger, { guild, case: id!.asNumber }).fetch(true).catch(() => null);
id: `${guild.id}:${id?.asNumber}`
}, { projection: { _id: 0 } });
if (!infraction) if (!infraction)
return { emoji: 'failure', index: 'COMMAND_CASE_NOTFOUND', params: { caseID: id!.asNumber } }; return { emoji: 'failure', index: 'COMMAND_CASE_NOTFOUND', params: { caseID: id!.asNumber } };
if (remove?.asBool === true)
{
if (!invoker.member?.permissions.has('Administrator'))
return { emoji: 'failure', index: 'ERR_MISSING_PERMISSIONS' };
await infraction.resolve(invoker.member, invoker.format('COMMAND_CASE_DELETED_LOG'), false);
await this.client.storageManager.mongodb.infractions.deleteOne({
id: `${guild.id}:${id?.asNumber}`
});
return { emoji: 'success', index: 'COMMAND_CASE_DELETED', params: { caseID: id!.asNumber } };
}
if (exp?.asString) if (exp?.asString)
return `\`\`\`js\n${inspect(infraction)}\`\`\``; return `\`\`\`js\n${inspect(infraction)}\`\`\``;
if (changes?.asString) if (changes?.asString)
return { embed: await this._listChanges(invoker, infraction) }; return { embed: await this._listChanges(invoker, infraction.json) };
const target = infraction.targetType === 'USER' ? await this.client.resolver.resolveUser(infraction.target) : await guild.resolveChannel(infraction.target); const target = infraction.targetType === 'USER' ? await this.client.resolver.resolveUser(infraction.targetId!) : await guild.resolveChannel(infraction.targetId);
const staff = await this.client.resolver.resolveUser(infraction.executor); const staff = await this.client.resolver.resolveUser(infraction.executor!);
let index = 'COMMAND_CASE_TEMPLATE', let index = 'COMMAND_CASE_TEMPLATE',
caseTitleIndex = 'COMMAND_CASE_TITLE', caseTitleIndex = 'COMMAND_CASE_TITLE',
@ -75,7 +85,7 @@ class CaseCommand extends SlashCommand
targetUserIndex += '_VERBOSE'; targetUserIndex += '_VERBOSE';
targetChannelIndex += '_VERBOSE'; targetChannelIndex += '_VERBOSE';
} }
const { json } = infraction;
let description = invoker.format(index, { let description = invoker.format(index, {
uniqueID: infraction.id, uniqueID: infraction.id,
type: infraction.type, type: infraction.type,
@ -86,25 +96,25 @@ class CaseCommand extends SlashCommand
target: infraction.targetType === 'USER' ? invoker.format(targetUserIndex, { target: (target as User).tag, targetID: target!.id }) : invoker.format(targetChannelIndex, { target: (target as GuildChannel).name, targetID: target!.id }), target: infraction.targetType === 'USER' ? invoker.format(targetUserIndex, { target: (target as User).tag, targetID: target!.id }) : invoker.format(targetChannelIndex, { target: (target as GuildChannel).name, targetID: target!.id }),
staff: staff?.tag??'N/A', staff: staff?.tag??'N/A',
staffID: staff?.id??'N/A', staffID: staff?.id??'N/A',
nrChanges: infraction.changes?.length || 0, nrChanges: json.changes?.length || 0,
changes: infraction.changes?.map((change) => change.type).join(', ') || 'N/A', changes: json.changes?.map((change) => change.type).join(', ') || 'N/A',
guild: guild.id, guild: guild.id,
channel: infraction.channel, channel: json.channel,
message: infraction.message message: json.message
}); });
if (infraction.data?.seconds) if (infraction.data?.seconds)
description += '\n' + invoker.format(caseSlowmodeIndex, { readable: Util.humanise(infraction.data.seconds), seconds: infraction.data.seconds }); description += '\n' + invoker.format(caseSlowmodeIndex, { readable: Util.humanise(infraction.data.seconds), seconds: infraction.data.seconds });
if (infraction.duration !== null) if (json.duration !== null)
{ {
const duration = Math.floor(infraction.duration / 1000); const duration = Math.floor(json.duration / 1000);
description += '\n' + invoker.format(caseLengthIndex, { time: Util.humanise(duration), inSeconds: duration }); description += '\n' + invoker.format(caseLengthIndex, { time: Util.humanise(duration), inSeconds: duration });
} }
if (guild._settings.modpoints.enabled) if (guild._settings.modpoints.enabled)
description += '\n' + invoker.format('COMMAND_CASE_MODPOINTS', { description += '\n' + invoker.format('COMMAND_CASE_MODPOINTS', {
points: infraction.points, points: infraction.points,
expires: infraction.expiration ? `<t:${Math.round(infraction.expiration / 1000)}:R>` : false expires: json.expiration ? `<t:${Math.round(json.expiration / 1000)}:R>` : false
}); });
description += '\n\n' + invoker.format('COMMAND_CASE_REASON', { reason: infraction.reason }); description += '\n\n' + invoker.format('COMMAND_CASE_REASON', { reason: infraction.reason });

View File

@ -3,7 +3,7 @@ import DiscordClient from '../../../DiscordClient.js';
import { Mute } from '../../../infractions/index.js'; import { Mute } from '../../../infractions/index.js';
import { CommandError, ModerationCommand } from '../../../interfaces/index.js'; import { CommandError, ModerationCommand } from '../../../interfaces/index.js';
import InvokerWrapper from '../../wrappers/InvokerWrapper.js'; import InvokerWrapper from '../../wrappers/InvokerWrapper.js';
import UserWrapper from '../../wrappers/UserWrapper.js'; import MemberWrapper from '../../wrappers/MemberWrapper.js';
class MuteCommand extends ModerationCommand class MuteCommand extends ModerationCommand
{ {
@ -41,9 +41,9 @@ class MuteCommand extends ModerationCommand
else if (!me!.permissions.has('ManageRoles')) else if (!me!.permissions.has('ManageRoles'))
throw new CommandError(invoker, { index: 'INHIBITOR_CLIENTPERMISSIONS_ERROR', formatParams: { command: this.name, missing: 'ManageRoles' } }); throw new CommandError(invoker, { index: 'INHIBITOR_CLIENTPERMISSIONS_ERROR', formatParams: { command: this.name, missing: 'ManageRoles' } });
const wrappers = await Promise.all(users.asUsers.map(user => this.client.getUserWrapper(user))); const wrappers = await Promise.all(users!.asUsers.map(user => guild.memberWrapper(user)));
return this.client.moderation.handleInfraction(Mute, invoker, { return this.client.moderation.handleInfraction(Mute, invoker, {
targets: wrappers.filter(Boolean) as UserWrapper[], targets: wrappers.filter(Boolean) as MemberWrapper[],
args args
}); });

View File

@ -3,7 +3,7 @@ import DiscordClient from '../../../DiscordClient.js';
import { Nickname } from '../../../infractions/index.js'; import { Nickname } from '../../../infractions/index.js';
import { CommandError, ModerationCommand } from '../../../interfaces/index.js'; import { CommandError, ModerationCommand } from '../../../interfaces/index.js';
import InvokerWrapper from '../../wrappers/InvokerWrapper.js'; import InvokerWrapper from '../../wrappers/InvokerWrapper.js';
import UserWrapper from '../../wrappers/UserWrapper.js'; import MemberWrapper from '../../wrappers/MemberWrapper.js';
class NicknameCommand extends ModerationCommand class NicknameCommand extends ModerationCommand
{ {
@ -28,15 +28,15 @@ class NicknameCommand extends ModerationCommand
} }
async execute (invoker: InvokerWrapper, { users, name, ...args }: CommandParams) async execute (invoker: InvokerWrapper<true>, { users, name, ...args }: CommandParams)
{ {
if (!users) if (!users)
throw new CommandError(invoker, { index: 'MODERATION_MISSING_USERS' }); throw new CommandError(invoker, { index: 'MODERATION_MISSING_USERS' });
const wrappers = await Promise.all(users.asUsers.map(user => this.client.getUserWrapper(user))); const wrappers = await Promise.all(users!.asUsers.map(user => invoker.guild.memberWrapper(user)));
return this.client.moderation.handleInfraction(Nickname, invoker, { return this.client.moderation.handleInfraction(Nickname, invoker, {
targets: wrappers.filter(Boolean) as UserWrapper[], targets: wrappers.filter(Boolean) as MemberWrapper[],
args, args,
data: { data: {
name: name?.asString name: name?.asString

View File

@ -21,7 +21,6 @@ class MuteInfraction extends Infraction
} }
member?: MemberWrapper; member?: MemberWrapper;
duration?: number;
constructor (client: DiscordClient, logger: LoggerClient, opts: MuteData) constructor (client: DiscordClient, logger: LoggerClient, opts: MuteData)
{ {
@ -52,10 +51,7 @@ class MuteInfraction extends Infraction
this.member = opts.target; this.member = opts.target;
const { mute } = this.guild._settings; const { mute } = this.guild._settings;
if (!this.duration && (!mute.permanent || mute.type === MuteType.Timeout)) if (!this.duration && (!mute.permanent || mute.type === MuteType.Timeout))
{
this.duration = mute.default * 1000; this.duration = mute.default * 1000;
}
} }
} }
@ -277,6 +273,7 @@ class MuteInfraction extends Infraction
break; break;
} }
} }
await super.resolve(_staff, _reason, _notify);
return { message, error }; return { message, error };
} }

View File

@ -8,6 +8,7 @@ import Command from './commands/Command.js';
import moment from 'moment'; import moment from 'moment';
import { GuildBasedChannel, GuildMember, Role, User } from 'discord.js'; import { GuildBasedChannel, GuildMember, Role, User } from 'discord.js';
import Module from './Module.js'; import Module from './Module.js';
import { Max32BitInt } from '../../constants/Constants.js';
const PointsReg = /^([-+]?[0-9]+) ?(points|point|pts|pt|p)$/iu; const PointsReg = /^([-+]?[0-9]+) ?(points|point|pts|pt|p)$/iu;
const ChannelType: {[key: string]: number} = { const ChannelType: {[key: string]: number} = {
@ -100,6 +101,8 @@ class CommandOption
this.#minimum = options.minimum; this.#minimum = options.minimum;
if (typeof options.maximum === 'number') if (typeof options.maximum === 'number')
this.#maximum = options.maximum; this.#maximum = options.maximum;
if (typeof this.#maximum === 'undefined' || this.#maximum > Number.MAX_SAFE_INTEGER)
this.#maximum = Number.MAX_SAFE_INTEGER;
this.#slashOption = options.slashOption || false; this.#slashOption = options.slashOption || false;
this.#flag = options.flag ?? false; // used with message based command options this.#flag = options.flag ?? false; // used with message based command options
@ -277,7 +280,7 @@ class CommandOption
POINTS: async () => POINTS: async () =>
{ {
if (this.slashOption) if (this.slashOption)
return { value: this.#rawValue }; return { value: parseInt(this.#rawValue as string) };
let value = null, let value = null,
removed = null; removed = null;
if (!this.#rawValue) if (!this.#rawValue)
@ -470,6 +473,8 @@ class CommandOption
const value = this.client.resolver.resolveTime(this.#rawValue); const value = this.client.resolver.resolveTime(this.#rawValue);
if (value === null) if (value === null)
return { error: true }; return { error: true };
if ((value*1000) > Max32BitInt)
return { error: true, index: 'O_COMMANDHANDLER_TYPETIME_MAX', params: { maximum: Util.humanise(Max32BitInt/1000) } };
if (typeof this.#maximum !== 'undefined' && value > this.#maximum) if (typeof this.#maximum !== 'undefined' && value > this.#maximum)
return { error: true, index: 'O_COMMANDHANDLER_TYPETIME_MAX', params: { maximum: Util.humanise(this.#maximum) } }; return { error: true, index: 'O_COMMANDHANDLER_TYPETIME_MAX', params: { maximum: Util.humanise(this.#maximum) } };
return { value, removed: [ this.#rawValue ] }; return { value, removed: [ this.#rawValue ] };

View File

@ -244,9 +244,12 @@ class Infraction
async save () async save ()
{ {
const { json } = this;
const filter: {id: string, _id?: ObjectId} = { id: this.id }; const filter: {id: string, _id?: ObjectId} = { id: this.id };
if (this.#mongoId) if (this.#mongoId)
filter._id = this.#mongoId; filter._id = this.#mongoId;
if (json.points && typeof json.points !== 'number')
throw new Error('Invalid points type');
return this.#client.mongodb.infractions.updateOne(filter, { $set: this.json }) return this.#client.mongodb.infractions.updateOne(filter, { $set: this.json })
.catch((error: Error) => .catch((error: Error) =>
{ {
@ -566,6 +569,16 @@ class Infraction
return InfractionColors[this.#type!]; return InfractionColors[this.#type!];
} }
get duration ()
{
return this.#duration;
}
set duration (duration: number | null)
{
this.#duration = duration;
}
// Super Functions // Super Functions
protected _succeed (): InfractionSuccess protected _succeed (): InfractionSuccess
{ {

View File

@ -2,6 +2,7 @@ import { InfractionType } from "../../@types/Client.js";
const ZeroWidthChar = '\u200b'; const ZeroWidthChar = '\u200b';
const EmbedDefaultColor = 619452; const EmbedDefaultColor = 619452;
const Max32BitInt = 2147483647;
const UploadLimit = { const UploadLimit = {
'0': 8, '0': 8,
@ -235,6 +236,7 @@ export {
UploadLimit, UploadLimit,
ZeroWidthChar, ZeroWidthChar,
EmbedDefaultColor, EmbedDefaultColor,
Max32BitInt,
PermissionNames, PermissionNames,
EmbedLimits, EmbedLimits,
InfractionResolves, InfractionResolves,

View File

@ -369,6 +369,12 @@ View a specific case
[COMMAND_CASE_NOTFOUND] [COMMAND_CASE_NOTFOUND]
No case matching ID `{caseID}` was found. No case matching ID `{caseID}` was found.
[COMMAND_CASE_DELETED]
Case `{caseID}` was deleted.
[COMMAND_CASE_DELETED_LOG]
Case deleted.
[COMMAND_CASE_TITLE] [COMMAND_CASE_TITLE]
{emoji_book} Case **#{caseID}** {emoji_book} Case **#{caseID}**