diff --git a/backend/src/plugins/Automod/AutomodPlugin.ts b/backend/src/plugins/Automod/AutomodPlugin.ts index 3edffc37..b8f40f0e 100644 --- a/backend/src/plugins/Automod/AutomodPlugin.ts +++ b/backend/src/plugins/Automod/AutomodPlugin.ts @@ -16,6 +16,9 @@ import { clearOldRecentSpam } from "./functions/clearOldRecentSpam"; import { GuildAntiraidLevels } from "../../data/GuildAntiraidLevels"; import { GuildArchives } from "../../data/GuildArchives"; import { clearOldRecentNicknameChanges } from "./functions/clearOldNicknameChanges"; +import { LogsPlugin } from "../Logs/LogsPlugin"; +import { ModActionsPlugin } from "../ModActions/ModActionsPlugin"; +import { MutesPlugin } from "../Mutes/MutesPlugin"; const defaultOptions = { config: { @@ -106,16 +109,22 @@ const configPreprocessor: ConfigPreprocessorFn = options => { }; export const AutomodPlugin = zeppelinPlugin()("automod", { + dependencies: [LogsPlugin, ModActionsPlugin, MutesPlugin], + configSchema: ConfigSchema, defaultOptions, configPreprocessor, + customOverrideMatcher(pluginData, criteria, matchParams) { + return criteria?.antiraid_level && criteria.antiraid_level === pluginData.state.cachedAntiraidLevel; + }, + events: [ RunAutomodOnJoinEvt, // Messages use message events from SavedMessages, see onLoad below ], - onLoad(pluginData) { + async onLoad(pluginData) { pluginData.state.queue = new Queue(); pluginData.state.recentActions = []; @@ -130,8 +139,6 @@ export const AutomodPlugin = zeppelinPlugin()("automod", { 30 * SECONDS, ); - pluginData.state.cachedAntiraidLevel = null; // TODO - pluginData.state.logs = new GuildLogs(pluginData.guild.id); pluginData.state.savedMessages = GuildSavedMessages.getGuildInstance(pluginData.guild.id); pluginData.state.antiraidLevels = GuildAntiraidLevels.getGuildInstance(pluginData.guild.id); @@ -142,6 +149,8 @@ export const AutomodPlugin = zeppelinPlugin()("automod", { pluginData.state.onMessageUpdateFn = message => runAutomodOnMessage(pluginData, message, true); pluginData.state.savedMessages.events.on("update", pluginData.state.onMessageUpdateFn); + + pluginData.state.cachedAntiraidLevel = await pluginData.state.antiraidLevels.get(); }, onUnload(pluginData) { diff --git a/backend/src/plugins/Automod/actions/alert.ts b/backend/src/plugins/Automod/actions/alert.ts index a14a2843..43287712 100644 --- a/backend/src/plugins/Automod/actions/alert.ts +++ b/backend/src/plugins/Automod/actions/alert.ts @@ -6,6 +6,7 @@ import { resolveActionContactMethods } from "../functions/resolveActionContactMe import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin"; import { TextChannel } from "eris"; import { renderTemplate } from "../../../templateFormatter"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; export const AlertAction = automodAction({ configType: t.type({ @@ -15,6 +16,7 @@ export const AlertAction = automodAction({ async apply({ pluginData, contexts, actionConfig, ruleName, matchResult }) { const channel = pluginData.guild.channels.get(actionConfig.channel); + const logs = pluginData.getPlugin(LogsPlugin); if (channel && channel instanceof TextChannel) { const text = actionConfig.text; @@ -23,26 +25,31 @@ export const AlertAction = automodAction({ const safeUsers = contexts.map(c => c.user && stripObjectToScalars(c.user)).filter(Boolean); const safeUser = safeUsers[0]; + const actionsTaken = Object.keys(pluginData.config.get().rules[ruleName].actions); - const takenActions = Object.keys(pluginData.config.get().rules[ruleName].actions); - // TODO: Generate logMessage - const logMessage = ""; + const logMessage = logs.getLogMessage(LogType.AUTOMOD_ACTION, { + rule: ruleName, + user: safeUser, + users: safeUsers, + actionsTaken, + matchSummary: matchResult.summary, + }); const rendered = await renderTemplate(actionConfig.text, { rule: ruleName, user: safeUser, users: safeUsers, text, + actionsTaken, matchSummary: matchResult.summary, messageLink: theMessageLink, logMessage, }); channel.createMessage(rendered); } else { - // TODO: Post BOT_ALERT log - /*this.getLogs().log(LogType.BOT_ALERT, { - body: `Invalid channel id \`${actionConfig.channel}\` for alert action in automod rule **${rule.name}**`, - });*/ + logs.log(LogType.BOT_ALERT, { + body: `Invalid channel id \`${actionConfig.channel}\` for alert action in automod rule **${ruleName}**`, + }); } }, }); diff --git a/backend/src/plugins/Automod/actions/ban.ts b/backend/src/plugins/Automod/actions/ban.ts index 8f433023..2fd2e025 100644 --- a/backend/src/plugins/Automod/actions/ban.ts +++ b/backend/src/plugins/Automod/actions/ban.ts @@ -13,7 +13,7 @@ export const BanAction = automodAction({ deleteMessageDays: tNullable(t.number), }), - async apply({ pluginData, contexts, actionConfig }) { + async apply({ pluginData, contexts, actionConfig, matchResult }) { const reason = actionConfig.reason || "Kicked automatically"; const contactMethods = resolveActionContactMethods(pluginData, actionConfig); const deleteMessageDays = actionConfig.deleteMessageDays; @@ -21,7 +21,7 @@ export const BanAction = automodAction({ const caseArgs = { modId: pluginData.client.user.id, extraNotes: [ - /* TODO */ + matchResult.summary, // TODO ], }; diff --git a/backend/src/plugins/Automod/actions/changeNickname.ts b/backend/src/plugins/Automod/actions/changeNickname.ts index 061bf98a..46358aa2 100644 --- a/backend/src/plugins/Automod/actions/changeNickname.ts +++ b/backend/src/plugins/Automod/actions/changeNickname.ts @@ -1,9 +1,7 @@ import * as t from "io-ts"; import { automodAction } from "../helpers"; import { LogType } from "../../../data/LogType"; -import { asyncMap, resolveMember, tNullable } from "../../../utils"; -import { resolveActionContactMethods } from "../functions/resolveActionContactMethods"; -import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; export const ChangeNicknameAction = automodAction({ configType: t.type({ @@ -18,7 +16,9 @@ export const ChangeNicknameAction = automodAction({ if (pluginData.state.recentNicknameChanges.has(member.id)) continue; member.edit({ nick: actionConfig.name }).catch(err => { - /* TODO: Log this error */ + pluginData.getPlugin(LogsPlugin).log(LogType.BOT_ALERT, { + body: `Failed to change the nickname of \`${member.id}\``, + }); }); pluginData.state.recentNicknameChanges.set(member.id, { timestamp: Date.now() }); diff --git a/backend/src/plugins/Automod/actions/kick.ts b/backend/src/plugins/Automod/actions/kick.ts index 09cb832f..88c6e488 100644 --- a/backend/src/plugins/Automod/actions/kick.ts +++ b/backend/src/plugins/Automod/actions/kick.ts @@ -12,14 +12,14 @@ export const KickAction = automodAction({ notifyChannel: tNullable(t.string), }), - async apply({ pluginData, contexts, actionConfig }) { + async apply({ pluginData, contexts, actionConfig, matchResult }) { const reason = actionConfig.reason || "Kicked automatically"; const contactMethods = resolveActionContactMethods(pluginData, actionConfig); const caseArgs = { modId: pluginData.client.user.id, extraNotes: [ - /* TODO */ + matchResult.summary, // TODO ], }; diff --git a/backend/src/plugins/Automod/actions/log.ts b/backend/src/plugins/Automod/actions/log.ts index a2038412..797b5080 100644 --- a/backend/src/plugins/Automod/actions/log.ts +++ b/backend/src/plugins/Automod/actions/log.ts @@ -1,10 +1,23 @@ import * as t from "io-ts"; import { automodAction } from "../helpers"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; +import { LogType } from "../../../data/LogType"; +import { stripObjectToScalars } from "../../../utils"; export const LogAction = automodAction({ configType: t.boolean, - async apply({ pluginData, contexts, actionConfig }) { - // TODO: Everything + async apply({ pluginData, contexts, ruleName, matchResult }) { + const safeUsers = contexts.map(c => c.user && stripObjectToScalars(c.user)).filter(Boolean); + const safeUser = safeUsers[0]; + const actionsTaken = Object.keys(pluginData.config.get().rules[ruleName].actions); + + pluginData.getPlugin(LogsPlugin).log(LogType.AUTOMOD_ACTION, { + rule: ruleName, + user: safeUser, + users: safeUsers, + actionsTaken, + matchSummary: matchResult.summary, + }); }, }); diff --git a/backend/src/plugins/Automod/actions/mute.ts b/backend/src/plugins/Automod/actions/mute.ts index bf67a269..9ae042eb 100644 --- a/backend/src/plugins/Automod/actions/mute.ts +++ b/backend/src/plugins/Automod/actions/mute.ts @@ -14,7 +14,7 @@ export const MuteAction = automodAction({ notifyChannel: tNullable(t.string), }), - async apply({ pluginData, contexts, actionConfig }) { + async apply({ pluginData, contexts, actionConfig, matchResult }) { const duration = actionConfig.duration ? convertDelayStringToMS(actionConfig.duration) : null; const reason = actionConfig.reason || "Muted automatically"; const contactMethods = resolveActionContactMethods(pluginData, actionConfig); @@ -22,7 +22,7 @@ export const MuteAction = automodAction({ const caseArgs = { modId: pluginData.client.user.id, extraNotes: [ - /* TODO */ + matchResult.summary, // TODO ], }; diff --git a/backend/src/plugins/Automod/actions/warn.ts b/backend/src/plugins/Automod/actions/warn.ts index 2062d2a2..7e17c841 100644 --- a/backend/src/plugins/Automod/actions/warn.ts +++ b/backend/src/plugins/Automod/actions/warn.ts @@ -12,14 +12,14 @@ export const WarnAction = automodAction({ notifyChannel: tNullable(t.string), }), - async apply({ pluginData, contexts, actionConfig }) { + async apply({ pluginData, contexts, actionConfig, matchResult }) { const reason = actionConfig.reason || "Warned automatically"; const contactMethods = resolveActionContactMethods(pluginData, actionConfig); const caseArgs = { modId: pluginData.client.user.id, extraNotes: [ - /* TODO */ + matchResult.summary, // TODO ], }; diff --git a/backend/src/plugins/Automod/functions/setAntiraidLevel.ts b/backend/src/plugins/Automod/functions/setAntiraidLevel.ts index 913f66e5..87031471 100644 --- a/backend/src/plugins/Automod/functions/setAntiraidLevel.ts +++ b/backend/src/plugins/Automod/functions/setAntiraidLevel.ts @@ -1,6 +1,9 @@ import { User } from "eris"; import { PluginData } from "knub"; import { AutomodPluginType } from "../types"; +import { LogsPlugin } from "../../Logs/LogsPlugin"; +import { LogType } from "../../../data/LogType"; +import { stripObjectToScalars } from "../../../utils"; export async function setAntiraidLevel( pluginData: PluginData, @@ -10,9 +13,16 @@ export async function setAntiraidLevel( pluginData.state.cachedAntiraidLevel = newLevel; await pluginData.state.antiraidLevels.set(newLevel); + const logs = pluginData.getPlugin(LogsPlugin); + if (user) { - // TODO: Log user action + logs.log(LogType.SET_ANTIRAID_USER, { + level: newLevel ?? "off", + user: stripObjectToScalars(user), + }); } else { - // TODO: Log automatic action + logs.log(LogType.SET_ANTIRAID_AUTO, { + level: newLevel ?? "off", + }); } } diff --git a/backend/src/plugins/Automod/types.ts b/backend/src/plugins/Automod/types.ts index f77028e6..bf92fe92 100644 --- a/backend/src/plugins/Automod/types.ts +++ b/backend/src/plugins/Automod/types.ts @@ -34,6 +34,11 @@ export type TConfigSchema = t.TypeOf; export interface AutomodPluginType extends BasePluginType { config: TConfigSchema; + + customOverrideCriteria: { + antiraid_level?: string; + }; + state: { /** * Automod checks/actions are handled in a queue so we don't get overlap on the same user diff --git a/backend/src/plugins/Logs/LogsPlugin.ts b/backend/src/plugins/Logs/LogsPlugin.ts index d2c3c380..56c0088d 100644 --- a/backend/src/plugins/Logs/LogsPlugin.ts +++ b/backend/src/plugins/Logs/LogsPlugin.ts @@ -17,6 +17,8 @@ import { LogsChannelCreateEvt, LogsChannelDeleteEvt } from "./events/LogsChannel import { LogsRoleCreateEvt, LogsRoleDeleteEvt } from "./events/LogsRoleModifyEvts"; import { LogsVoiceJoinEvt, LogsVoiceLeaveEvt, LogsVoiceSwitchEvt } from "./events/LogsVoiceChannelEvts"; import { log } from "./util/log"; +import { LogType } from "../../data/LogType"; +import { getLogMessage } from "./util/getLogMessage"; const defaultOptions: PluginOptions = { config: { @@ -58,6 +60,20 @@ export const LogsPlugin = zeppelinPlugin()("logs", { LogsVoiceSwitchEvt, ], + public: { + log(pluginData) { + return (type: LogType, data: any) => { + return log(pluginData, type, data); + }; + }, + + getLogMessage(pluginData) { + return (type: LogType, data: any) => { + return getLogMessage(pluginData, type, data); + }; + }, + }, + onLoad(pluginData) { const { state, guild } = pluginData; diff --git a/backend/src/plugins/Logs/util/getLogMessage.ts b/backend/src/plugins/Logs/util/getLogMessage.ts index f7501fd9..b2ca3cb7 100644 --- a/backend/src/plugins/Logs/util/getLogMessage.ts +++ b/backend/src/plugins/Logs/util/getLogMessage.ts @@ -7,7 +7,7 @@ import { renderTemplate, TemplateParseError } from "src/templateFormatter"; import { logger } from "src/logger"; import moment from "moment-timezone"; -export async function getLogMessage(pluginData: PluginData, type, data): Promise { +export async function getLogMessage(pluginData: PluginData, type: LogType, data: any): Promise { const config = pluginData.config.get(); const format = config.format[LogType[type]] || ""; if (format === "") return; diff --git a/backend/src/plugins/Logs/util/log.ts b/backend/src/plugins/Logs/util/log.ts index 2c365967..59fa2a64 100644 --- a/backend/src/plugins/Logs/util/log.ts +++ b/backend/src/plugins/Logs/util/log.ts @@ -5,7 +5,7 @@ import { TextChannel } from "eris"; import { createChunkedMessage, noop } from "src/utils"; import { getLogMessage } from "./getLogMessage"; -export async function log(pluginData: PluginData, type, data) { +export async function log(pluginData: PluginData, type: LogType, data: any) { const logChannels: TLogChannelMap = pluginData.config.get().channels; const typeStr = LogType[type];