mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-03-16 14:11:50 +00:00
automod: add triggers for mod actions
This commit is contained in:
parent
5ffc3e7cc4
commit
93912541b4
34 changed files with 412 additions and 3 deletions
|
@ -31,6 +31,9 @@ import { RunAutomodOnMemberUpdate } from "./events/RunAutomodOnMemberUpdate";
|
||||||
import { CountersPlugin } from "../Counters/CountersPlugin";
|
import { CountersPlugin } from "../Counters/CountersPlugin";
|
||||||
import { parseCondition } from "../../data/GuildCounters";
|
import { parseCondition } from "../../data/GuildCounters";
|
||||||
import { runAutomodOnCounterTrigger } from "./events/runAutomodOnCounterTrigger";
|
import { runAutomodOnCounterTrigger } from "./events/runAutomodOnCounterTrigger";
|
||||||
|
import { runAutomodOnModAction } from "./events/runAutomodOnModAction";
|
||||||
|
import { registerEventListenersFromMap } from "../../utils/registerEventListenersFromMap";
|
||||||
|
import { unregisterEventListenersFromMap } from "../../utils/unregisterEventListenersFromMap";
|
||||||
|
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
config: {
|
config: {
|
||||||
|
@ -163,7 +166,13 @@ export const AutomodPlugin = zeppelinGuildPlugin<AutomodPluginType>()("automod",
|
||||||
showInDocs: true,
|
showInDocs: true,
|
||||||
info: pluginInfo,
|
info: pluginInfo,
|
||||||
|
|
||||||
dependencies: [LogsPlugin, ModActionsPlugin, MutesPlugin, CountersPlugin],
|
// prettier-ignore
|
||||||
|
dependencies: [
|
||||||
|
LogsPlugin,
|
||||||
|
ModActionsPlugin,
|
||||||
|
MutesPlugin,
|
||||||
|
CountersPlugin,
|
||||||
|
],
|
||||||
|
|
||||||
configSchema: ConfigSchema,
|
configSchema: ConfigSchema,
|
||||||
defaultOptions,
|
defaultOptions,
|
||||||
|
@ -173,6 +182,7 @@ export const AutomodPlugin = zeppelinGuildPlugin<AutomodPluginType>()("automod",
|
||||||
return criteria?.antiraid_level ? criteria.antiraid_level === pluginData.state.cachedAntiraidLevel : false;
|
return criteria?.antiraid_level ? criteria.antiraid_level === pluginData.state.cachedAntiraidLevel : false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
events: [
|
events: [
|
||||||
RunAutomodOnJoinEvt,
|
RunAutomodOnJoinEvt,
|
||||||
RunAutomodOnMemberUpdate,
|
RunAutomodOnMemberUpdate,
|
||||||
|
@ -238,13 +248,45 @@ export const AutomodPlugin = zeppelinGuildPlugin<AutomodPluginType>()("automod",
|
||||||
|
|
||||||
countersPlugin.onCounterEvent("trigger", pluginData.state.onCounterTrigger);
|
countersPlugin.onCounterEvent("trigger", pluginData.state.onCounterTrigger);
|
||||||
countersPlugin.onCounterEvent("reverseTrigger", pluginData.state.onCounterReverseTrigger);
|
countersPlugin.onCounterEvent("reverseTrigger", pluginData.state.onCounterReverseTrigger);
|
||||||
|
|
||||||
|
const modActionsEvents = pluginData.getPlugin(ModActionsPlugin).getEventEmitter();
|
||||||
|
pluginData.state.modActionsListeners = new Map();
|
||||||
|
pluginData.state.modActionsListeners.set("note", (userId: string) =>
|
||||||
|
runAutomodOnModAction(pluginData, "note", userId),
|
||||||
|
);
|
||||||
|
pluginData.state.modActionsListeners.set("warn", (userId: string) =>
|
||||||
|
runAutomodOnModAction(pluginData, "warn", userId),
|
||||||
|
);
|
||||||
|
pluginData.state.modActionsListeners.set("kick", (userId: string) =>
|
||||||
|
runAutomodOnModAction(pluginData, "kick", userId),
|
||||||
|
);
|
||||||
|
pluginData.state.modActionsListeners.set("ban", (userId: string) =>
|
||||||
|
runAutomodOnModAction(pluginData, "ban", userId),
|
||||||
|
);
|
||||||
|
pluginData.state.modActionsListeners.set("unban", (userId: string) =>
|
||||||
|
runAutomodOnModAction(pluginData, "unban", userId),
|
||||||
|
);
|
||||||
|
registerEventListenersFromMap(modActionsEvents, pluginData.state.modActionsListeners);
|
||||||
|
|
||||||
|
const mutesEvents = pluginData.getPlugin(MutesPlugin).getEventEmitter();
|
||||||
|
pluginData.state.mutesListeners = new Map();
|
||||||
|
pluginData.state.mutesListeners.set("mute", (userId: string) => runAutomodOnModAction(pluginData, "mute", userId));
|
||||||
|
pluginData.state.mutesListeners.set("unmute", (userId: string) =>
|
||||||
|
runAutomodOnModAction(pluginData, "unmute", userId),
|
||||||
|
);
|
||||||
|
registerEventListenersFromMap(mutesEvents, pluginData.state.mutesListeners);
|
||||||
},
|
},
|
||||||
|
|
||||||
async onBeforeUnload(pluginData) {
|
async onBeforeUnload(pluginData) {
|
||||||
const countersPlugin = pluginData.getPlugin(CountersPlugin);
|
const countersPlugin = pluginData.getPlugin(CountersPlugin);
|
||||||
|
|
||||||
countersPlugin.offCounterEvent("trigger", pluginData.state.onCounterTrigger);
|
countersPlugin.offCounterEvent("trigger", pluginData.state.onCounterTrigger);
|
||||||
countersPlugin.offCounterEvent("reverseTrigger", pluginData.state.onCounterReverseTrigger);
|
countersPlugin.offCounterEvent("reverseTrigger", pluginData.state.onCounterReverseTrigger);
|
||||||
|
|
||||||
|
const modActionsEvents = pluginData.getPlugin(ModActionsPlugin).getEventEmitter();
|
||||||
|
unregisterEventListenersFromMap(modActionsEvents, pluginData.state.modActionsListeners);
|
||||||
|
|
||||||
|
const mutesEvents = pluginData.getPlugin(MutesPlugin).getEventEmitter();
|
||||||
|
unregisterEventListenersFromMap(mutesEvents, pluginData.state.mutesListeners);
|
||||||
},
|
},
|
||||||
|
|
||||||
async onUnload(pluginData) {
|
async onUnload(pluginData) {
|
||||||
|
|
27
backend/src/plugins/Automod/events/runAutomodOnModAction.ts
Normal file
27
backend/src/plugins/Automod/events/runAutomodOnModAction.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { GuildPluginData } from "knub";
|
||||||
|
import { AutomodContext, AutomodPluginType } from "../types";
|
||||||
|
import { runAutomod } from "../functions/runAutomod";
|
||||||
|
import { resolveUser, UnknownUser } from "../../../utils";
|
||||||
|
import { ModActionType } from "../../ModActions/types";
|
||||||
|
|
||||||
|
export async function runAutomodOnModAction(
|
||||||
|
pluginData: GuildPluginData<AutomodPluginType>,
|
||||||
|
modAction: ModActionType,
|
||||||
|
userId: string,
|
||||||
|
reason?: string,
|
||||||
|
) {
|
||||||
|
const user = await resolveUser(pluginData.client, userId);
|
||||||
|
|
||||||
|
const context: AutomodContext = {
|
||||||
|
timestamp: Date.now(),
|
||||||
|
user: user instanceof UnknownUser ? undefined : user,
|
||||||
|
modAction: {
|
||||||
|
type: modAction,
|
||||||
|
reason,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pluginData.state.queue.add(async () => {
|
||||||
|
await runAutomod(pluginData, context);
|
||||||
|
});
|
||||||
|
}
|
|
@ -18,6 +18,13 @@ import { RoleAddedTrigger } from "./roleAdded";
|
||||||
import { RoleRemovedTrigger } from "./roleRemoved";
|
import { RoleRemovedTrigger } from "./roleRemoved";
|
||||||
import { StickerSpamTrigger } from "./stickerSpam";
|
import { StickerSpamTrigger } from "./stickerSpam";
|
||||||
import { CounterTrigger } from "./counter";
|
import { CounterTrigger } from "./counter";
|
||||||
|
import { NoteTrigger } from "./note";
|
||||||
|
import { WarnTrigger } from "./warn";
|
||||||
|
import { MuteTrigger } from "./mute";
|
||||||
|
import { UnmuteTrigger } from "./unmute";
|
||||||
|
import { KickTrigger } from "./kick";
|
||||||
|
import { BanTrigger } from "./ban";
|
||||||
|
import { UnbanTrigger } from "./unban";
|
||||||
|
|
||||||
export const availableTriggers: Record<string, AutomodTriggerBlueprint<any, any>> = {
|
export const availableTriggers: Record<string, AutomodTriggerBlueprint<any, any>> = {
|
||||||
match_words: MatchWordsTrigger,
|
match_words: MatchWordsTrigger,
|
||||||
|
@ -40,6 +47,14 @@ export const availableTriggers: Record<string, AutomodTriggerBlueprint<any, any>
|
||||||
sticker_spam: StickerSpamTrigger,
|
sticker_spam: StickerSpamTrigger,
|
||||||
|
|
||||||
counter: CounterTrigger,
|
counter: CounterTrigger,
|
||||||
|
|
||||||
|
note: NoteTrigger,
|
||||||
|
warn: WarnTrigger,
|
||||||
|
mute: MuteTrigger,
|
||||||
|
unmute: UnmuteTrigger,
|
||||||
|
kick: KickTrigger,
|
||||||
|
ban: BanTrigger,
|
||||||
|
unban: UnbanTrigger,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AvailableTriggers = t.type({
|
export const AvailableTriggers = t.type({
|
||||||
|
@ -63,4 +78,12 @@ export const AvailableTriggers = t.type({
|
||||||
sticker_spam: StickerSpamTrigger.configType,
|
sticker_spam: StickerSpamTrigger.configType,
|
||||||
|
|
||||||
counter: CounterTrigger.configType,
|
counter: CounterTrigger.configType,
|
||||||
|
|
||||||
|
note: NoteTrigger.configType,
|
||||||
|
warn: WarnTrigger.configType,
|
||||||
|
mute: MuteTrigger.configType,
|
||||||
|
unmute: UnmuteTrigger.configType,
|
||||||
|
kick: KickTrigger.configType,
|
||||||
|
ban: BanTrigger.configType,
|
||||||
|
unban: UnbanTrigger.configType,
|
||||||
});
|
});
|
||||||
|
|
24
backend/src/plugins/Automod/triggers/ban.ts
Normal file
24
backend/src/plugins/Automod/triggers/ban.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import * as t from "io-ts";
|
||||||
|
import { automodTrigger } from "../helpers";
|
||||||
|
|
||||||
|
// tslint:disable-next-line:no-empty-interface
|
||||||
|
interface BanTriggerResultType {}
|
||||||
|
|
||||||
|
export const BanTrigger = automodTrigger<BanTriggerResultType>()({
|
||||||
|
configType: t.type({}),
|
||||||
|
defaultConfig: {},
|
||||||
|
|
||||||
|
async match({ context }) {
|
||||||
|
if (context.modAction?.type !== "ban") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
extra: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
renderMatchInformation({ matchResult }) {
|
||||||
|
return `User was banned`;
|
||||||
|
},
|
||||||
|
});
|
24
backend/src/plugins/Automod/triggers/kick.ts
Normal file
24
backend/src/plugins/Automod/triggers/kick.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import * as t from "io-ts";
|
||||||
|
import { automodTrigger } from "../helpers";
|
||||||
|
|
||||||
|
// tslint:disable-next-line:no-empty-interface
|
||||||
|
interface KickTriggerResultType {}
|
||||||
|
|
||||||
|
export const KickTrigger = automodTrigger<KickTriggerResultType>()({
|
||||||
|
configType: t.type({}),
|
||||||
|
defaultConfig: {},
|
||||||
|
|
||||||
|
async match({ context }) {
|
||||||
|
if (context.modAction?.type !== "kick") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
extra: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
renderMatchInformation({ matchResult }) {
|
||||||
|
return `User was kicked`;
|
||||||
|
},
|
||||||
|
});
|
24
backend/src/plugins/Automod/triggers/mute.ts
Normal file
24
backend/src/plugins/Automod/triggers/mute.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import * as t from "io-ts";
|
||||||
|
import { automodTrigger } from "../helpers";
|
||||||
|
|
||||||
|
// tslint:disable-next-line:no-empty-interface
|
||||||
|
interface MuteTriggerResultType {}
|
||||||
|
|
||||||
|
export const MuteTrigger = automodTrigger<MuteTriggerResultType>()({
|
||||||
|
configType: t.type({}),
|
||||||
|
defaultConfig: {},
|
||||||
|
|
||||||
|
async match({ context }) {
|
||||||
|
if (context.modAction?.type !== "mute") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
extra: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
renderMatchInformation({ matchResult }) {
|
||||||
|
return `User was muted`;
|
||||||
|
},
|
||||||
|
});
|
24
backend/src/plugins/Automod/triggers/note.ts
Normal file
24
backend/src/plugins/Automod/triggers/note.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import * as t from "io-ts";
|
||||||
|
import { automodTrigger } from "../helpers";
|
||||||
|
|
||||||
|
// tslint:disable-next-line:no-empty-interface
|
||||||
|
interface NoteTriggerResultType {}
|
||||||
|
|
||||||
|
export const NoteTrigger = automodTrigger<NoteTriggerResultType>()({
|
||||||
|
configType: t.type({}),
|
||||||
|
defaultConfig: {},
|
||||||
|
|
||||||
|
async match({ context }) {
|
||||||
|
if (context.modAction?.type !== "note") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
extra: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
renderMatchInformation({ matchResult }) {
|
||||||
|
return `Note was added on user`;
|
||||||
|
},
|
||||||
|
});
|
24
backend/src/plugins/Automod/triggers/unban.ts
Normal file
24
backend/src/plugins/Automod/triggers/unban.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import * as t from "io-ts";
|
||||||
|
import { automodTrigger } from "../helpers";
|
||||||
|
|
||||||
|
// tslint:disable-next-line:no-empty-interface
|
||||||
|
interface UnbanTriggerResultType {}
|
||||||
|
|
||||||
|
export const UnbanTrigger = automodTrigger<UnbanTriggerResultType>()({
|
||||||
|
configType: t.type({}),
|
||||||
|
defaultConfig: {},
|
||||||
|
|
||||||
|
async match({ context }) {
|
||||||
|
if (context.modAction?.type !== "unban") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
extra: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
renderMatchInformation({ matchResult }) {
|
||||||
|
return `User was unbanned`;
|
||||||
|
},
|
||||||
|
});
|
24
backend/src/plugins/Automod/triggers/unmute.ts
Normal file
24
backend/src/plugins/Automod/triggers/unmute.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import * as t from "io-ts";
|
||||||
|
import { automodTrigger } from "../helpers";
|
||||||
|
|
||||||
|
// tslint:disable-next-line:no-empty-interface
|
||||||
|
interface UnmuteTriggerResultType {}
|
||||||
|
|
||||||
|
export const UnmuteTrigger = automodTrigger<UnmuteTriggerResultType>()({
|
||||||
|
configType: t.type({}),
|
||||||
|
defaultConfig: {},
|
||||||
|
|
||||||
|
async match({ context }) {
|
||||||
|
if (context.modAction?.type !== "unmute") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
extra: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
renderMatchInformation({ matchResult }) {
|
||||||
|
return `User was unmuted`;
|
||||||
|
},
|
||||||
|
});
|
24
backend/src/plugins/Automod/triggers/warn.ts
Normal file
24
backend/src/plugins/Automod/triggers/warn.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import * as t from "io-ts";
|
||||||
|
import { automodTrigger } from "../helpers";
|
||||||
|
|
||||||
|
// tslint:disable-next-line:no-empty-interface
|
||||||
|
interface WarnTriggerResultType {}
|
||||||
|
|
||||||
|
export const WarnTrigger = automodTrigger<WarnTriggerResultType>()({
|
||||||
|
configType: t.type({}),
|
||||||
|
defaultConfig: {},
|
||||||
|
|
||||||
|
async match({ context }) {
|
||||||
|
if (context.modAction?.type !== "warn") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
extra: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
renderMatchInformation({ matchResult }) {
|
||||||
|
return `User was warned`;
|
||||||
|
},
|
||||||
|
});
|
|
@ -14,6 +14,8 @@ import { RecentActionType } from "./constants";
|
||||||
import Timeout = NodeJS.Timeout;
|
import Timeout = NodeJS.Timeout;
|
||||||
import { RegExpRunner } from "../../RegExpRunner";
|
import { RegExpRunner } from "../../RegExpRunner";
|
||||||
import { CounterEvents } from "../Counters/types";
|
import { CounterEvents } from "../Counters/types";
|
||||||
|
import { ModActionsEvents, ModActionType } from "../ModActions/types";
|
||||||
|
import { MutesEvents } from "../Mutes/types";
|
||||||
|
|
||||||
export const Rule = t.type({
|
export const Rule = t.type({
|
||||||
enabled: t.boolean,
|
enabled: t.boolean,
|
||||||
|
@ -90,6 +92,9 @@ export interface AutomodPluginType extends BasePluginType {
|
||||||
|
|
||||||
onCounterTrigger: CounterEvents["trigger"];
|
onCounterTrigger: CounterEvents["trigger"];
|
||||||
onCounterReverseTrigger: CounterEvents["reverseTrigger"];
|
onCounterReverseTrigger: CounterEvents["reverseTrigger"];
|
||||||
|
|
||||||
|
modActionsListeners: Map<keyof ModActionsEvents, any>;
|
||||||
|
mutesListeners: Map<keyof MutesEvents, any>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,6 +117,10 @@ export interface AutomodContext {
|
||||||
added?: string[];
|
added?: string[];
|
||||||
removed?: string[];
|
removed?: string[];
|
||||||
};
|
};
|
||||||
|
modAction?: {
|
||||||
|
type: ModActionType;
|
||||||
|
reason?: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RecentAction {
|
export interface RecentAction {
|
||||||
|
|
|
@ -36,7 +36,6 @@ export interface CounterEvents {
|
||||||
|
|
||||||
export interface CounterEventEmitter extends EventEmitter {
|
export interface CounterEventEmitter extends EventEmitter {
|
||||||
on<U extends keyof CounterEvents>(event: U, listener: CounterEvents[U]): this;
|
on<U extends keyof CounterEvents>(event: U, listener: CounterEvents[U]): this;
|
||||||
|
|
||||||
emit<U extends keyof CounterEvents>(event: U, ...args: Parameters<CounterEvents[U]>): boolean;
|
emit<U extends keyof CounterEvents>(event: U, ...args: Parameters<CounterEvents[U]>): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,10 @@ import { DeleteCaseCmd } from "./commands/DeleteCaseCmd";
|
||||||
import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin";
|
import { TimeAndDatePlugin } from "../TimeAndDate/TimeAndDatePlugin";
|
||||||
import { GuildTempbans } from "../../data/GuildTempbans";
|
import { GuildTempbans } from "../../data/GuildTempbans";
|
||||||
import { outdatedTempbansLoop } from "./functions/outdatedTempbansLoop";
|
import { outdatedTempbansLoop } from "./functions/outdatedTempbansLoop";
|
||||||
|
import { EventEmitter } from "events";
|
||||||
|
import { mapToPublicFn } from "../../pluginUtils";
|
||||||
|
import { onModActionsEvent } from "./functions/onModActionsEvent";
|
||||||
|
import { offModActionsEvent } from "./functions/offModActionsEvent";
|
||||||
|
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
config: {
|
config: {
|
||||||
|
@ -165,6 +169,12 @@ export const ModActionsPlugin = zeppelinGuildPlugin<ModActionsPluginType>()("mod
|
||||||
banUserId(pluginData, userId, reason, banOptions);
|
banUserId(pluginData, userId, reason, banOptions);
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: mapToPublicFn(onModActionsEvent),
|
||||||
|
off: mapToPublicFn(offModActionsEvent),
|
||||||
|
getEventEmitter(pluginData) {
|
||||||
|
return () => pluginData.state.events;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
onLoad(pluginData) {
|
onLoad(pluginData) {
|
||||||
|
@ -179,10 +189,13 @@ export const ModActionsPlugin = zeppelinGuildPlugin<ModActionsPluginType>()("mod
|
||||||
state.outdatedTempbansTimeout = null;
|
state.outdatedTempbansTimeout = null;
|
||||||
state.ignoredEvents = [];
|
state.ignoredEvents = [];
|
||||||
|
|
||||||
|
state.events = new EventEmitter();
|
||||||
|
|
||||||
outdatedTempbansLoop(pluginData);
|
outdatedTempbansLoop(pluginData);
|
||||||
},
|
},
|
||||||
|
|
||||||
onUnload(pluginData) {
|
onUnload(pluginData) {
|
||||||
pluginData.state.unloaded = true;
|
pluginData.state.unloaded = true;
|
||||||
|
pluginData.state.events.removeAllListeners();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -67,6 +67,7 @@ export const ForcebanCmd = modActionsCmd({
|
||||||
pluginData.state.serverLogs.ignoreLog(LogType.MEMBER_BAN, user.id);
|
pluginData.state.serverLogs.ignoreLog(LogType.MEMBER_BAN, user.id);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// FIXME: Use banUserId()?
|
||||||
await pluginData.guild.banMember(user.id, 1, reason != null ? encodeURIComponent(reason) : undefined);
|
await pluginData.guild.banMember(user.id, 1, reason != null ? encodeURIComponent(reason) : undefined);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
sendErrorMessage(pluginData, msg.channel, "Failed to forceban member");
|
sendErrorMessage(pluginData, msg.channel, "Failed to forceban member");
|
||||||
|
@ -93,5 +94,7 @@ export const ForcebanCmd = modActionsCmd({
|
||||||
caseNumber: createdCase.case_number,
|
caseNumber: createdCase.case_number,
|
||||||
reason,
|
reason,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pluginData.state.events.emit("ban", user.id, reason);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -75,6 +75,8 @@ export const MassbanCmd = modActionsCmd({
|
||||||
reason: `Mass ban: ${banReason}`,
|
reason: `Mass ban: ${banReason}`,
|
||||||
postInCaseLogOverride: false,
|
postInCaseLogOverride: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pluginData.state.events.emit("ban", userId, banReason);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
failedBans.push(userId);
|
failedBans.push(userId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,5 +49,7 @@ export const NoteCmd = modActionsCmd({
|
||||||
});
|
});
|
||||||
|
|
||||||
sendSuccessMessage(pluginData, msg.channel, `Note added on **${userName}** (Case #${createdCase.case_number})`);
|
sendSuccessMessage(pluginData, msg.channel, `Note added on **${userName}** (Case #${createdCase.case_number})`);
|
||||||
|
|
||||||
|
pluginData.state.events.emit("note", user.id, reason);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -112,5 +112,7 @@ export const WarnCmd = modActionsCmd({
|
||||||
msg.channel,
|
msg.channel,
|
||||||
`Warned **${memberToWarn.user.username}#${memberToWarn.user.discriminator}** (Case #${warnResult.case.case_number})${messageResultText}`,
|
`Warned **${memberToWarn.user.username}#${memberToWarn.user.discriminator}** (Case #${warnResult.case.case_number})${messageResultText}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
pluginData.state.events.emit("warn", user.id, reason);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -69,5 +69,7 @@ export const CreateBanCaseOnManualBanEvt = modActionsEvt(
|
||||||
caseNumber: createdCase?.case_number ?? 0,
|
caseNumber: createdCase?.case_number ?? 0,
|
||||||
reason,
|
reason,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pluginData.state.events.emit("ban", user.id, reason);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -62,6 +62,8 @@ export const CreateKickCaseOnManualKickEvt = modActionsEvt(
|
||||||
mod: mod ? stripObjectToScalars(mod) : null,
|
mod: mod ? stripObjectToScalars(mod) : null,
|
||||||
caseNumber: createdCase?.case_number ?? 0,
|
caseNumber: createdCase?.case_number ?? 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pluginData.state.events.emit("kick", member.id, kickAuditLogEntry.reason || undefined);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -66,5 +66,7 @@ export const CreateUnbanCaseOnManualUnbanEvt = modActionsEvt(
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
caseNumber: createdCase?.case_number ?? 0,
|
caseNumber: createdCase?.case_number ?? 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pluginData.state.events.emit("unban", user.id);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -127,6 +127,8 @@ export async function banUserId(
|
||||||
banTime: banTime ? humanizeDuration(banTime) : null,
|
banTime: banTime ? humanizeDuration(banTime) : null,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pluginData.state.events.emit("ban", user.id, reason);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status: "success",
|
status: "success",
|
||||||
case: createdCase,
|
case: createdCase,
|
||||||
|
|
|
@ -85,6 +85,8 @@ export async function kickMember(
|
||||||
reason,
|
reason,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pluginData.state.events.emit("kick", member.id, reason);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status: "success",
|
status: "success",
|
||||||
case: createdCase,
|
case: createdCase,
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { GuildPluginData } from "knub";
|
||||||
|
import { ModActionsEvents, ModActionsPluginType } from "../types";
|
||||||
|
|
||||||
|
export function offModActionsEvent<TEvent extends keyof ModActionsEvents>(
|
||||||
|
pluginData: GuildPluginData<ModActionsPluginType>,
|
||||||
|
event: TEvent,
|
||||||
|
listener: ModActionsEvents[TEvent],
|
||||||
|
) {
|
||||||
|
return pluginData.state.events.off(event, listener);
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { GuildPluginData } from "knub";
|
||||||
|
import { ModActionsEvents, ModActionsPluginType } from "../types";
|
||||||
|
|
||||||
|
export function onModActionsEvent<TEvent extends keyof ModActionsEvents>(
|
||||||
|
pluginData: GuildPluginData<ModActionsPluginType>,
|
||||||
|
event: TEvent,
|
||||||
|
listener: ModActionsEvents[TEvent],
|
||||||
|
) {
|
||||||
|
return pluginData.state.events.on(event, listener);
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import { CaseArgs } from "../Cases/types";
|
||||||
import { TextChannel } from "eris";
|
import { TextChannel } from "eris";
|
||||||
import { GuildTempbans } from "../../data/GuildTempbans";
|
import { GuildTempbans } from "../../data/GuildTempbans";
|
||||||
import Timeout = NodeJS.Timeout;
|
import Timeout = NodeJS.Timeout;
|
||||||
|
import { EventEmitter } from "events";
|
||||||
|
|
||||||
export const ConfigSchema = t.type({
|
export const ConfigSchema = t.type({
|
||||||
dm_on_warn: t.boolean,
|
dm_on_warn: t.boolean,
|
||||||
|
@ -45,6 +46,20 @@ export const ConfigSchema = t.type({
|
||||||
});
|
});
|
||||||
export type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
|
export type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
|
||||||
|
|
||||||
|
export interface ModActionsEvents {
|
||||||
|
note: (userId: string, reason?: string) => void;
|
||||||
|
warn: (userId: string, reason?: string) => void;
|
||||||
|
kick: (userId: string, reason?: string) => void;
|
||||||
|
ban: (userId: string, reason?: string) => void;
|
||||||
|
unban: (userId: string, reason?: string) => void;
|
||||||
|
// mute/unmute are in the Mutes plugin
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ModActionsEventEmitter extends EventEmitter {
|
||||||
|
on<U extends keyof ModActionsEvents>(event: U, listener: ModActionsEvents[U]): this;
|
||||||
|
emit<U extends keyof ModActionsEvents>(event: U, ...args: Parameters<ModActionsEvents[U]>): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ModActionsPluginType extends BasePluginType {
|
export interface ModActionsPluginType extends BasePluginType {
|
||||||
config: TConfigSchema;
|
config: TConfigSchema;
|
||||||
state: {
|
state: {
|
||||||
|
@ -56,6 +71,8 @@ export interface ModActionsPluginType extends BasePluginType {
|
||||||
unloaded: boolean;
|
unloaded: boolean;
|
||||||
outdatedTempbansTimeout: Timeout | null;
|
outdatedTempbansTimeout: Timeout | null;
|
||||||
ignoredEvents: IIgnoredEvent[];
|
ignoredEvents: IIgnoredEvent[];
|
||||||
|
|
||||||
|
events: ModActionsEventEmitter;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,5 +139,7 @@ export interface BanOptions {
|
||||||
deleteMessageDays?: number;
|
deleteMessageDays?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ModActionType = "note" | "warn" | "mute" | "unmute" | "kick" | "ban" | "unban";
|
||||||
|
|
||||||
export const modActionsCmd = guildCommand<ModActionsPluginType>();
|
export const modActionsCmd = guildCommand<ModActionsPluginType>();
|
||||||
export const modActionsEvt = guildEventListener<ModActionsPluginType>();
|
export const modActionsEvt = guildEventListener<ModActionsPluginType>();
|
||||||
|
|
|
@ -17,6 +17,9 @@ import { Member } from "eris";
|
||||||
import { ClearActiveMuteOnMemberBanEvt } from "./events/ClearActiveMuteOnMemberBanEvt";
|
import { ClearActiveMuteOnMemberBanEvt } from "./events/ClearActiveMuteOnMemberBanEvt";
|
||||||
import { ReapplyActiveMuteOnJoinEvt } from "./events/ReapplyActiveMuteOnJoinEvt";
|
import { ReapplyActiveMuteOnJoinEvt } from "./events/ReapplyActiveMuteOnJoinEvt";
|
||||||
import { mapToPublicFn } from "../../pluginUtils";
|
import { mapToPublicFn } from "../../pluginUtils";
|
||||||
|
import { EventEmitter } from "events";
|
||||||
|
import { onMutesEvent } from "./functions/onMutesEvent";
|
||||||
|
import { offMutesEvent } from "./functions/offMutesEvent";
|
||||||
|
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
config: {
|
config: {
|
||||||
|
@ -92,6 +95,12 @@ export const MutesPlugin = zeppelinGuildPlugin<MutesPluginType>()("mutes", {
|
||||||
return muteRole ? member.roles.includes(muteRole) : false;
|
return muteRole ? member.roles.includes(muteRole) : false;
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
on: mapToPublicFn(onMutesEvent),
|
||||||
|
off: mapToPublicFn(offMutesEvent),
|
||||||
|
getEventEmitter(pluginData) {
|
||||||
|
return () => pluginData.state.events;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
onLoad(pluginData) {
|
onLoad(pluginData) {
|
||||||
|
@ -100,6 +109,8 @@ export const MutesPlugin = zeppelinGuildPlugin<MutesPluginType>()("mutes", {
|
||||||
pluginData.state.serverLogs = new GuildLogs(pluginData.guild.id);
|
pluginData.state.serverLogs = new GuildLogs(pluginData.guild.id);
|
||||||
pluginData.state.archives = GuildArchives.getGuildInstance(pluginData.guild.id);
|
pluginData.state.archives = GuildArchives.getGuildInstance(pluginData.guild.id);
|
||||||
|
|
||||||
|
pluginData.state.events = new EventEmitter();
|
||||||
|
|
||||||
// Check for expired mutes every 5s
|
// Check for expired mutes every 5s
|
||||||
const firstCheckTime = Math.max(Date.now(), FIRST_CHECK_TIME) + FIRST_CHECK_INCREMENT;
|
const firstCheckTime = Math.max(Date.now(), FIRST_CHECK_TIME) + FIRST_CHECK_INCREMENT;
|
||||||
FIRST_CHECK_TIME = firstCheckTime;
|
FIRST_CHECK_TIME = firstCheckTime;
|
||||||
|
@ -115,5 +126,6 @@ export const MutesPlugin = zeppelinGuildPlugin<MutesPluginType>()("mutes", {
|
||||||
|
|
||||||
onUnload(pluginData) {
|
onUnload(pluginData) {
|
||||||
clearInterval(pluginData.state.muteClearIntervalId);
|
clearInterval(pluginData.state.muteClearIntervalId);
|
||||||
|
pluginData.state.events.removeAllListeners();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -38,5 +38,7 @@ export async function clearExpiredMutes(pluginData: GuildPluginData<MutesPluginT
|
||||||
? stripObjectToScalars(member, ["user", "roles"])
|
? stripObjectToScalars(member, ["user", "roles"])
|
||||||
: { id: mute.user_id, user: new UnknownUser({ id: mute.user_id }) },
|
: { id: mute.user_id, user: new UnknownUser({ id: mute.user_id }) },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pluginData.state.events.emit("unmute", mute.user_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -246,6 +246,8 @@ export async function muteUser(
|
||||||
|
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
|
pluginData.state.events.emit("mute", user.id, reason);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
case: theCase,
|
case: theCase,
|
||||||
notifyResult,
|
notifyResult,
|
||||||
|
|
10
backend/src/plugins/Mutes/functions/offMutesEvent.ts
Normal file
10
backend/src/plugins/Mutes/functions/offMutesEvent.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { GuildPluginData } from "knub";
|
||||||
|
import { MutesEvents, MutesPluginType } from "../types";
|
||||||
|
|
||||||
|
export function offMutesEvent<TEvent extends keyof MutesEvents>(
|
||||||
|
pluginData: GuildPluginData<MutesPluginType>,
|
||||||
|
event: TEvent,
|
||||||
|
listener: MutesEvents[TEvent],
|
||||||
|
) {
|
||||||
|
return pluginData.state.events.off(event, listener);
|
||||||
|
}
|
10
backend/src/plugins/Mutes/functions/onMutesEvent.ts
Normal file
10
backend/src/plugins/Mutes/functions/onMutesEvent.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { GuildPluginData } from "knub";
|
||||||
|
import { MutesEvents, MutesPluginType } from "../types";
|
||||||
|
|
||||||
|
export function onMutesEvent<TEvent extends keyof MutesEvents>(
|
||||||
|
pluginData: GuildPluginData<MutesPluginType>,
|
||||||
|
event: TEvent,
|
||||||
|
listener: MutesEvents[TEvent],
|
||||||
|
) {
|
||||||
|
return pluginData.state.events.on(event, listener);
|
||||||
|
}
|
|
@ -45,6 +45,7 @@ export async function unmuteUser(
|
||||||
member.edit(memberOptions);
|
member.edit(memberOptions);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// tslint:disable-next-line:no-console
|
||||||
console.warn(
|
console.warn(
|
||||||
`Member ${userId} not found in guild ${pluginData.guild.name} (${pluginData.guild.id}) when attempting to unmute`,
|
`Member ${userId} not found in guild ${pluginData.guild.name} (${pluginData.guild.id}) when attempting to unmute`,
|
||||||
);
|
);
|
||||||
|
@ -95,6 +96,12 @@ export async function unmuteUser(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!unmuteTime) {
|
||||||
|
// If the member was unmuted, not just scheduled to be unmuted, fire the unmute event as well
|
||||||
|
// Scheduled unmutes have their event fired in clearExpiredMutes()
|
||||||
|
pluginData.state.events.emit("unmute", user.id, caseArgs.reason);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
case: createdCase,
|
case: createdCase,
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { GuildArchives } from "../../data/GuildArchives";
|
||||||
import { GuildMutes } from "../../data/GuildMutes";
|
import { GuildMutes } from "../../data/GuildMutes";
|
||||||
import { CaseArgs } from "../Cases/types";
|
import { CaseArgs } from "../Cases/types";
|
||||||
import Timeout = NodeJS.Timeout;
|
import Timeout = NodeJS.Timeout;
|
||||||
|
import { EventEmitter } from "events";
|
||||||
|
|
||||||
export const ConfigSchema = t.type({
|
export const ConfigSchema = t.type({
|
||||||
mute_role: tNullable(t.string),
|
mute_role: tNullable(t.string),
|
||||||
|
@ -31,6 +32,16 @@ export const ConfigSchema = t.type({
|
||||||
});
|
});
|
||||||
export type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
|
export type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
|
||||||
|
|
||||||
|
export interface MutesEvents {
|
||||||
|
mute: (userId: string, reason?: string) => void;
|
||||||
|
unmute: (userId: string, reason?: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MutesEventEmitter extends EventEmitter {
|
||||||
|
on<U extends keyof MutesEvents>(event: U, listener: MutesEvents[U]): this;
|
||||||
|
emit<U extends keyof MutesEvents>(event: U, ...args: Parameters<MutesEvents[U]>): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface MutesPluginType extends BasePluginType {
|
export interface MutesPluginType extends BasePluginType {
|
||||||
config: TConfigSchema;
|
config: TConfigSchema;
|
||||||
state: {
|
state: {
|
||||||
|
@ -40,6 +51,8 @@ export interface MutesPluginType extends BasePluginType {
|
||||||
archives: GuildArchives;
|
archives: GuildArchives;
|
||||||
|
|
||||||
muteClearIntervalId: Timeout;
|
muteClearIntervalId: Timeout;
|
||||||
|
|
||||||
|
events: MutesEventEmitter;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
7
backend/src/utils/registerEventListenersFromMap.ts
Normal file
7
backend/src/utils/registerEventListenersFromMap.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { EventEmitter } from "events";
|
||||||
|
|
||||||
|
export function registerEventListenersFromMap(eventEmitter: EventEmitter, map: Map<string, any>) {
|
||||||
|
for (const [event, listener] of map.entries()) {
|
||||||
|
eventEmitter.on(event, listener);
|
||||||
|
}
|
||||||
|
}
|
7
backend/src/utils/unregisterEventListenersFromMap.ts
Normal file
7
backend/src/utils/unregisterEventListenersFromMap.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { EventEmitter } from "events";
|
||||||
|
|
||||||
|
export function unregisterEventListenersFromMap(eventEmitter: EventEmitter, map: Map<string, any>) {
|
||||||
|
for (const [event, listener] of map.entries()) {
|
||||||
|
eventEmitter.off(event, listener);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue