diff options
author | IRONM00N <64110067+IRONM00N@users.noreply.github.com> | 2021-12-13 19:19:06 -0500 |
---|---|---|
committer | IRONM00N <64110067+IRONM00N@users.noreply.github.com> | 2021-12-13 19:19:06 -0500 |
commit | 8a27a3428dc3946a538f6471e480a34e41ceb7e6 (patch) | |
tree | ca45e5d9a94b32aa410624ae8a02d53f11cb4a33 /src/lib | |
parent | 64d8476209d33c06547988c27874b986814db232 (diff) | |
download | tanzanite-8a27a3428dc3946a538f6471e480a34e41ceb7e6.tar.gz tanzanite-8a27a3428dc3946a538f6471e480a34e41ceb7e6.tar.bz2 tanzanite-8a27a3428dc3946a538f6471e480a34e41ceb7e6.zip |
added new automod thing, fixed some errors
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/common/AutoMod.ts | 175 | ||||
-rw-r--r-- | src/lib/common/ButtonPaginator.ts | 13 | ||||
-rw-r--r-- | src/lib/extensions/discord.js/BushMessage.ts | 2 | ||||
-rw-r--r-- | src/lib/models/Guild.ts | 4 |
4 files changed, 176 insertions, 18 deletions
diff --git a/src/lib/common/AutoMod.ts b/src/lib/common/AutoMod.ts index 5fd5d2d..c52754a 100644 --- a/src/lib/common/AutoMod.ts +++ b/src/lib/common/AutoMod.ts @@ -4,18 +4,36 @@ import badLinksSecretArray from '../badlinks-secret.js'; // I cannot make this p import badLinksArray from '../badlinks.js'; import badWords from '../badwords.js'; +/** + * Handles auto moderation functionality. + */ export class AutoMod { + /** + * The message to check for blacklisted phrases on + */ private message: BushMessage; + /** + * Whether or not a punishment has already been given to the user + */ + private punished = false; + + /** + * @param message The message to check and potentially perform automod actions to + */ public constructor(message: BushMessage) { this.message = message; if (message.author.id === client.user?.id) return; void this.handle(); } + /** + * Handles the auto moderation + */ private async handle() { if (this.message.channel.type === 'DM' || !this.message.guild) return; - if (!(await this.message.guild.hasFeature('automod'))) return; + const hasFeature = this.message.guild.hasFeature; + if (!(await hasFeature('automod'))) return; const customAutomodPhrases = (await this.message.guild.getSetting('autoModPhases')) ?? {}; const badLinks: BadWords = {}; @@ -34,8 +52,8 @@ export class AutoMod { const result = { ...this.checkWords(customAutomodPhrases), - ...this.checkWords((await this.message.guild.hasFeature('excludeDefaultAutomod')) ? {} : badWords), - ...this.checkWords((await this.message.guild.hasFeature('excludeAutomodScamLinks')) ? {} : badLinks) + ...this.checkWords((await hasFeature('excludeDefaultAutomod')) ? {} : badWords), + ...this.checkWords((await hasFeature('excludeAutomodScamLinks')) ? {} : badLinks) }; if (Object.keys(result).length === 0) return; @@ -44,7 +62,7 @@ export class AutoMod { .map(([key, value]) => ({ word: key, ...value })) .sort((a, b) => b.severity - a.severity)[0]; - if (highestOffence.severity === undefined || highestOffence.severity === null) + if (highestOffence.severity === undefined || highestOffence.severity === null) { void this.message.guild.sendLogChannel('error', { embeds: [ { @@ -54,12 +72,19 @@ export class AutoMod { } ] }); - else { + } else { const color = this.punish(highestOffence); void this.log(highestOffence, color, result); } + + if (!this.punished && (await hasFeature('delScamMentions'))) void this.checkScamMentions(); } + /** + * Checks if any of the words provided are in the message + * @param words The words to check for + * @returns The blacklisted words found in the message + */ private checkWords(words: BadWords): BadWords { if (Object.keys(words).length === 0) return {}; @@ -79,17 +104,81 @@ export class AutoMod { return matchedWords; } + /** + * If the message contains '@everyone' or '@here' and it contains a common scam phrase, it will be deleted + * @returns + */ + private async checkScamMentions() { + const includes = this.message.content.toLocaleLowerCase().includes; + if (!includes('@everyone' || !includes('@here'))) return; + // It would be bad if we deleted a message that actually pinged @everyone or @here + if (this.message.member?.permissionsIn(this.message.channelId).has('MENTION_EVERYONE') || this.message.mentions.everyone) + return; + + if ( + includes('steam') || + includes('www.youtube.com') || + includes('youtu.be') || + includes('nitro') || + includes('1 month') || + includes('3 months') || + includes('personalize your profile') || + includes('even more') || + includes('xbox and discord') || + includes('left over') || + includes('check this lol') || + includes('airdrop') + ) { + const color = this.punish({ severity: Severity.TEMP_MUTE, reason: 'everyone mention and scam phrase' } as HighestOffence); + void this.message.guild!.sendLogChannel('automod', { + embeds: [ + new MessageEmbed() + .setTitle(`[Severity ${Severity.TEMP_MUTE}] Mention Scam Deleted`) + .setDescription( + `**User:** ${this.message.author} (${this.message.author.tag})\n**Sent From**: <#${this.message.channel.id}> [Jump to context](${this.message.url})` + ) + .addField('Message Content', `${await util.codeblock(this.message.content, 1024)}`) + .setColor(color) + .setTimestamp() + ], + components: + Severity.TEMP_MUTE >= 2 + ? [ + new MessageActionRow().addComponents( + new MessageButton() + .setStyle('DANGER') + .setLabel('Ban User') + .setCustomId(`automod;ban;${this.message.author.id};everyone mention and scam phrase`) + ) + ] + : undefined + }); + } + } + + /** + * Format a string according to the word options + * @param string The string to format + * @param wordOptions The word options to format with + * @returns The formatted string + */ private format(string: string, wordOptions: BadWordDetails) { const temp = wordOptions.ignoreCapitalization ? string.toLowerCase() : string; return wordOptions.ignoreSpaces ? temp.replace(/ /g, '') : temp; } - private punish(highestOffence: BadWordDetails & { word: string }) { + /** + * Punishes the user based on the severity of the offence + * @param highestOffence The highest offence to punish the user for + * @returns The color of the embed that the log should, based on the severity of the offence + */ + private punish(highestOffence: HighestOffence) { let color; switch (highestOffence.severity) { case Severity.DELETE: { color = util.colors.lightGray; void this.message.delete().catch((e) => deleteError.bind(this, e)); + this.punished = true; break; } case Severity.WARN: { @@ -99,6 +188,7 @@ export class AutoMod { moderator: this.message.guild!.me!, reason: `[AutoMod] ${highestOffence.reason}` }); + this.punished = true; break; } case Severity.TEMP_MUTE: { @@ -109,6 +199,7 @@ export class AutoMod { reason: `[AutoMod] ${highestOffence.reason}`, duration: 900_000 // 15 minutes }); + this.punished = true; break; } case Severity.PERM_MUTE: { @@ -119,6 +210,7 @@ export class AutoMod { reason: `[AutoMod] ${highestOffence.reason}`, duration: 0 // permanent }); + this.punished = true; break; } default: { @@ -142,7 +234,13 @@ export class AutoMod { } } - private async log(highestOffence: BadWordDetails & { word: string }, color: `#${string}`, offences: BadWords) { + /** + * Log an automod infraction to the guild's specified automod log channel + * @param highestOffence The highest severity word found in the message + * @param color The color that the log embed should be (based on the severity) + * @param offences The other offences that were also matched in the message + */ + private async log(highestOffence: HighestOffence, color: `#${string}`, offences: BadWords) { void client.console.info( 'autoMod', `Severity <<${highestOffence.severity}>> action performed on <<${this.message.author.tag}>> (<<${ @@ -150,7 +248,7 @@ export class AutoMod { }>>) in <<#${(this.message.channel as TextChannel).name}>> in <<${this.message.guild!.name}>>` ); - return await this.message.guild!.sendLogChannel('automod', { + await this.message.guild!.sendLogChannel('automod', { embeds: [ new MessageEmbed() .setTitle(`[Severity ${highestOffence.severity}] Automod Action Performed`) @@ -179,6 +277,10 @@ export class AutoMod { }); } + /** + * Handles the ban button in the automod log. + * @param interaction The button interaction. + */ public static async handleInteraction(interaction: BushButtonInteraction) { if (!interaction.memberPermissions?.has('BAN_MEMBERS')) return interaction.reply({ @@ -228,25 +330,74 @@ export class AutoMod { } } +/** + * The severity of the blacklisted word + */ export const enum Severity { - /** Delete message */ + /** + * Delete message + */ DELETE, - /** Delete message and warn user */ + + /** + * Delete message and warn user + */ WARN, - /** Delete message and mute user for 15 minutes */ + + /** + * Delete message and mute user for 15 minutes + */ TEMP_MUTE, - /** Delete message and mute user permanently */ + + /** + * Delete message and mute user permanently + */ PERM_MUTE } +/** + * Details about a blacklisted word + */ interface BadWordDetails { + /** + * The severity of the word + */ severity: Severity; + + /** + * Whether or not to ignore spaces when checking for the word + */ ignoreSpaces: boolean; + + /** + * Whether or not to ignore case when checking for the word + */ ignoreCapitalization: boolean; + + /** + * The reason that this word is blacklisted (used for the punishment reason) + */ reason: string; + + /** + * Whether or not the word is regex + */ regex: boolean; } +interface HighestOffence extends BadWordDetails { + /** + * The word that is blacklisted + */ + word: string; +} + +/** + * Blacklisted words mapped to their details + */ export interface BadWords { + /** + * The blacklisted word + */ [key: string]: BadWordDetails; } diff --git a/src/lib/common/ButtonPaginator.ts b/src/lib/common/ButtonPaginator.ts index b8ae249..983eb56 100644 --- a/src/lib/common/ButtonPaginator.ts +++ b/src/lib/common/ButtonPaginator.ts @@ -18,11 +18,11 @@ export class ButtonPaginator { /** * Sends multiple embeds with controls to switch between them - * @param message - The message to respond to - * @param embeds - The embeds to switch between - * @param text - The text send with the embeds (optional) - * @param deleteOnExit - Whether to delete the message when the exit button is clicked (defaults to true) - * @param startOn - The page to start from (**not** the index) + * @param message The message to respond to + * @param embeds The embeds to switch between + * @param text The text send with the embeds (optional) + * @param deleteOnExit Whether to delete the message when the exit button is clicked (defaults to true) + * @param startOn The page to start from (**not** the index) */ public static async send( message: BushMessage | BushSlashMessage, @@ -37,6 +37,9 @@ export class ButtonPaginator { return await new ButtonPaginator(message, embeds, text, deleteOnExit, startOn).send(); } + /** + * The number of pages in the paginator + */ protected get numPages(): number { return this.embeds.length; } diff --git a/src/lib/extensions/discord.js/BushMessage.ts b/src/lib/extensions/discord.js/BushMessage.ts index c722f3d..9f6d422 100644 --- a/src/lib/extensions/discord.js/BushMessage.ts +++ b/src/lib/extensions/discord.js/BushMessage.ts @@ -18,7 +18,7 @@ export type PartialBushMessage = Partialize< export class BushMessage<Cached extends boolean = boolean> extends Message<Cached> { public declare readonly client: BushClient; public declare util: BushCommandUtil<BushMessage<true>>; - public declare readonly guild: BushGuild | null; + public declare readonly guild: If<Cached, BushGuild>; public declare readonly member: BushGuildMember | null; public declare author: BushUser; public declare readonly channel: If<Cached, BushGuildTextBasedChannel, BushTextBasedChannels>; diff --git a/src/lib/models/Guild.ts b/src/lib/models/Guild.ts index 02f487b..50113bf 100644 --- a/src/lib/models/Guild.ts +++ b/src/lib/models/Guild.ts @@ -280,6 +280,10 @@ export const guildFeaturesObj = asGuildFeature({ name: 'Exclude Automod Scam Links', description: 'Opt out of having automod delete scam links.' }, + delScamMentions: { + name: 'Delete Scam Mentions', + description: 'Deletes messages with @everyone and @here mentions that have common scam phrases.' + }, autoPublish: { name: 'Auto Publish', description: 'Publishes messages in configured announcement channels.' |