Automod work vol 3
This commit is contained in:
parent
0e9f65e0d5
commit
0f0728bc1c
18 changed files with 133 additions and 38 deletions
|
@ -1,7 +1,7 @@
|
||||||
import { SavedMessage } from "../../../data/entities/SavedMessage";
|
import { eventListener } from "knub";
|
||||||
import { eventListener, PluginData } from "knub";
|
|
||||||
import { AutomodContext, AutomodPluginType } from "../types";
|
import { AutomodContext, AutomodPluginType } from "../types";
|
||||||
import { runAutomod } from "../functions/runAutomod";
|
import { runAutomod } from "../functions/runAutomod";
|
||||||
|
import { RecentActionType } from "../constants";
|
||||||
|
|
||||||
export const RunAutomodOnJoinEvt = eventListener<AutomodPluginType>()(
|
export const RunAutomodOnJoinEvt = eventListener<AutomodPluginType>()(
|
||||||
"guildMemberAdd",
|
"guildMemberAdd",
|
||||||
|
@ -9,8 +9,19 @@ export const RunAutomodOnJoinEvt = eventListener<AutomodPluginType>()(
|
||||||
const context: AutomodContext = {
|
const context: AutomodContext = {
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
user: member.user,
|
user: member.user,
|
||||||
|
member,
|
||||||
|
joined: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
pluginData.state.queue.add(() => runAutomod(pluginData, context));
|
pluginData.state.queue.add(() => {
|
||||||
|
pluginData.state.recentActions.push({
|
||||||
|
type: RecentActionType.MemberJoin,
|
||||||
|
context,
|
||||||
|
count: 1,
|
||||||
|
identifier: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
runAutomod(pluginData, context);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,9 +6,14 @@ import { addRecentActionsFromMessage } from "../functions/addRecentActionsFromMe
|
||||||
import moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
|
|
||||||
export function runAutomodOnMessage(pluginData: PluginData<AutomodPluginType>, message: SavedMessage, isEdit: boolean) {
|
export function runAutomodOnMessage(pluginData: PluginData<AutomodPluginType>, message: SavedMessage, isEdit: boolean) {
|
||||||
|
const user = pluginData.client.users.get(message.user_id);
|
||||||
|
const member = pluginData.guild.members.get(message.user_id);
|
||||||
|
|
||||||
const context: AutomodContext = {
|
const context: AutomodContext = {
|
||||||
timestamp: moment.utc(message.posted_at).valueOf(),
|
timestamp: moment.utc(message.posted_at).valueOf(),
|
||||||
message,
|
message,
|
||||||
|
user,
|
||||||
|
member,
|
||||||
};
|
};
|
||||||
|
|
||||||
pluginData.state.queue.add(async () => {
|
pluginData.state.queue.add(async () => {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import moment from "moment-timezone";
|
|
||||||
import { AutomodContext, AutomodPluginType } from "../types";
|
import { AutomodContext, AutomodPluginType } from "../types";
|
||||||
import { PluginData } from "knub";
|
import { PluginData } from "knub";
|
||||||
import { RECENT_ACTION_EXPIRY_TIME, RecentActionType } from "../constants";
|
import { RECENT_ACTION_EXPIRY_TIME, RecentActionType } from "../constants";
|
||||||
|
|
|
@ -52,7 +52,7 @@ export function createMessageSpamTrigger(spamType: RecentActionType, prettyName:
|
||||||
|
|
||||||
pluginData.state.recentSpam.push({
|
pluginData.state.recentSpam.push({
|
||||||
type: spamType,
|
type: spamType,
|
||||||
userId: context.message.user_id,
|
userIds: [context.message.user_id],
|
||||||
archiveId,
|
archiveId,
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,8 +2,8 @@ import { PluginData } from "knub";
|
||||||
import { AutomodPluginType } from "../types";
|
import { AutomodPluginType } from "../types";
|
||||||
import { RecentActionType } from "../constants";
|
import { RecentActionType } from "../constants";
|
||||||
|
|
||||||
export function findRecentSpam(pluginData: PluginData<AutomodPluginType>, type: RecentActionType, userId: string) {
|
export function findRecentSpam(pluginData: PluginData<AutomodPluginType>, type: RecentActionType, userId?: string) {
|
||||||
return pluginData.state.recentSpam.find(spam => {
|
return pluginData.state.recentSpam.find(spam => {
|
||||||
return spam.type === type && spam.userId === userId;
|
return spam.type === type && (!userId || spam.userIds.includes(userId));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,10 @@ export function getMatchingRecentActions(
|
||||||
type: RecentActionType,
|
type: RecentActionType,
|
||||||
identifier: string | null,
|
identifier: string | null,
|
||||||
since: number,
|
since: number,
|
||||||
to: number,
|
to?: number,
|
||||||
) {
|
) {
|
||||||
|
to = to || Date.now();
|
||||||
|
|
||||||
return pluginData.state.recentActions.filter(action => {
|
return pluginData.state.recentActions.filter(action => {
|
||||||
return (
|
return (
|
||||||
action.type === type &&
|
action.type === type &&
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import * as t from "io-ts";
|
|
||||||
import { SavedMessage } from "../../../data/entities/SavedMessage";
|
import { SavedMessage } from "../../../data/entities/SavedMessage";
|
||||||
import { resolveMember } from "../../../utils";
|
import { resolveMember } from "../../../utils";
|
||||||
import { PluginData } from "knub";
|
import { PluginData } from "knub";
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { PluginData } from "knub";
|
import { PluginData } from "knub";
|
||||||
import { AutomodContext, AutomodPluginType, TRule } from "../types";
|
import { AutomodContext, AutomodPluginType } from "../types";
|
||||||
import { availableTriggers } from "../triggers/availableTriggers";
|
import { availableTriggers } from "../triggers/availableTriggers";
|
||||||
import { availableActions } from "../actions/availableActions";
|
import { availableActions } from "../actions/availableActions";
|
||||||
import { AutomodTriggerMatchResult } from "../helpers";
|
import { AutomodTriggerMatchResult } from "../helpers";
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { RecentAction } from "../types";
|
||||||
|
|
||||||
|
export function sumRecentActionCounts(actions: RecentAction[]) {
|
||||||
|
return actions.reduce((total, action) => total + action.count, 0);
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ export interface AutomodTriggerMatchResult<TExtra extends any = unknown> {
|
||||||
extraContexts?: AutomodContext[];
|
extraContexts?: AutomodContext[];
|
||||||
extra?: TExtra;
|
extra?: TExtra;
|
||||||
|
|
||||||
silentClean?: boolean;
|
silentClean?: boolean; // TODO: Maybe generalize to a "silent" value in general, which mutes alert/log
|
||||||
}
|
}
|
||||||
|
|
||||||
type AutomodTriggerMatchFn<TConfigType, TMatchResultExtra> = (meta: {
|
type AutomodTriggerMatchFn<TConfigType, TMatchResultExtra> = (meta: {
|
||||||
|
|
|
@ -12,6 +12,8 @@ import { MatchRegexTrigger } from "./matchRegex";
|
||||||
import { MatchInvitesTrigger } from "./matchInvites";
|
import { MatchInvitesTrigger } from "./matchInvites";
|
||||||
import { MatchLinksTrigger } from "./matchLinks";
|
import { MatchLinksTrigger } from "./matchLinks";
|
||||||
import { MatchAttachmentTypeTrigger } from "./matchAttachmentType";
|
import { MatchAttachmentTypeTrigger } from "./matchAttachmentType";
|
||||||
|
import { MemberJoinSpamTrigger } from "./memberJoinSpam";
|
||||||
|
import { MemberJoinTrigger } from "./memberJoin";
|
||||||
|
|
||||||
export const availableTriggers: Record<string, AutomodTriggerBlueprint<any, any>> = {
|
export const availableTriggers: Record<string, AutomodTriggerBlueprint<any, any>> = {
|
||||||
match_words: MatchWordsTrigger,
|
match_words: MatchWordsTrigger,
|
||||||
|
@ -19,6 +21,7 @@ export const availableTriggers: Record<string, AutomodTriggerBlueprint<any, any>
|
||||||
match_invites: MatchInvitesTrigger,
|
match_invites: MatchInvitesTrigger,
|
||||||
match_links: MatchLinksTrigger,
|
match_links: MatchLinksTrigger,
|
||||||
match_attachment_type: MatchAttachmentTypeTrigger,
|
match_attachment_type: MatchAttachmentTypeTrigger,
|
||||||
|
member_join: MemberJoinTrigger,
|
||||||
|
|
||||||
message_spam: MessageSpamTrigger,
|
message_spam: MessageSpamTrigger,
|
||||||
mention_spam: MentionSpamTrigger,
|
mention_spam: MentionSpamTrigger,
|
||||||
|
@ -27,6 +30,7 @@ export const availableTriggers: Record<string, AutomodTriggerBlueprint<any, any>
|
||||||
emoji_spam: EmojiSpamTrigger,
|
emoji_spam: EmojiSpamTrigger,
|
||||||
line_spam: LineSpamTrigger,
|
line_spam: LineSpamTrigger,
|
||||||
character_spam: CharacterSpamTrigger,
|
character_spam: CharacterSpamTrigger,
|
||||||
|
member_join_spam: MemberJoinSpamTrigger,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AvailableTriggers = t.type({
|
export const AvailableTriggers = t.type({
|
||||||
|
@ -35,6 +39,7 @@ export const AvailableTriggers = t.type({
|
||||||
match_invites: MatchInvitesTrigger.configType,
|
match_invites: MatchInvitesTrigger.configType,
|
||||||
match_links: MatchLinksTrigger.configType,
|
match_links: MatchLinksTrigger.configType,
|
||||||
match_attachment_type: MatchAttachmentTypeTrigger.configType,
|
match_attachment_type: MatchAttachmentTypeTrigger.configType,
|
||||||
|
member_join: MemberJoinTrigger.configType,
|
||||||
|
|
||||||
message_spam: MessageSpamTrigger.configType,
|
message_spam: MessageSpamTrigger.configType,
|
||||||
mention_spam: MentionSpamTrigger.configType,
|
mention_spam: MentionSpamTrigger.configType,
|
||||||
|
@ -43,4 +48,5 @@ export const AvailableTriggers = t.type({
|
||||||
emoji_spam: EmojiSpamTrigger.configType,
|
emoji_spam: EmojiSpamTrigger.configType,
|
||||||
line_spam: LineSpamTrigger.configType,
|
line_spam: LineSpamTrigger.configType,
|
||||||
character_spam: CharacterSpamTrigger.configType,
|
character_spam: CharacterSpamTrigger.configType,
|
||||||
|
member_join_spam: MemberJoinSpamTrigger.configType,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
import * as t from "io-ts";
|
import * as t from "io-ts";
|
||||||
import { transliterate } from "transliteration";
|
|
||||||
import escapeStringRegexp from "escape-string-regexp";
|
|
||||||
import { AnyInvite, Attachment, GuildInvite } from "eris";
|
|
||||||
import { automodTrigger } from "../helpers";
|
import { automodTrigger } from "../helpers";
|
||||||
import {
|
import { asSingleLine, disableCodeBlocks, disableInlineCode, verboseChannelMention } from "../../../utils";
|
||||||
asSingleLine,
|
|
||||||
disableCodeBlocks,
|
|
||||||
disableInlineCode,
|
|
||||||
getInviteCodesInString,
|
|
||||||
isGuildInvite,
|
|
||||||
resolveInvite,
|
|
||||||
tNullable,
|
|
||||||
verboseChannelMention,
|
|
||||||
} from "../../../utils";
|
|
||||||
import { MatchableTextType, matchMultipleTextTypesOnMessage } from "../functions/matchMultipleTextTypesOnMessage";
|
|
||||||
|
|
||||||
interface MatchResultType {
|
interface MatchResultType {
|
||||||
matchedType: string;
|
matchedType: string;
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
import * as t from "io-ts";
|
import * as t from "io-ts";
|
||||||
import { transliterate } from "transliteration";
|
import { GuildInvite } from "eris";
|
||||||
import escapeStringRegexp from "escape-string-regexp";
|
|
||||||
import { AnyInvite, GuildInvite } from "eris";
|
|
||||||
import { automodTrigger } from "../helpers";
|
import { automodTrigger } from "../helpers";
|
||||||
import {
|
import {
|
||||||
asSingleLine,
|
|
||||||
disableCodeBlocks,
|
disableCodeBlocks,
|
||||||
disableInlineCode,
|
|
||||||
getInviteCodesInString,
|
getInviteCodesInString,
|
||||||
isGuildInvite,
|
isGuildInvite,
|
||||||
resolveInvite,
|
resolveInvite,
|
||||||
|
|
|
@ -1,16 +1,11 @@
|
||||||
import * as t from "io-ts";
|
import * as t from "io-ts";
|
||||||
import { transliterate } from "transliteration";
|
|
||||||
import escapeStringRegexp from "escape-string-regexp";
|
import escapeStringRegexp from "escape-string-regexp";
|
||||||
import { AnyInvite, GuildInvite } from "eris";
|
|
||||||
import { automodTrigger } from "../helpers";
|
import { automodTrigger } from "../helpers";
|
||||||
import {
|
import {
|
||||||
asSingleLine,
|
asSingleLine,
|
||||||
disableCodeBlocks,
|
disableCodeBlocks,
|
||||||
disableInlineCode,
|
disableInlineCode,
|
||||||
getInviteCodesInString,
|
|
||||||
getUrlsInString,
|
getUrlsInString,
|
||||||
isGuildInvite,
|
|
||||||
resolveInvite,
|
|
||||||
tNullable,
|
tNullable,
|
||||||
verboseChannelMention,
|
verboseChannelMention,
|
||||||
} from "../../../utils";
|
} from "../../../utils";
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import * as t from "io-ts";
|
import * as t from "io-ts";
|
||||||
import { transliterate } from "transliteration";
|
import { transliterate } from "transliteration";
|
||||||
import escapeStringRegexp from "escape-string-regexp";
|
|
||||||
import { automodTrigger } from "../helpers";
|
import { automodTrigger } from "../helpers";
|
||||||
import { disableInlineCode, verboseChannelMention } from "../../../utils";
|
import { disableInlineCode, verboseChannelMention } from "../../../utils";
|
||||||
import { MatchableTextType, matchMultipleTextTypesOnMessage } from "../functions/matchMultipleTextTypesOnMessage";
|
import { MatchableTextType, matchMultipleTextTypesOnMessage } from "../functions/matchMultipleTextTypesOnMessage";
|
||||||
|
|
34
backend/src/plugins/Automod/triggers/memberJoin.ts
Normal file
34
backend/src/plugins/Automod/triggers/memberJoin.ts
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import * as t from "io-ts";
|
||||||
|
import { automodTrigger } from "../helpers";
|
||||||
|
import { convertDelayStringToMS, tDelayString } from "../../../utils";
|
||||||
|
|
||||||
|
export const MemberJoinTrigger = automodTrigger<unknown>()({
|
||||||
|
configType: t.type({
|
||||||
|
only_new: t.boolean,
|
||||||
|
new_threshold: tDelayString,
|
||||||
|
}),
|
||||||
|
|
||||||
|
defaultConfig: {
|
||||||
|
only_new: false,
|
||||||
|
new_threshold: "1h",
|
||||||
|
},
|
||||||
|
|
||||||
|
async match({ pluginData, context, triggerConfig }) {
|
||||||
|
if (!context.joined || !context.member) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (triggerConfig.only_new) {
|
||||||
|
const threshold = Date.now() - convertDelayStringToMS(triggerConfig.new_threshold);
|
||||||
|
if (context.member.createdAt >= threshold) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
|
||||||
|
renderMatchInformation({ pluginData, contexts, triggerConfig }) {
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
});
|
55
backend/src/plugins/Automod/triggers/memberJoinSpam.ts
Normal file
55
backend/src/plugins/Automod/triggers/memberJoinSpam.ts
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import * as t from "io-ts";
|
||||||
|
import { automodTrigger } from "../helpers";
|
||||||
|
import { convertDelayStringToMS, tDelayString } from "../../../utils";
|
||||||
|
import { getMatchingRecentActions } from "../functions/getMatchingRecentActions";
|
||||||
|
import { RecentActionType } from "../constants";
|
||||||
|
import { sumRecentActionCounts } from "../functions/sumRecentActionCounts";
|
||||||
|
import { findRecentSpam } from "../functions/findRecentSpam";
|
||||||
|
|
||||||
|
export const MemberJoinSpamTrigger = automodTrigger<unknown>()({
|
||||||
|
configType: t.type({
|
||||||
|
amount: t.number,
|
||||||
|
within: tDelayString,
|
||||||
|
}),
|
||||||
|
|
||||||
|
defaultConfig: {},
|
||||||
|
|
||||||
|
async match({ pluginData, context, triggerConfig }) {
|
||||||
|
if (!context.joined || !context.member) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const recentSpam = findRecentSpam(pluginData, RecentActionType.MemberJoin);
|
||||||
|
if (recentSpam) {
|
||||||
|
context.actioned = true;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const since = Date.now() - convertDelayStringToMS(triggerConfig.within);
|
||||||
|
const matchingActions = getMatchingRecentActions(pluginData, RecentActionType.MemberJoin, null, since);
|
||||||
|
const totalCount = sumRecentActionCounts(matchingActions);
|
||||||
|
|
||||||
|
if (totalCount >= triggerConfig.amount) {
|
||||||
|
const contexts = [context, ...matchingActions.map(a => a.context).filter(c => c !== context)];
|
||||||
|
|
||||||
|
for (const _context of contexts) {
|
||||||
|
_context.actioned = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginData.state.recentSpam.push({
|
||||||
|
type: RecentActionType.MemberJoin,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
archiveId: null,
|
||||||
|
userIds: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
extraContexts: contexts,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
renderMatchInformation({ pluginData, contexts, triggerConfig }) {
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
});
|
|
@ -4,7 +4,7 @@ import { BasePluginType } from "knub";
|
||||||
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
|
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
|
||||||
import { GuildLogs } from "../../data/GuildLogs";
|
import { GuildLogs } from "../../data/GuildLogs";
|
||||||
import { SavedMessage } from "../../data/entities/SavedMessage";
|
import { SavedMessage } from "../../data/entities/SavedMessage";
|
||||||
import { User } from "eris";
|
import { Member, User } from "eris";
|
||||||
import { AvailableTriggers } from "./triggers/availableTriggers";
|
import { AvailableTriggers } from "./triggers/availableTriggers";
|
||||||
import { AvailableActions } from "./actions/availableActions";
|
import { AvailableActions } from "./actions/availableActions";
|
||||||
import { Queue } from "../../Queue";
|
import { Queue } from "../../Queue";
|
||||||
|
@ -71,6 +71,8 @@ export interface AutomodContext {
|
||||||
|
|
||||||
user?: User | UnknownUser;
|
user?: User | UnknownUser;
|
||||||
message?: SavedMessage;
|
message?: SavedMessage;
|
||||||
|
member?: Member;
|
||||||
|
joined?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RecentAction {
|
export interface RecentAction {
|
||||||
|
@ -83,6 +85,6 @@ export interface RecentAction {
|
||||||
export interface RecentSpam {
|
export interface RecentSpam {
|
||||||
archiveId: string;
|
archiveId: string;
|
||||||
type: RecentActionType;
|
type: RecentActionType;
|
||||||
userId: string;
|
userIds: string[];
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue