From 41472981207d84985d6a8fa89167c675d25faf86 Mon Sep 17 00:00:00 2001 From: Dragory <2606411+Dragory@users.noreply.github.com> Date: Fri, 2 Apr 2021 17:44:21 +0300 Subject: [PATCH] Add counter documentation/examples. Tweak counter triggers/actions in automod. Rename change_counter automod action to add_to_counter, add set_counter action, rename counter trigger to counter_trigger. --- .../{changeCounter.ts => addToCounter.ts} | 15 +- .../Automod/actions/availableActions.ts | 9 +- .../src/plugins/Automod/actions/setCounter.ts | 22 ++ .../Automod/triggers/availableTriggers.ts | 6 +- .../{counter.ts => counterTrigger.ts} | 0 .../src/plugins/Counters/CountersPlugin.ts | 8 + dashboard/src/components/docs/Counters.vue | 295 ++++++++++++++++++ dashboard/src/components/docs/DocsLayout.vue | 4 + dashboard/src/routes.ts | 4 + 9 files changed, 347 insertions(+), 16 deletions(-) rename backend/src/plugins/Automod/actions/{changeCounter.ts => addToCounter.ts} (62%) create mode 100644 backend/src/plugins/Automod/actions/setCounter.ts rename backend/src/plugins/Automod/triggers/{counter.ts => counterTrigger.ts} (100%) create mode 100644 dashboard/src/components/docs/Counters.vue diff --git a/backend/src/plugins/Automod/actions/changeCounter.ts b/backend/src/plugins/Automod/actions/addToCounter.ts similarity index 62% rename from backend/src/plugins/Automod/actions/changeCounter.ts rename to backend/src/plugins/Automod/actions/addToCounter.ts index a3db8d31..fe293e4c 100644 --- a/backend/src/plugins/Automod/actions/changeCounter.ts +++ b/backend/src/plugins/Automod/actions/addToCounter.ts @@ -2,26 +2,21 @@ import * as t from "io-ts"; import { automodAction } from "../helpers"; import { CountersPlugin } from "../../Counters/CountersPlugin"; -export const ChangeCounterAction = automodAction({ +export const AddToCounterAction = automodAction({ configType: t.type({ - name: t.string, - change: t.string, + counter: t.string, + amount: t.number, }), 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, + actionConfig.counter, contexts[0].message?.channel_id || null, contexts[0].user?.id || null, - change, + actionConfig.amount, ); }, }); diff --git a/backend/src/plugins/Automod/actions/availableActions.ts b/backend/src/plugins/Automod/actions/availableActions.ts index 21ebe945..7857db6f 100644 --- a/backend/src/plugins/Automod/actions/availableActions.ts +++ b/backend/src/plugins/Automod/actions/availableActions.ts @@ -12,7 +12,8 @@ import { AddRolesAction } from "./addRoles"; import { RemoveRolesAction } from "./removeRoles"; import { SetAntiraidLevelAction } from "./setAntiraidLevel"; import { ReplyAction } from "./reply"; -import { ChangeCounterAction } from "./changeCounter"; +import { AddToCounterAction } from "./addToCounter"; +import { SetCounterAction } from "./setCounter"; export const availableActions: Record<string, AutomodActionBlueprint<any>> = { clean: CleanAction, @@ -27,7 +28,8 @@ export const availableActions: Record<string, AutomodActionBlueprint<any>> = { remove_roles: RemoveRolesAction, set_antiraid_level: SetAntiraidLevelAction, reply: ReplyAction, - change_counter: ChangeCounterAction, + add_to_counter: AddToCounterAction, + set_counter: SetCounterAction, }; export const AvailableActions = t.type({ @@ -43,5 +45,6 @@ export const AvailableActions = t.type({ remove_roles: RemoveRolesAction.configType, set_antiraid_level: SetAntiraidLevelAction.configType, reply: ReplyAction.configType, - change_counter: ChangeCounterAction.configType, + add_to_counter: AddToCounterAction.configType, + set_counter: SetCounterAction.configType, }); diff --git a/backend/src/plugins/Automod/actions/setCounter.ts b/backend/src/plugins/Automod/actions/setCounter.ts new file mode 100644 index 00000000..0dbbaa37 --- /dev/null +++ b/backend/src/plugins/Automod/actions/setCounter.ts @@ -0,0 +1,22 @@ +import * as t from "io-ts"; +import { automodAction } from "../helpers"; +import { CountersPlugin } from "../../Counters/CountersPlugin"; + +export const SetCounterAction = automodAction({ + configType: t.type({ + counter: t.string, + value: t.number, + }), + + defaultConfig: {}, + + async apply({ pluginData, contexts, actionConfig, matchResult }) { + const countersPlugin = pluginData.getPlugin(CountersPlugin); + countersPlugin.setCounterValue( + actionConfig.counter, + contexts[0].message?.channel_id || null, + contexts[0].user?.id || null, + actionConfig.value, + ); + }, +}); diff --git a/backend/src/plugins/Automod/triggers/availableTriggers.ts b/backend/src/plugins/Automod/triggers/availableTriggers.ts index 175df6d7..18c671b6 100644 --- a/backend/src/plugins/Automod/triggers/availableTriggers.ts +++ b/backend/src/plugins/Automod/triggers/availableTriggers.ts @@ -17,7 +17,7 @@ import { MemberJoinTrigger } from "./memberJoin"; import { RoleAddedTrigger } from "./roleAdded"; import { RoleRemovedTrigger } from "./roleRemoved"; import { StickerSpamTrigger } from "./stickerSpam"; -import { CounterTrigger } from "./counter"; +import { CounterTrigger } from "./counterTrigger"; import { NoteTrigger } from "./note"; import { WarnTrigger } from "./warn"; import { MuteTrigger } from "./mute"; @@ -46,7 +46,7 @@ export const availableTriggers: Record<string, AutomodTriggerBlueprint<any, any> member_join_spam: MemberJoinSpamTrigger, sticker_spam: StickerSpamTrigger, - counter: CounterTrigger, + counter_trigger: CounterTrigger, note: NoteTrigger, warn: WarnTrigger, @@ -77,7 +77,7 @@ export const AvailableTriggers = t.type({ member_join_spam: MemberJoinSpamTrigger.configType, sticker_spam: StickerSpamTrigger.configType, - counter: CounterTrigger.configType, + counter_trigger: CounterTrigger.configType, note: NoteTrigger.configType, warn: WarnTrigger.configType, diff --git a/backend/src/plugins/Automod/triggers/counter.ts b/backend/src/plugins/Automod/triggers/counterTrigger.ts similarity index 100% rename from backend/src/plugins/Automod/triggers/counter.ts rename to backend/src/plugins/Automod/triggers/counterTrigger.ts diff --git a/backend/src/plugins/Counters/CountersPlugin.ts b/backend/src/plugins/Counters/CountersPlugin.ts index b6789e81..68a5bcea 100644 --- a/backend/src/plugins/Counters/CountersPlugin.ts +++ b/backend/src/plugins/Counters/CountersPlugin.ts @@ -102,6 +102,14 @@ const configPreprocessor: ConfigPreprocessorFn<CountersPluginType> = options => * After being triggered, a trigger is "reset" if the counter value no longer matches the trigger (e.g. drops to 100 or below in the above example). After this, that trigger can be triggered again. */ export const CountersPlugin = zeppelinGuildPlugin<CountersPluginType>()("counters", { + showInDocs: true, + info: { + prettyName: "Counters", + description: + "Keep track of per-user, per-channel, or global numbers and trigger specific actions based on this number", + configurationGuide: "See <a href='/docs/setup-guides/counters'>Counters setup guide</a>", + }, + configSchema: ConfigSchema, defaultOptions, configPreprocessor, diff --git a/dashboard/src/components/docs/Counters.vue b/dashboard/src/components/docs/Counters.vue new file mode 100644 index 00000000..c8af5159 --- /dev/null +++ b/dashboard/src/components/docs/Counters.vue @@ -0,0 +1,295 @@ +<template> + <div> + <h1>Counters</h1> + <p> + Counters are an advanced feature in Zeppelin that allows you keep track of per-user, per-channel, or global numbers and trigger specific actions based on this number. + Common use cases are infraction points, XP systems, activity roles, and so on. + </p> + <p> + This guide will be expanded in the future. For now, it contains examples of common counter use cases. + Also see the <router-link to="/docs/plugins/counters">documentation for the Counters plugin.</router-link> + </p> + + <h2>Examples</h2> + + <h3>Infraction points</h3> + <p> + In this example, warns, mutes, and kicks all accumulate "infraction points" for a user. + When the user reaches too many points, they are automatically banned. + </p> + + <Expandable class="wide"> + <template v-slot:title>Click to view example</template> + <template v-slot:content> + <CodeBlock> + plugins: + counters: + config: + counters: + + infraction_points: + per_user: true + triggers: + # When a user accumulates 50 or more (>=50) infraction points, this trigger will activate. + # The numbers here are arbitrary - you could choose to use 5 or 500 instead, depending on the granularity you want. + autoban: + condition: ">=50" + # Remove 1 infraction point each day + decay: + amount: 1 + every: 24h + + automod: + config: + rules: + + add_infraction_points_on_warn: + triggers: + - warn: {} + actions: + add_to_counter: + counter: "infraction_points" + amount: 10 + + add_infraction_points_on_mute: + triggers: + - mute: {} + actions: + add_to_counter: + counter: "infraction_points" + amount: 20 + + add_infraction_points_on_kick: + triggers: + - kick: {} + actions: + add_to_counter: + counter: "infraction_points" + amount: 40 + + autoban_on_too_many_infraction_points: + triggers: + # The counter trigger we specified further above, "autoban", is used to trigger an automod rule here + - counter_trigger: + counter: "infraction_points" + trigger: "autoban" + actions: + ban: + reason: "Too many infraction points" + </CodeBlock> + </template> + </Expandable> + + <h3>Escalating automod punishments</h3> + <p> + This example allows users to trigger the `some_infraction` automod rule 3 times. On the 4th time, they are automatically muted. + </p> + + <Expandable class="wide"> + <template v-slot:title>Click to view example</template> + <template v-slot:content> + <CodeBlock code-lang="yaml"> + plugins: + counters: + config: + counters: + + automod_infractions: + per_user: true + triggers: + # When a user accumulates 100 or more (>=100) automod infraction points, this trigger will activate + # The numbers here are arbitrary - you could choose to use 10 or 1000 instead. + too_many_infractions: + condition: ">=100" + # Remove 100 automod infraction points per hour + decay: + amount: 100 + every: 1h + + automod: + config: + rules: + + # An example automod rule that adds automod infraction points + some_infraction: + triggers: + - match_words: + words: ['poopoo head'] + + actions: + clean: true + reply: 'Do not insult other users' + add_to_counter: + counter: "automod_infractions" + amount: 25 # This infraction adds 25 automod infraction points + + # An example rule that is triggered when the user accumulates too many automod infraction points + automute_on_too_many_infractions: + triggers: + - counter_trigger: + counter: "automod_infractions" + trigger: "too_many_infractions" + + actions: + mute: + reason: "You have been muted for tripping too many automod filters" + remove_roles_on_mute: true + restore_roles_on_mute: true + </CodeBlock> + </template> + </Expandable> + + <h3>Simple XP system</h3> + <p> + This example creates an XP system where every message sent grants you 1 XP, max once per minute. + At 100, 250, 500, and 1000 XP the system grants the user a new role. + </p> + + <Expandable class="wide"> + <template v-slot:title>Click to view example</template> + <template v-slot:content> + <CodeBlock> + plugins: + counters: + config: + counters: + xp: + per_user: true + triggers: + role_1: + condition: ">=100" + role_2: + condition: ">=250" + role_3: + condition: ">=500" + role_4: + condition: ">=1000" + + automod: + config: + rules: + + accumulate_xp: + triggers: + - any_message: {} + + actions: + log: false # Don't spam logs with XP changes + add_to_counter: + counter: "xp" + amount: 1 # Each message adds 1 XP + + cooldown: 1m # Only count 1 message per minute + + add_xp_role_1: + triggers: + - counter_trigger: + counter: "xp" + trigger: "role_1" + + actions: + add_roles: ["123456789123456789"] # Role ID for xp role 1 + + add_xp_role_2: + triggers: + - counter_trigger: + counter: "xp" + trigger: "role_2" + + actions: + add_roles: ["123456789123456789"] # Role ID for xp role 2 + + add_xp_role_3: + triggers: + - counter_trigger: + counter: "xp" + trigger: "role_3" + + actions: + add_roles: ["123456789123456789"] # Role ID for xp role 3 + + add_xp_role_4: + triggers: + - counter_trigger: + counter: "xp" + trigger: "role_4" + + actions: + add_roles: ["123456789123456789"] # Role ID for xp role 4 + </CodeBlock> + </template> + </Expandable> + + <h3>Activity role ("regular role")</h3> + <p> + This example is similar to the XP system, but the number decays and the role granted by the system can be removed if the user's activity goes down. + </p> + + <Expandable class="wide"> + <template v-slot:title>Click to view example</template> + <template v-slot:content> + <CodeBlock code-lang="yaml"> + plugins: + counters: + config: + counters: + activity: + per_user: true + triggers: + grant_role: + condition: ">=100" + # We set a separate threshold for when the role should be removed. This is so the decay doesn't remove the activity role immediately. + # If this value isn't set, reverse_condition defaults to the opposite of the condition, i.e. "<100" in this case. + reverse_condition: "<50" + decay: + amount: 1 + every: 1h + + automod: + config: + rules: + + accumulate_activity: + triggers: + - any_message: {} + + actions: + log: false # Don't spam logs with activity changes + add_to_counter: + counter: "activity" + amount: 1 # Each message adds 1 to the counter + + cooldown: 1m # Only count 1 message per minute + + grant_activity_role: + triggers: + - counter_trigger: + counter: "activity" + trigger: "grant_role" + + actions: + add_roles: ["123456789123456789"] # Role ID for activity role + + remove_activity_role: + triggers: + - counter_trigger: + counter: "activity" + trigger: "grant_role" + reverse: true # This indicates we want to use the *reverse* of the specified trigger, see reverse_condition in counters above + + actions: + remove_roles: ["123456789123456789"] # Role ID for activity role + </CodeBlock> + </template> + </Expandable> + </div> +</template> + +<script> +import CodeBlock from "./CodeBlock"; +import Expandable from "../Expandable"; + +export default { + components: { CodeBlock, Expandable }, +}; +</script> diff --git a/dashboard/src/components/docs/DocsLayout.vue b/dashboard/src/components/docs/DocsLayout.vue index b508ef46..e8657d8a 100644 --- a/dashboard/src/components/docs/DocsLayout.vue +++ b/dashboard/src/components/docs/DocsLayout.vue @@ -115,6 +115,10 @@ to: '/docs/setup-guides/moderation', label: 'Moderation', }, + { + to: '/docs/setup-guides/counters', + label: 'Counters', + }, ], }, ]; diff --git a/dashboard/src/routes.ts b/dashboard/src/routes.ts index c38d1f7e..4ef8e996 100644 --- a/dashboard/src/routes.ts +++ b/dashboard/src/routes.ts @@ -53,6 +53,10 @@ export const router = new VueRouter({ path: "setup-guides/moderation", component: () => import("./components/docs/WorkInProgress.vue"), }, + { + path: "setup-guides/counters", + component: () => import("./components/docs/Counters.vue"), + }, { path: "plugins/:pluginName/:tab?", component: () => import("./components/docs/Plugin.vue"),