From 61804d9e64b5d778f57d3332b1db5b58c0b33002 Mon Sep 17 00:00:00 2001 From: Dragory <2606411+Dragory@users.noreply.github.com> Date: Sun, 13 Sep 2020 22:45:02 +0300 Subject: [PATCH] Improve error handling with mutes --- backend/src/plugins/Automod/actions/mute.ts | 16 ++++++++-- backend/src/plugins/Spam/SpamPlugin.ts | 3 ++ .../Spam/util/logAndDetectMessageSpam.ts | 32 +++++++++++++------ .../Spam/util/logAndDetectOtherSpam.ts | 28 ++++++++++++---- 4 files changed, 61 insertions(+), 18 deletions(-) diff --git a/backend/src/plugins/Automod/actions/mute.ts b/backend/src/plugins/Automod/actions/mute.ts index aa9d4443..b26d7fc5 100644 --- a/backend/src/plugins/Automod/actions/mute.ts +++ b/backend/src/plugins/Automod/actions/mute.ts @@ -5,6 +5,8 @@ import { asyncMap, convertDelayStringToMS, resolveMember, tDelayString, tNullabl import { resolveActionContactMethods } from "../functions/resolveActionContactMethods"; import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin"; import { MutesPlugin } from "../../Mutes/MutesPlugin"; +import { ERRORS, RecoverablePluginError } from "../../../RecoverablePluginError"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; export const MuteAction = automodAction({ configType: t.type({ @@ -18,7 +20,7 @@ export const MuteAction = automodAction({ notify: null, // Use defaults from ModActions }, - async apply({ pluginData, contexts, actionConfig, matchResult }) { + async apply({ pluginData, contexts, actionConfig, ruleName, matchResult }) { const duration = actionConfig.duration ? convertDelayStringToMS(actionConfig.duration) : null; const reason = actionConfig.reason || "Muted automatically"; const contactMethods = resolveActionContactMethods(pluginData, actionConfig); @@ -32,7 +34,17 @@ export const MuteAction = automodAction({ const mutes = pluginData.getPlugin(MutesPlugin); for (const userId of userIdsToMute) { - await mutes.muteUser(userId, duration, reason, { contactMethods, caseArgs }); + try { + await mutes.muteUser(userId, duration, reason, { contactMethods, caseArgs }); + } catch (e) { + if (e instanceof RecoverablePluginError && e.code === ERRORS.NO_MUTE_ROLE_IN_CONFIG) { + pluginData.getPlugin(LogsPlugin).log(LogType.BOT_ALERT, { + body: `Failed to mute <@!${userId}> in Automod rule \`${ruleName}\` because a mute role has not been specified in server config`, + }); + } else { + throw e; + } + } } }, }); diff --git a/backend/src/plugins/Spam/SpamPlugin.ts b/backend/src/plugins/Spam/SpamPlugin.ts index e174042e..31abb3fb 100644 --- a/backend/src/plugins/Spam/SpamPlugin.ts +++ b/backend/src/plugins/Spam/SpamPlugin.ts @@ -9,6 +9,7 @@ import { onMessageCreate } from "./util/onMessageCreate"; import { clearOldRecentActions } from "./util/clearOldRecentActions"; import { SpamVoiceJoinEvt, SpamVoiceSwitchEvt } from "./events/SpamVoiceEvt"; import { trimPluginDescription } from "../../utils"; +import { LogsPlugin } from "../Logs/LogsPlugin"; const defaultOptions: PluginOptions = { config: { @@ -51,6 +52,8 @@ export const SpamPlugin = zeppelinPlugin()("spam", { `), }, + dependencies: [LogsPlugin], + configSchema: ConfigSchema, defaultOptions, diff --git a/backend/src/plugins/Spam/util/logAndDetectMessageSpam.ts b/backend/src/plugins/Spam/util/logAndDetectMessageSpam.ts index 801b8510..4979d3c7 100644 --- a/backend/src/plugins/Spam/util/logAndDetectMessageSpam.ts +++ b/backend/src/plugins/Spam/util/logAndDetectMessageSpam.ts @@ -1,8 +1,8 @@ import { SavedMessage } from "src/data/entities/SavedMessage"; -import { RecentActionType, TBaseSingleSpamConfig, SpamPluginType } from "../types"; +import { RecentActionType, SpamPluginType, TBaseSingleSpamConfig } from "../types"; import moment from "moment-timezone"; import { MuteResult } from "src/plugins/Mutes/types"; -import { convertDelayStringToMS, trimLines, stripObjectToScalars, resolveMember, noop, DBDateFormat } from "src/utils"; +import { convertDelayStringToMS, DBDateFormat, noop, resolveMember, stripObjectToScalars, trimLines } from "src/utils"; import { LogType } from "src/data/LogType"; import { CaseTypes } from "src/data/CaseTypes"; import { logger } from "src/logger"; @@ -14,6 +14,8 @@ import { getRecentActionCount } from "./getRecentActionCount"; import { getRecentActions } from "./getRecentActions"; import { clearRecentUserActions } from "./clearRecentUserActions"; import { saveSpamArchives } from "./saveSpamArchives"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; +import { ERRORS, RecoverablePluginError } from "../../../RecoverablePluginError"; export async function logAndDetectMessageSpam( pluginData: PluginData, @@ -38,6 +40,7 @@ export async function logAndDetectMessageSpam( async () => { const timestamp = moment.utc(savedMessage.posted_at, DBDateFormat).valueOf(); const member = await resolveMember(pluginData.client, pluginData.guild, savedMessage.user_id); + const logs = pluginData.getPlugin(LogsPlugin); // Log this action... addRecentAction( @@ -69,12 +72,23 @@ export async function logAndDetectMessageSpam( if (spamConfig.mute && member) { const mutesPlugin = pluginData.getPlugin(MutesPlugin); const muteTime = spamConfig.mute_time ? convertDelayStringToMS(spamConfig.mute_time.toString()) : 120 * 1000; - muteResult = await mutesPlugin.muteUser(member.id, muteTime, "Automatic spam detection", { - caseArgs: { - modId: pluginData.client.user.id, - postInCaseLogOverride: false, - }, - }); + + try { + muteResult = await mutesPlugin.muteUser(member.id, muteTime, "Automatic spam detection", { + caseArgs: { + modId: pluginData.client.user.id, + postInCaseLogOverride: false, + }, + }); + } catch (e) { + if (e instanceof RecoverablePluginError && e.code === ERRORS.NO_MUTE_ROLE_IN_CONFIG) { + logs.log(LogType.BOT_ALERT, { + body: `Failed to mute <@!${member.id}> in \`spam\` plugin because a mute role has not been specified in server config`, + }); + } else { + throw e; + } + } } // Get the offending message IDs @@ -150,7 +164,7 @@ export async function logAndDetectMessageSpam( } // Create a log entry - pluginData.state.logs.log(LogType.MESSAGE_SPAM_DETECTED, { + logs.log(LogType.MESSAGE_SPAM_DETECTED, { member: stripObjectToScalars(member, ["user", "roles"]), channel: stripObjectToScalars(channel), description, diff --git a/backend/src/plugins/Spam/util/logAndDetectOtherSpam.ts b/backend/src/plugins/Spam/util/logAndDetectOtherSpam.ts index 7fc55d54..ec44dbc1 100644 --- a/backend/src/plugins/Spam/util/logAndDetectOtherSpam.ts +++ b/backend/src/plugins/Spam/util/logAndDetectOtherSpam.ts @@ -8,6 +8,8 @@ import { CasesPlugin } from "src/plugins/Cases/CasesPlugin"; import { CaseTypes } from "src/data/CaseTypes"; import { clearRecentUserActions } from "./clearRecentUserActions"; import { LogType } from "src/data/LogType"; +import { ERRORS, RecoverablePluginError } from "../../../RecoverablePluginError"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; export async function logAndDetectOtherSpam( pluginData: PluginData, @@ -31,16 +33,28 @@ export async function logAndDetectOtherSpam( if (recentActionsCount > spamConfig.count) { const member = await resolveMember(pluginData.client, pluginData.guild, userId); const details = `${description} (over ${spamConfig.count} in ${spamConfig.interval}s)`; + const logs = pluginData.getPlugin(LogsPlugin); if (spamConfig.mute && member) { const mutesPlugin = pluginData.getPlugin(MutesPlugin); const muteTime = spamConfig.mute_time ? convertDelayStringToMS(spamConfig.mute_time.toString()) : 120 * 1000; - await mutesPlugin.muteUser(member.id, muteTime, "Automatic spam detection", { - caseArgs: { - modId: pluginData.client.user.id, - extraNotes: [`Details: ${details}`], - }, - }); + + try { + await mutesPlugin.muteUser(member.id, muteTime, "Automatic spam detection", { + caseArgs: { + modId: pluginData.client.user.id, + extraNotes: [`Details: ${details}`], + }, + }); + } catch (e) { + if (e instanceof RecoverablePluginError && e.code === ERRORS.NO_MUTE_ROLE_IN_CONFIG) { + logs.log(LogType.BOT_ALERT, { + body: `Failed to mute <@!${member.id}> in \`spam\` plugin because a mute role has not been specified in server config`, + }); + } else { + throw e; + } + } } else { // If we're not muting the user, just add a note on them const casesPlugin = pluginData.getPlugin(CasesPlugin); @@ -55,7 +69,7 @@ export async function logAndDetectOtherSpam( // Clear recent cases clearRecentUserActions(pluginData, RecentActionType.VoiceChannelMove, userId, actionGroupId); - pluginData.state.logs.log(LogType.OTHER_SPAM_DETECTED, { + logs.log(LogType.OTHER_SPAM_DETECTED, { member: stripObjectToScalars(member, ["user", "roles"]), description, limit: spamConfig.count,