Automod: add custom status support; add rule cooldown support (not for raidspam yet)
This commit is contained in:
parent
9a04e0fb25
commit
80a82fe348
1 changed files with 77 additions and 1 deletions
|
@ -33,6 +33,7 @@ import { SavedMessage } from "../data/entities/SavedMessage";
|
||||||
import moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
import { renderTemplate } from "../templateFormatter";
|
import { renderTemplate } from "../templateFormatter";
|
||||||
import Timeout = NodeJS.Timeout;
|
import Timeout = NodeJS.Timeout;
|
||||||
|
import { CooldownManager } from "knub";
|
||||||
|
|
||||||
type MessageInfo = { channelId: string; messageId: string };
|
type MessageInfo = { channelId: string; messageId: string };
|
||||||
|
|
||||||
|
@ -42,6 +43,7 @@ type TextTriggerWithMultipleMatchTypes = {
|
||||||
match_visible_names: boolean;
|
match_visible_names: boolean;
|
||||||
match_usernames: boolean;
|
match_usernames: boolean;
|
||||||
match_nicknames: boolean;
|
match_nicknames: boolean;
|
||||||
|
match_custom_status: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface TriggerMatchResult {
|
interface TriggerMatchResult {
|
||||||
|
@ -56,7 +58,7 @@ interface MessageTextTriggerMatchResult extends TriggerMatchResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OtherTextTriggerMatchResult extends TriggerMatchResult {
|
interface OtherTextTriggerMatchResult extends TriggerMatchResult {
|
||||||
type: "username" | "nickname" | "visiblename";
|
type: "username" | "nickname" | "visiblename" | "customstatus";
|
||||||
str: string;
|
str: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
}
|
}
|
||||||
|
@ -104,6 +106,7 @@ const MatchWordsTrigger = t.type({
|
||||||
match_visible_names: t.boolean,
|
match_visible_names: t.boolean,
|
||||||
match_usernames: t.boolean,
|
match_usernames: t.boolean,
|
||||||
match_nicknames: t.boolean,
|
match_nicknames: t.boolean,
|
||||||
|
match_custom_status: t.boolean,
|
||||||
});
|
});
|
||||||
type TMatchWordsTrigger = t.TypeOf<typeof MatchWordsTrigger>;
|
type TMatchWordsTrigger = t.TypeOf<typeof MatchWordsTrigger>;
|
||||||
const defaultMatchWordsTrigger: TMatchWordsTrigger = {
|
const defaultMatchWordsTrigger: TMatchWordsTrigger = {
|
||||||
|
@ -115,6 +118,7 @@ const defaultMatchWordsTrigger: TMatchWordsTrigger = {
|
||||||
match_visible_names: false,
|
match_visible_names: false,
|
||||||
match_usernames: false,
|
match_usernames: false,
|
||||||
match_nicknames: false,
|
match_nicknames: false,
|
||||||
|
match_custom_status: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const MatchRegexTrigger = t.type({
|
const MatchRegexTrigger = t.type({
|
||||||
|
@ -125,6 +129,7 @@ const MatchRegexTrigger = t.type({
|
||||||
match_visible_names: t.boolean,
|
match_visible_names: t.boolean,
|
||||||
match_usernames: t.boolean,
|
match_usernames: t.boolean,
|
||||||
match_nicknames: t.boolean,
|
match_nicknames: t.boolean,
|
||||||
|
match_custom_status: t.boolean,
|
||||||
});
|
});
|
||||||
type TMatchRegexTrigger = t.TypeOf<typeof MatchRegexTrigger>;
|
type TMatchRegexTrigger = t.TypeOf<typeof MatchRegexTrigger>;
|
||||||
const defaultMatchRegexTrigger: Partial<TMatchRegexTrigger> = {
|
const defaultMatchRegexTrigger: Partial<TMatchRegexTrigger> = {
|
||||||
|
@ -134,6 +139,7 @@ const defaultMatchRegexTrigger: Partial<TMatchRegexTrigger> = {
|
||||||
match_visible_names: false,
|
match_visible_names: false,
|
||||||
match_usernames: false,
|
match_usernames: false,
|
||||||
match_nicknames: false,
|
match_nicknames: false,
|
||||||
|
match_custom_status: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const MatchInvitesTrigger = t.type({
|
const MatchInvitesTrigger = t.type({
|
||||||
|
@ -147,6 +153,7 @@ const MatchInvitesTrigger = t.type({
|
||||||
match_visible_names: t.boolean,
|
match_visible_names: t.boolean,
|
||||||
match_usernames: t.boolean,
|
match_usernames: t.boolean,
|
||||||
match_nicknames: t.boolean,
|
match_nicknames: t.boolean,
|
||||||
|
match_custom_status: t.boolean,
|
||||||
});
|
});
|
||||||
type TMatchInvitesTrigger = t.TypeOf<typeof MatchInvitesTrigger>;
|
type TMatchInvitesTrigger = t.TypeOf<typeof MatchInvitesTrigger>;
|
||||||
const defaultMatchInvitesTrigger: Partial<TMatchInvitesTrigger> = {
|
const defaultMatchInvitesTrigger: Partial<TMatchInvitesTrigger> = {
|
||||||
|
@ -156,6 +163,7 @@ const defaultMatchInvitesTrigger: Partial<TMatchInvitesTrigger> = {
|
||||||
match_visible_names: false,
|
match_visible_names: false,
|
||||||
match_usernames: false,
|
match_usernames: false,
|
||||||
match_nicknames: false,
|
match_nicknames: false,
|
||||||
|
match_custom_status: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const MatchLinksTrigger = t.type({
|
const MatchLinksTrigger = t.type({
|
||||||
|
@ -167,6 +175,7 @@ const MatchLinksTrigger = t.type({
|
||||||
match_visible_names: t.boolean,
|
match_visible_names: t.boolean,
|
||||||
match_usernames: t.boolean,
|
match_usernames: t.boolean,
|
||||||
match_nicknames: t.boolean,
|
match_nicknames: t.boolean,
|
||||||
|
match_custom_status: t.boolean,
|
||||||
});
|
});
|
||||||
type TMatchLinksTrigger = t.TypeOf<typeof MatchLinksTrigger>;
|
type TMatchLinksTrigger = t.TypeOf<typeof MatchLinksTrigger>;
|
||||||
const defaultMatchLinksTrigger: Partial<TMatchLinksTrigger> = {
|
const defaultMatchLinksTrigger: Partial<TMatchLinksTrigger> = {
|
||||||
|
@ -176,6 +185,7 @@ const defaultMatchLinksTrigger: Partial<TMatchLinksTrigger> = {
|
||||||
match_visible_names: false,
|
match_visible_names: false,
|
||||||
match_usernames: false,
|
match_usernames: false,
|
||||||
match_nicknames: false,
|
match_nicknames: false,
|
||||||
|
match_custom_status: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const BaseSpamTrigger = t.type({
|
const BaseSpamTrigger = t.type({
|
||||||
|
@ -279,6 +289,7 @@ const Rule = t.type({
|
||||||
change_nickname: tNullable(ChangeNicknameAction),
|
change_nickname: tNullable(ChangeNicknameAction),
|
||||||
log: tNullable(LogAction),
|
log: tNullable(LogAction),
|
||||||
}),
|
}),
|
||||||
|
cooldown: tNullable(t.string),
|
||||||
});
|
});
|
||||||
type TRule = t.TypeOf<typeof Rule>;
|
type TRule = t.TypeOf<typeof Rule>;
|
||||||
|
|
||||||
|
@ -433,6 +444,26 @@ export class AutomodPlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
my_second_filter:
|
my_second_filter:
|
||||||
enabled: false
|
enabled: false
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
|
### Custom status alerts
|
||||||
|
This example sends an alert any time a user with a matching custom status sends a message.
|
||||||
|
|
||||||
|
~~~yml
|
||||||
|
automod:
|
||||||
|
config:
|
||||||
|
rules:
|
||||||
|
bad_custom_statuses:
|
||||||
|
triggers:
|
||||||
|
- match_words:
|
||||||
|
words: ['banana']
|
||||||
|
match_custom_status: true
|
||||||
|
actions:
|
||||||
|
alert:
|
||||||
|
channel: "473087035574321152"
|
||||||
|
text: |-
|
||||||
|
Bad custom status on user <@!{user.id}>:
|
||||||
|
{matchSummary}
|
||||||
|
~~~
|
||||||
`),
|
`),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -457,6 +488,8 @@ export class AutomodPlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
protected recentNicknameChanges: Map<string, { expiresAt: number }>;
|
protected recentNicknameChanges: Map<string, { expiresAt: number }>;
|
||||||
protected recentNicknameChangesClearInterval: Timeout;
|
protected recentNicknameChangesClearInterval: Timeout;
|
||||||
|
|
||||||
|
protected cooldownManager: CooldownManager;
|
||||||
|
|
||||||
protected onMessageCreateFn;
|
protected onMessageCreateFn;
|
||||||
|
|
||||||
protected modActions: ModActionsPlugin;
|
protected modActions: ModActionsPlugin;
|
||||||
|
@ -525,6 +558,8 @@ export class AutomodPlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
this.recentNicknameChanges = new Map();
|
this.recentNicknameChanges = new Map();
|
||||||
this.recentNicknameChangesClearInterval = setInterval(() => this.clearExpiredRecentNicknameChanges(), 30 * SECONDS);
|
this.recentNicknameChangesClearInterval = setInterval(() => this.clearExpiredRecentNicknameChanges(), 30 * SECONDS);
|
||||||
|
|
||||||
|
this.cooldownManager = new CooldownManager();
|
||||||
|
|
||||||
this.savedMessages = GuildSavedMessages.getGuildInstance(this.guildId);
|
this.savedMessages = GuildSavedMessages.getGuildInstance(this.guildId);
|
||||||
this.archives = GuildArchives.getGuildInstance(this.guildId);
|
this.archives = GuildArchives.getGuildInstance(this.guildId);
|
||||||
this.guildLogs = new GuildLogs(this.guildId);
|
this.guildLogs = new GuildLogs(this.guildId);
|
||||||
|
@ -703,6 +738,13 @@ export class AutomodPlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
if (match) return { type: "nickname", str, userId: msg.user_id };
|
if (match) return { type: "nickname", str, userId: msg.user_id };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// type 4 = custom status
|
||||||
|
if (trigger.match_custom_status && member.game && member.game.type === 4) {
|
||||||
|
const str = member.game.state;
|
||||||
|
const match = await cb(str);
|
||||||
|
if (match) return { type: "customstatus", str, userId: msg.user_id };
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1012,6 +1054,38 @@ export class AutomodPlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
let matchSummary = null;
|
let matchSummary = null;
|
||||||
let caseExtraNote = null;
|
let caseExtraNote = null;
|
||||||
|
|
||||||
|
if (rule.cooldown) {
|
||||||
|
let cooldownKey = rule.name + "-";
|
||||||
|
|
||||||
|
if (matchResult.type === "textspam") {
|
||||||
|
cooldownKey += matchResult.channelId ? `${matchResult.channelId}-${matchResult.userId}` : matchResult.userId;
|
||||||
|
} else if (matchResult.type === "message" || matchResult.type === "embed") {
|
||||||
|
cooldownKey += matchResult.userId;
|
||||||
|
} else if (
|
||||||
|
matchResult.type === "username" ||
|
||||||
|
matchResult.type === "nickname" ||
|
||||||
|
matchResult.type === "visiblename" ||
|
||||||
|
matchResult.type === "customstatus"
|
||||||
|
) {
|
||||||
|
cooldownKey += matchResult.userId;
|
||||||
|
} else if (matchResult.type === "otherspam") {
|
||||||
|
cooldownKey += matchResult.userId;
|
||||||
|
} else {
|
||||||
|
cooldownKey = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cooldownKey) {
|
||||||
|
if (this.cooldownManager.isOnCooldown(cooldownKey)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cooldownTime = convertDelayStringToMS(rule.cooldown, "s");
|
||||||
|
if (cooldownTime) {
|
||||||
|
this.cooldownManager.setCooldown(cooldownKey, cooldownTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (matchResult.type === "textspam") {
|
if (matchResult.type === "textspam") {
|
||||||
this.activateGracePeriod(matchResult);
|
this.activateGracePeriod(matchResult);
|
||||||
this.clearSpecificRecentActions(
|
this.clearSpecificRecentActions(
|
||||||
|
@ -1049,6 +1123,8 @@ export class AutomodPlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
matchSummary = `Matched nickname: ${matchResult.str}`;
|
matchSummary = `Matched nickname: ${matchResult.str}`;
|
||||||
} else if (matchResult.type === "visiblename") {
|
} else if (matchResult.type === "visiblename") {
|
||||||
matchSummary = `Matched visible name: ${matchResult.str}`;
|
matchSummary = `Matched visible name: ${matchResult.str}`;
|
||||||
|
} else if (matchResult.type === "customstatus") {
|
||||||
|
matchSummary = `Matched custom status: ${matchResult.str}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
caseExtraNote = `Matched automod rule "${rule.name}"`;
|
caseExtraNote = `Matched automod rule "${rule.name}"`;
|
||||||
|
|
Loading…
Add table
Reference in a new issue