Counters v0.9

Includes automod trigger/action. No user-facing commands yet.
This commit is contained in:
Dragory 2021-02-13 17:29:10 +02:00
parent ec37cf27a2
commit c3407e2d5d
No known key found for this signature in database
GPG key ID: 5F387BA66DF8AAC1
29 changed files with 1387 additions and 3 deletions

View file

@ -28,6 +28,9 @@ import { LogType } from "../../data/LogType";
import { logger } from "../../logger";
import { discardRegExpRunner, getRegExpRunner } from "../../regExpRunners";
import { RunAutomodOnMemberUpdate } from "./events/RunAutomodOnMemberUpdate";
import { CountersPlugin } from "../Counters/CountersPlugin";
import { parseCondition } from "../../data/GuildCounters";
import { runAutomodOnCounterTrigger } from "./events/runAutomodOnCounterTrigger";
const defaultOptions = {
config: {
@ -53,7 +56,7 @@ const defaultOptions = {
};
/**
* Config preprocessor to set default values for triggers
* Config preprocessor to set default values for triggers and perform extra validation
*/
const configPreprocessor: ConfigPreprocessorFn<AutomodPluginType> = options => {
if (options.config?.rules) {
@ -108,6 +111,15 @@ const configPreprocessor: ConfigPreprocessorFn<AutomodPluginType> = options => {
]);
}
}
if (triggerName === "counter") {
const parsedCondition = parseCondition(triggerObj[triggerName]!.condition);
if (parsedCondition == null) {
throw new StrictValidationError([
`Invalid counter condition '${triggerObj[triggerName]!.condition}' in rule <${rule.name}>`,
]);
}
}
}
}
}
@ -151,7 +163,7 @@ export const AutomodPlugin = zeppelinGuildPlugin<AutomodPluginType>()("automod",
showInDocs: true,
info: pluginInfo,
dependencies: [LogsPlugin, ModActionsPlugin, MutesPlugin],
dependencies: [LogsPlugin, ModActionsPlugin, MutesPlugin, CountersPlugin],
configSchema: ConfigSchema,
defaultOptions,
@ -204,6 +216,39 @@ export const AutomodPlugin = zeppelinGuildPlugin<AutomodPluginType>()("automod",
pluginData.state.cachedAntiraidLevel = await pluginData.state.antiraidLevels.get();
},
async onAfterLoad(pluginData) {
const countersPlugin = pluginData.getPlugin(CountersPlugin);
pluginData.state.onCounterTrigger = (name, condition, channelId, userId) => {
console.log("trigger", name, condition, channelId, userId);
runAutomodOnCounterTrigger(pluginData, name, condition, channelId, userId, false);
};
pluginData.state.onCounterReverseTrigger = (name, condition, channelId, userId) => {
console.log("reverseTrigger", name, condition, channelId, userId);
runAutomodOnCounterTrigger(pluginData, name, condition, channelId, userId, true);
};
const config = pluginData.config.get();
for (const rule of Object.values(config.rules)) {
for (const trigger of rule.triggers) {
if (trigger.counter) {
await countersPlugin.initCounterTrigger(trigger.counter.name, trigger.counter.condition);
}
}
}
countersPlugin.onCounterEvent("trigger", pluginData.state.onCounterTrigger);
countersPlugin.onCounterEvent("reverseTrigger", pluginData.state.onCounterReverseTrigger);
},
async onBeforeUnload(pluginData) {
const countersPlugin = pluginData.getPlugin(CountersPlugin);
countersPlugin.offCounterEvent("trigger", pluginData.state.onCounterTrigger);
countersPlugin.offCounterEvent("reverseTrigger", pluginData.state.onCounterReverseTrigger);
},
async onUnload(pluginData) {
pluginData.state.queue.clear();

View file

@ -12,6 +12,7 @@ import { AddRolesAction } from "./addRoles";
import { RemoveRolesAction } from "./removeRoles";
import { SetAntiraidLevelAction } from "./setAntiraidLevel";
import { ReplyAction } from "./reply";
import { ChangeCounterAction } from "./changeCounter";
export const availableActions: Record<string, AutomodActionBlueprint<any>> = {
clean: CleanAction,
@ -26,6 +27,7 @@ export const availableActions: Record<string, AutomodActionBlueprint<any>> = {
remove_roles: RemoveRolesAction,
set_antiraid_level: SetAntiraidLevelAction,
reply: ReplyAction,
change_counter: ChangeCounterAction,
};
export const AvailableActions = t.type({
@ -41,4 +43,5 @@ export const AvailableActions = t.type({
remove_roles: RemoveRolesAction.configType,
set_antiraid_level: SetAntiraidLevelAction.configType,
reply: ReplyAction.configType,
change_counter: ChangeCounterAction.configType,
});

View file

@ -0,0 +1,27 @@
import * as t from "io-ts";
import { automodAction } from "../helpers";
import { CountersPlugin } from "../../Counters/CountersPlugin";
export const ChangeCounterAction = automodAction({
configType: t.type({
name: t.string,
change: t.string,
}),
defaultConfig: {},
async apply({ pluginData, contexts, actionConfig, matchResult }) {
const change = parseInt(actionConfig.change, 10);
if (Number.isNaN(change)) {
throw new Error("Invalid change number");
}
const countersPlugin = pluginData.getPlugin(CountersPlugin);
countersPlugin.changeCounterValue(
actionConfig.name,
contexts[0].message?.channel_id || null,
contexts[0].user?.id || null,
change,
);
},
});

View file

@ -0,0 +1,27 @@
import { GuildPluginData } from "knub";
import { AutomodContext, AutomodPluginType } from "../types";
import { runAutomod } from "../functions/runAutomod";
export function runAutomodOnCounterTrigger(
pluginData: GuildPluginData<AutomodPluginType>,
counterName: string,
condition: string,
channelId: string | null,
userId: string | null,
reverse: boolean,
) {
const context: AutomodContext = {
timestamp: Date.now(),
counterTrigger: {
name: counterName,
condition,
channelId,
userId,
reverse,
},
};
pluginData.state.queue.add(async () => {
await runAutomod(pluginData, context);
});
}

View file

@ -24,7 +24,7 @@ export async function runAutomod(pluginData: GuildPluginData<AutomodPluginType>,
for (const [ruleName, rule] of Object.entries(config.rules)) {
if (rule.enabled === false) continue;
if (!rule.affects_bots && (!user || user.bot)) continue;
if (!rule.affects_bots && (!user || user.bot) && !context.counterTrigger) continue;
if (rule.cooldown && checkAndUpdateCooldown(pluginData, rule, context)) {
return;

View file

@ -17,6 +17,7 @@ import { MemberJoinTrigger } from "./memberJoin";
import { RoleAddedTrigger } from "./roleAdded";
import { RoleRemovedTrigger } from "./roleRemoved";
import { StickerSpamTrigger } from "./stickerSpam";
import { CounterTrigger } from "./counter";
export const availableTriggers: Record<string, AutomodTriggerBlueprint<any, any>> = {
match_words: MatchWordsTrigger,
@ -37,6 +38,8 @@ export const availableTriggers: Record<string, AutomodTriggerBlueprint<any, any>
character_spam: CharacterSpamTrigger,
member_join_spam: MemberJoinSpamTrigger,
sticker_spam: StickerSpamTrigger,
counter: CounterTrigger,
};
export const AvailableTriggers = t.type({
@ -58,4 +61,6 @@ export const AvailableTriggers = t.type({
character_spam: CharacterSpamTrigger.configType,
member_join_spam: MemberJoinSpamTrigger.configType,
sticker_spam: StickerSpamTrigger.configType,
counter: CounterTrigger.configType,
});

View file

@ -0,0 +1,46 @@
import * as t from "io-ts";
import { automodTrigger } from "../helpers";
import { consumeIgnoredRoleChange } from "../functions/ignoredRoleChanges";
import { CountersPlugin } from "../../Counters/CountersPlugin";
import { tNullable } from "../../../utils";
// tslint:disable-next-line
interface CounterTriggerResult {}
export const CounterTrigger = automodTrigger<CounterTriggerResult>()({
configType: t.type({
name: t.string,
condition: t.string,
reverse: tNullable(t.boolean),
}),
defaultConfig: {},
async match({ triggerConfig, context, pluginData }) {
if (!context.counterTrigger) {
return;
}
if (context.counterTrigger.name !== triggerConfig.name) {
return;
}
if (context.counterTrigger.condition !== triggerConfig.condition) {
return;
}
const reverse = triggerConfig.reverse ?? false;
if (context.counterTrigger.reverse !== reverse) {
return;
}
return {
extra: {},
};
},
renderMatchInformation({ matchResult, pluginData, contexts, triggerConfig }) {
// TODO: Show user, channel, reverse
return `Matched counter \`${triggerConfig.name} ${triggerConfig.condition}\``;
},
});

View file

@ -13,6 +13,7 @@ import { GuildArchives } from "../../data/GuildArchives";
import { RecentActionType } from "./constants";
import Timeout = NodeJS.Timeout;
import { RegExpRunner } from "../../RegExpRunner";
import { CounterEvents } from "../Counters/types";
export const Rule = t.type({
enabled: t.boolean,
@ -86,6 +87,9 @@ export interface AutomodPluginType extends BasePluginType {
onMessageCreateFn: any;
onMessageUpdateFn: any;
onCounterTrigger: CounterEvents["trigger"];
onCounterReverseTrigger: CounterEvents["reverseTrigger"];
};
}
@ -93,6 +97,13 @@ export interface AutomodContext {
timestamp: number;
actioned?: boolean;
counterTrigger?: {
name: string;
condition: string;
channelId: string | null;
userId: string | null;
reverse: boolean;
};
user?: User;
message?: SavedMessage;
member?: Member;