mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-05-10 12:25:02 +00:00
Run user-supplied regexes in worker threads with a timeout
This commit is contained in:
parent
19b97bc32b
commit
a7fa258f2a
15 changed files with 237 additions and 43 deletions
|
@ -19,6 +19,9 @@ import { LogsVoiceJoinEvt, LogsVoiceLeaveEvt, LogsVoiceSwitchEvt } from "./event
|
|||
import { log } from "./util/log";
|
||||
import { LogType } from "../../data/LogType";
|
||||
import { getLogMessage } from "./util/getLogMessage";
|
||||
import { discardRegExpRunner, getRegExpRunner } from "../../regExpRunners";
|
||||
import { disableCodeBlocks } from "../../utils";
|
||||
import { logger } from "../../logger";
|
||||
|
||||
const defaultOptions: PluginOptions<LogsPluginType> = {
|
||||
config: {
|
||||
|
@ -100,6 +103,24 @@ export const LogsPlugin = zeppelinPlugin<LogsPluginType>()("logs", {
|
|||
|
||||
state.onMessageUpdateFn = (newMsg, oldMsg) => onMessageUpdate(pluginData, newMsg, oldMsg);
|
||||
state.savedMessages.events.on("update", state.onMessageUpdateFn);
|
||||
|
||||
state.regexRunner = getRegExpRunner(`guild-${pluginData.guild.id}`);
|
||||
state.regexRunnerTimeoutListener = (regexSource, timeoutMs) => {
|
||||
logger.warn(`Heavy regex (${timeoutMs}): ${regexSource}`);
|
||||
};
|
||||
state.regexRunnerRepeatedTimeoutListener = (regexSource, timeoutMs, failedTimes) => {
|
||||
log(pluginData, LogType.BOT_ALERT, {
|
||||
body:
|
||||
`
|
||||
The following regex has taken longer than ${timeoutMs}ms for ${failedTimes} times and has been temporarily disabled:
|
||||
`.trim() +
|
||||
"\n```" +
|
||||
disableCodeBlocks(regexSource) +
|
||||
"```",
|
||||
});
|
||||
};
|
||||
state.regexRunner.on("timeout", state.regexRunnerTimeoutListener);
|
||||
state.regexRunner.on("repeatedTimeout", state.regexRunnerRepeatedTimeoutListener);
|
||||
},
|
||||
|
||||
onUnload(pluginData) {
|
||||
|
@ -108,5 +129,9 @@ export const LogsPlugin = zeppelinPlugin<LogsPluginType>()("logs", {
|
|||
pluginData.state.savedMessages.events.off("delete", pluginData.state.onMessageDeleteFn);
|
||||
pluginData.state.savedMessages.events.off("deleteBulk", pluginData.state.onMessageDeleteBulkFn);
|
||||
pluginData.state.savedMessages.events.off("update", pluginData.state.onMessageUpdateFn);
|
||||
|
||||
pluginData.state.regexRunner.off("timeout", pluginData.state.regexRunnerTimeoutListener);
|
||||
pluginData.state.regexRunner.off("repeatedTimeout", pluginData.state.regexRunnerRepeatedTimeoutListener);
|
||||
discardRegExpRunner(`guild-${pluginData.guild.id}`);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import * as t from "io-ts";
|
||||
import { BasePluginType, eventListener } from "knub";
|
||||
import { TSafeRegex } from "src/validatorUtils";
|
||||
import { TRegex } from "src/validatorUtils";
|
||||
import { GuildLogs } from "src/data/GuildLogs";
|
||||
import { GuildSavedMessages } from "src/data/GuildSavedMessages";
|
||||
import { GuildArchives } from "src/data/GuildArchives";
|
||||
import { GuildCases } from "src/data/GuildCases";
|
||||
import { tMessageContent, tNullable } from "../../utils";
|
||||
import { RegExpRunner } from "../../RegExpRunner";
|
||||
|
||||
export const tLogFormats = t.record(t.string, t.union([t.string, tMessageContent]));
|
||||
export type TLogFormats = t.TypeOf<typeof tLogFormats>;
|
||||
|
@ -16,7 +17,7 @@ const LogChannel = t.partial({
|
|||
batched: t.boolean,
|
||||
batch_time: t.number,
|
||||
excluded_users: t.array(t.string),
|
||||
excluded_message_regexes: t.array(TSafeRegex),
|
||||
excluded_message_regexes: t.array(TRegex),
|
||||
excluded_channels: t.array(t.string),
|
||||
format: tNullable(tLogFormats),
|
||||
});
|
||||
|
@ -45,6 +46,10 @@ export interface LogsPluginType extends BasePluginType {
|
|||
archives: GuildArchives;
|
||||
cases: GuildCases;
|
||||
|
||||
regexRunner: RegExpRunner;
|
||||
regexRunnerTimeoutListener;
|
||||
regexRunnerRepeatedTimeoutListener;
|
||||
|
||||
logListener;
|
||||
|
||||
batches: Map<string, string[]>;
|
||||
|
|
|
@ -4,6 +4,7 @@ import { LogType } from "src/data/LogType";
|
|||
import { TextChannel } from "eris";
|
||||
import { createChunkedMessage, noop } from "src/utils";
|
||||
import { getLogMessage } from "./getLogMessage";
|
||||
import { allowTimeout } from "../../../RegExpRunner";
|
||||
|
||||
const excludedUserProps = ["user", "member", "mod"];
|
||||
|
||||
|
@ -45,7 +46,8 @@ export async function log(pluginData: PluginData<LogsPluginType>, type: LogType,
|
|||
// If this entry contains a message with an excluded regex, skip it
|
||||
if (type === LogType.MESSAGE_DELETE && opts.excluded_message_regexes && data.message.data.content) {
|
||||
for (const regex of opts.excluded_message_regexes) {
|
||||
if (regex.test(data.message.data.content)) {
|
||||
const matches = await pluginData.state.regexRunner.exec(regex, data.message.data.content).catch(allowTimeout);
|
||||
if (matches) {
|
||||
continue logChannelLoop;
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +55,8 @@ export async function log(pluginData: PluginData<LogsPluginType>, type: LogType,
|
|||
|
||||
if (type === LogType.MESSAGE_EDIT && opts.excluded_message_regexes && data.before.data.content) {
|
||||
for (const regex of opts.excluded_message_regexes) {
|
||||
if (regex.test(data.before.data.content)) {
|
||||
const matches = await pluginData.state.regexRunner.exec(regex, data.message.data.content).catch(allowTimeout);
|
||||
if (matches) {
|
||||
continue logChannelLoop;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue