Skip to content

Commit

Permalink
refactor: streamline moderation settings commands (#2592)
Browse files Browse the repository at this point in the history
  • Loading branch information
kyranet authored Apr 5, 2024
1 parent 9785963 commit 3c790b6
Show file tree
Hide file tree
Showing 66 changed files with 1,108 additions and 1,383 deletions.
21 changes: 21 additions & 0 deletions src/commands/Management/AutoModeration/automod-attachments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { GuildSettings } from '#lib/database';
import { LanguageKeys } from '#lib/i18n/languageKeys';
import { AutoModerationCommand } from '#lib/moderation';
import { ApplyOptions } from '@sapphire/decorators';

const Root = LanguageKeys.Commands.AutoModeration;
const SettingsRoot = GuildSettings.AutoModeration.Attachments;

@ApplyOptions<AutoModerationCommand.Options>({
aliases: ['attachment-mode', 'attachments-mode', 'manage-attachment', 'manage-attachments'],
description: Root.AttachmentsDescription,
localizedNameKey: Root.AttachmentsName,
adderPropertyName: 'attachments',
keyEnabled: SettingsRoot.Enabled,
keyOnInfraction: SettingsRoot.SoftAction,
keyPunishment: SettingsRoot.HardAction,
keyPunishmentDuration: SettingsRoot.HardActionDuration,
keyPunishmentThreshold: SettingsRoot.ThresholdMaximum,
keyPunishmentThresholdPeriod: SettingsRoot.ThresholdDuration
})
export class UserAutoModerationCommand extends AutoModerationCommand {}
21 changes: 21 additions & 0 deletions src/commands/Management/AutoModeration/automod-capitals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { GuildSettings } from '#lib/database';
import { LanguageKeys } from '#lib/i18n/languageKeys';
import { AutoModerationCommand } from '#lib/moderation';
import { ApplyOptions } from '@sapphire/decorators';

const Root = LanguageKeys.Commands.AutoModeration;
const SettingsRoot = GuildSettings.AutoModeration.Capitals;

@ApplyOptions<AutoModerationCommand.Options>({
aliases: ['capitals-mode', 'manage-capitals'],
description: Root.CapitalsDescription,
localizedNameKey: Root.CapitalsName,
adderPropertyName: 'capitals',
keyEnabled: SettingsRoot.Enabled,
keyOnInfraction: SettingsRoot.SoftAction,
keyPunishment: SettingsRoot.HardAction,
keyPunishmentDuration: SettingsRoot.HardActionDuration,
keyPunishmentThreshold: SettingsRoot.ThresholdMaximum,
keyPunishmentThresholdPeriod: SettingsRoot.ThresholdDuration
})
export class UserAutoModerationCommand extends AutoModerationCommand {}
21 changes: 21 additions & 0 deletions src/commands/Management/AutoModeration/automod-invites.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { GuildSettings } from '#lib/database';
import { LanguageKeys } from '#lib/i18n/languageKeys';
import { AutoModerationCommand } from '#lib/moderation';
import { ApplyOptions } from '@sapphire/decorators';

const Root = LanguageKeys.Commands.AutoModeration;
const SettingsRoot = GuildSettings.AutoModeration.Invites;

@ApplyOptions<AutoModerationCommand.Options>({
aliases: ['invite-mode', 'invites-mode', 'manage-invite', 'manage-invites'],
description: Root.InvitesDescription,
localizedNameKey: Root.InvitesName,
adderPropertyName: 'invites',
keyEnabled: SettingsRoot.Enabled,
keyOnInfraction: SettingsRoot.SoftAction,
keyPunishment: SettingsRoot.HardAction,
keyPunishmentDuration: SettingsRoot.HardActionDuration,
keyPunishmentThreshold: SettingsRoot.ThresholdMaximum,
keyPunishmentThresholdPeriod: SettingsRoot.ThresholdDuration
})
export class UserAutoModerationCommand extends AutoModerationCommand {}
21 changes: 21 additions & 0 deletions src/commands/Management/AutoModeration/automod-links.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { GuildSettings } from '#lib/database';
import { LanguageKeys } from '#lib/i18n/languageKeys';
import { AutoModerationCommand } from '#lib/moderation';
import { ApplyOptions } from '@sapphire/decorators';

const Root = LanguageKeys.Commands.AutoModeration;
const SettingsRoot = GuildSettings.AutoModeration.Links;

@ApplyOptions<AutoModerationCommand.Options>({
aliases: ['link-mode', 'links-mode', 'manage-link', 'manage-links'],
description: Root.LinksDescription,
localizedNameKey: Root.LinksName,
adderPropertyName: 'links',
keyEnabled: SettingsRoot.Enabled,
keyOnInfraction: SettingsRoot.SoftAction,
keyPunishment: SettingsRoot.HardAction,
keyPunishmentDuration: SettingsRoot.HardActionDuration,
keyPunishmentThreshold: SettingsRoot.ThresholdMaximum,
keyPunishmentThresholdPeriod: SettingsRoot.ThresholdDuration
})
export class UserAutoModerationCommand extends AutoModerationCommand {}
21 changes: 21 additions & 0 deletions src/commands/Management/AutoModeration/automod-newlines.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { GuildSettings } from '#lib/database';
import { LanguageKeys } from '#lib/i18n/languageKeys';
import { AutoModerationCommand } from '#lib/moderation';
import { ApplyOptions } from '@sapphire/decorators';

const Root = LanguageKeys.Commands.AutoModeration;
const SettingsRoot = GuildSettings.AutoModeration.NewLines;

@ApplyOptions<AutoModerationCommand.Options>({
aliases: ['newline-mode', 'newlines-mode', 'manage-newline', 'manage-newlines'],
description: Root.NewlinesDescription,
localizedNameKey: Root.NewlinesName,
adderPropertyName: 'newlines',
keyEnabled: SettingsRoot.Enabled,
keyOnInfraction: SettingsRoot.SoftAction,
keyPunishment: SettingsRoot.HardAction,
keyPunishmentDuration: SettingsRoot.HardActionDuration,
keyPunishmentThreshold: SettingsRoot.ThresholdMaximum,
keyPunishmentThresholdPeriod: SettingsRoot.ThresholdDuration
})
export class UserAutoModerationCommand extends AutoModerationCommand {}
21 changes: 21 additions & 0 deletions src/commands/Management/AutoModeration/automod-reactions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { GuildSettings } from '#lib/database';
import { LanguageKeys } from '#lib/i18n/languageKeys';
import { AutoModerationCommand } from '#lib/moderation';
import { ApplyOptions } from '@sapphire/decorators';

const Root = LanguageKeys.Commands.AutoModeration;
const SettingsRoot = GuildSettings.AutoModeration.Reactions;

@ApplyOptions<AutoModerationCommand.Options>({
aliases: ['reaction-mode', 'r-mode'],
description: Root.ReactionsDescription,
localizedNameKey: Root.ReactionsName,
adderPropertyName: 'reactions',
keyEnabled: SettingsRoot.Enabled,
keyOnInfraction: SettingsRoot.SoftAction,
keyPunishment: SettingsRoot.HardAction,
keyPunishmentDuration: SettingsRoot.HardActionDuration,
keyPunishmentThreshold: SettingsRoot.ThresholdMaximum,
keyPunishmentThresholdPeriod: SettingsRoot.ThresholdDuration
})
export class UserAutoModerationCommand extends AutoModerationCommand {}
21 changes: 21 additions & 0 deletions src/commands/Management/AutoModeration/automod-spam.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { GuildSettings } from '#lib/database';
import { LanguageKeys } from '#lib/i18n/languageKeys';
import { AutoModerationCommand } from '#lib/moderation';
import { ApplyOptions } from '@sapphire/decorators';

const Root = LanguageKeys.Commands.AutoModeration;
const SettingsRoot = GuildSettings.AutoModeration.Messages;

@ApplyOptions<AutoModerationCommand.Options>({
aliases: ['message-mode', 'messages-mode', 'manage-message', 'manage-messages'],
description: Root.SpamDescription,
localizedNameKey: Root.SpamName,
adderPropertyName: 'messages',
keyEnabled: SettingsRoot.Enabled,
keyOnInfraction: SettingsRoot.SoftAction,
keyPunishment: SettingsRoot.HardAction,
keyPunishmentDuration: SettingsRoot.HardActionDuration,
keyPunishmentThreshold: SettingsRoot.ThresholdMaximum,
keyPunishmentThresholdPeriod: SettingsRoot.ThresholdDuration
})
export class UserAutoModerationCommand extends AutoModerationCommand {}
144 changes: 144 additions & 0 deletions src/commands/Management/AutoModeration/automod-words.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { GuildSettings, readSettings, writeSettings, type GuildEntity } from '#lib/database';
import { LanguageKeys } from '#lib/i18n/languageKeys';
import { getSupportedUserLanguageT } from '#lib/i18n/translate';
import { AutoModerationCommand } from '#lib/moderation';
import { IncomingType, OutgoingType } from '#lib/moderation/workers';
import type { SkyraSubcommand } from '#lib/structures';
import type { GuildMessage } from '#lib/types';
import { addAutomaticFields } from '#utils/functions';
import { chatInputApplicationCommandMention, type SlashCommandBuilder, type SlashCommandSubcommandBuilder } from '@discordjs/builders';
import { ApplyOptions } from '@sapphire/decorators';
import { send } from '@sapphire/plugin-editable-commands';
import { applyLocalizedBuilder, type TFunction } from '@sapphire/plugin-i18next';
import { isNullishOrEmpty, type Awaitable } from '@sapphire/utilities';
import { remove as removeConfusables } from 'confusables';
import { inlineCode, type Guild } from 'discord.js';

const Root = LanguageKeys.Commands.AutoModeration;
const SettingsRoot = GuildSettings.AutoModeration.Filter;

@ApplyOptions<AutoModerationCommand.Options>({
aliases: ['filter-mode', 'word-filter-mode', 'manage-filter', 'filter'],
description: Root.WordsDescription,
localizedNameKey: Root.WordsName,
subcommands: [
{ name: 'add', chatInputRun: 'chatInputRunAdd', messageRun: 'messageRunAdd' },
{ name: 'remove', chatInputRun: 'chatInputRunRemove', messageRun: 'messageRunRemove' }
],
resetKeys: [{ key: Root.OptionsKeyWords, value: 'words' }],
adderPropertyName: 'words',
keyEnabled: SettingsRoot.Enabled,
keyOnInfraction: SettingsRoot.SoftAction,
keyPunishment: SettingsRoot.HardAction,
keyPunishmentDuration: SettingsRoot.HardActionDuration,
keyPunishmentThreshold: SettingsRoot.ThresholdMaximum,
keyPunishmentThresholdPeriod: SettingsRoot.ThresholdDuration
})
export class UserAutoModerationCommand extends AutoModerationCommand {
/** @deprecated */
public messageRunAdd(message: GuildMessage, args: SkyraSubcommand.Args) {
const command = chatInputApplicationCommandMention(this.name, 'add', this.getGlobalCommandId());
return send(message, args.t(LanguageKeys.Commands.Shared.DeprecatedMessage, { command }));
}

/** @deprecated */
public messageRunRemove(message: GuildMessage, args: SkyraSubcommand.Args) {
const command = chatInputApplicationCommandMention(this.name, 'remove', this.getGlobalCommandId());
return send(message, args.t(LanguageKeys.Commands.Shared.DeprecatedMessage, { command }));
}

public async chatInputRunAdd(interaction: AutoModerationCommand.Interaction) {
const word = this.#getWord(interaction);
const { guild } = interaction;
const settings = await readSettings(guild);

const t = getSupportedUserLanguageT(interaction);
if (await this.#hasWord(settings, word)) {
return interaction.reply({ content: t(Root.WordAddFiltered, { word }), ephemeral: true });
}

const updated = settings[SettingsRoot.Raw].concat(word);
await writeSettings(guild, [[SettingsRoot.Raw, updated]]);
return interaction.reply({ content: t(Root.EditSuccess), ephemeral: true });
}

public async chatInputRunRemove(interaction: AutoModerationCommand.Interaction) {
const word = this.#getWord(interaction);
const { guild } = interaction;
const settings = await readSettings(guild);

const t = getSupportedUserLanguageT(interaction);
const index = settings[SettingsRoot.Raw].indexOf(word);
if (index === -1) {
return interaction.reply({ content: t(Root.WordRemoveNotFiltered, { word }), ephemeral: true });
}

const updated = settings[SettingsRoot.Raw].toSpliced(index, 1);
await writeSettings(guild, [[SettingsRoot.Raw, updated]]);
return interaction.reply({ content: t(Root.EditSuccess), ephemeral: true });
}

protected override showEnabled(t: TFunction, settings: GuildEntity) {
const embed = super.showEnabled(t, settings);

const words = settings[SettingsRoot.Raw];
if (isNullishOrEmpty(words)) {
const command = chatInputApplicationCommandMention(this.name, 'add', this.getGlobalCommandId());
embed.addFields({
name: t(Root.WordShowListTitleEmpty),
value: t(Root.WordShowListEmpty, { command })
});
} else {
addAutomaticFields(
embed,
t(Root.WordShowListTitle, { count: words.length }),
t(Root.WordShowList, { words: words.map((word) => inlineCode(word)) })
);
}
return embed;
}

protected override registerSubcommands(builder: SlashCommandBuilder) {
return super
.registerSubcommands(builder)
.addSubcommand((subcommand) => this.registerAddSubcommand(subcommand))
.addSubcommand((subcommand) => this.registerRemoveSubcommand(subcommand));
}

protected registerAddSubcommand(subcommand: SlashCommandSubcommandBuilder) {
return applyLocalizedBuilder(subcommand, Root.AddName, Root.WordAddDescription) //
.addStringOption((option) => applyLocalizedBuilder(option, Root.OptionsWord).setRequired(true).setMinLength(2).setMaxLength(32));
}

protected registerRemoveSubcommand(subcommand: SlashCommandSubcommandBuilder) {
return applyLocalizedBuilder(subcommand, Root.RemoveName, Root.WordRemoveDescription) //
.addStringOption((option) => applyLocalizedBuilder(option, Root.OptionsWord).setRequired(true).setMinLength(2).setMaxLength(32));
}

protected override resetGetKeyValuePairFallback(
guild: Guild,
key: string
): Awaitable<readonly [keyof GuildEntity, GuildEntity[keyof GuildEntity]]> {
if (key === 'words') return [SettingsRoot.Raw, []];
return super.resetGetKeyValuePairFallback(guild, key);
}

#getWord(interaction: AutoModerationCommand.Interaction) {
return removeConfusables(interaction.options.getString('word', true).toLowerCase());
}

async #hasWord(settings: GuildEntity, word: string) {
const words = settings[SettingsRoot.Raw];
if (words.includes(word)) return true;

const regExp = settings.wordFilterRegExp;
if (regExp === null) return false;

try {
const result = await this.container.workers.send({ type: IncomingType.RunRegExp, content: word, regExp });
return result.type === OutgoingType.RegExpMatch;
} catch {
return false;
}
}
}
19 changes: 0 additions & 19 deletions src/commands/Management/Message Filters/attachmentMode.ts

This file was deleted.

19 changes: 0 additions & 19 deletions src/commands/Management/Message Filters/capitalsMode.ts

This file was deleted.

Loading

0 comments on commit 3c790b6

Please sign in to comment.