chore: run prettier
This commit is contained in:
parent
77ab2718e7
commit
873bf7eb99
54 changed files with 462 additions and 416 deletions
|
@ -4,12 +4,12 @@ import { Repository } from "typeorm";
|
||||||
import { TemplateSafeValueContainer, renderTemplate } from "../templateFormatter";
|
import { TemplateSafeValueContainer, renderTemplate } from "../templateFormatter";
|
||||||
import { renderUsername, trimLines } from "../utils";
|
import { renderUsername, trimLines } from "../utils";
|
||||||
import { decrypt, encrypt } from "../utils/crypt";
|
import { decrypt, encrypt } from "../utils/crypt";
|
||||||
|
import { isDefaultSticker } from "../utils/isDefaultSticker";
|
||||||
import { channelToTemplateSafeChannel, guildToTemplateSafeGuild } from "../utils/templateSafeObjects";
|
import { channelToTemplateSafeChannel, guildToTemplateSafeGuild } from "../utils/templateSafeObjects";
|
||||||
import { BaseGuildRepository } from "./BaseGuildRepository";
|
import { BaseGuildRepository } from "./BaseGuildRepository";
|
||||||
import { dataSource } from "./dataSource";
|
import { dataSource } from "./dataSource";
|
||||||
import { ArchiveEntry } from "./entities/ArchiveEntry";
|
import { ArchiveEntry } from "./entities/ArchiveEntry";
|
||||||
import { SavedMessage } from "./entities/SavedMessage";
|
import { SavedMessage } from "./entities/SavedMessage";
|
||||||
import { isDefaultSticker } from "../utils/isDefaultSticker";
|
|
||||||
|
|
||||||
const DEFAULT_EXPIRY_DAYS = 30;
|
const DEFAULT_EXPIRY_DAYS = 30;
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { guildPlugins } from "./plugins/availablePlugins";
|
|
||||||
import zodToJsonSchema from "zod-to-json-schema";
|
import zodToJsonSchema from "zod-to-json-schema";
|
||||||
|
import { guildPlugins } from "./plugins/availablePlugins";
|
||||||
import { zZeppelinGuildConfig } from "./types";
|
import { zZeppelinGuildConfig } from "./types";
|
||||||
|
|
||||||
const pluginSchemaMap = guildPlugins.reduce((map, plugin) => {
|
const pluginSchemaMap = guildPlugins.reduce((map, plugin) => {
|
||||||
if (! plugin.info) {
|
if (!plugin.info) {
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
map[plugin.name] = plugin.info.configSchema;
|
map[plugin.name] = plugin.info.configSchema;
|
||||||
return map;
|
return map;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
const fullSchema = zZeppelinGuildConfig
|
const fullSchema = zZeppelinGuildConfig.omit({ plugins: true }).merge(
|
||||||
.omit({ plugins: true })
|
z.strictObject({
|
||||||
.merge(z.strictObject({
|
|
||||||
plugins: z.strictObject(pluginSchemaMap).partial(),
|
plugins: z.strictObject(pluginSchemaMap).partial(),
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const jsonSchema = zodToJsonSchema(fullSchema);
|
const jsonSchema = zodToJsonSchema(fullSchema);
|
||||||
|
|
||||||
|
|
|
@ -10,13 +10,7 @@ import {
|
||||||
PermissionsBitField,
|
PermissionsBitField,
|
||||||
TextBasedChannel,
|
TextBasedChannel,
|
||||||
} from "discord.js";
|
} from "discord.js";
|
||||||
import {
|
import { AnyPluginData, CommandContext, ExtendedMatchParams, GuildPluginData, helpers } from "knub";
|
||||||
AnyPluginData,
|
|
||||||
CommandContext,
|
|
||||||
ExtendedMatchParams,
|
|
||||||
GuildPluginData,
|
|
||||||
helpers
|
|
||||||
} from "knub";
|
|
||||||
import { logger } from "./logger";
|
import { logger } from "./logger";
|
||||||
import { isStaff } from "./staff";
|
import { isStaff } from "./staff";
|
||||||
import { TZeppelinKnub } from "./types";
|
import { TZeppelinKnub } from "./types";
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { BasePluginType } from "knub";
|
import { BasePluginType } from "knub";
|
||||||
|
import z from "zod";
|
||||||
import { GuildLogs } from "../../data/GuildLogs";
|
import { GuildLogs } from "../../data/GuildLogs";
|
||||||
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
|
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
|
||||||
import { SavedMessage } from "../../data/entities/SavedMessage";
|
import { SavedMessage } from "../../data/entities/SavedMessage";
|
||||||
import { MINUTES, zDelayString } from "../../utils";
|
import { MINUTES, zDelayString } from "../../utils";
|
||||||
import Timeout = NodeJS.Timeout;
|
import Timeout = NodeJS.Timeout;
|
||||||
import z from "zod";
|
|
||||||
|
|
||||||
export const MAX_DELAY = 5 * MINUTES;
|
export const MAX_DELAY = 5 * MINUTES;
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { CooldownManager } from "knub";
|
import { CooldownManager } from "knub";
|
||||||
|
import { Queue } from "../../Queue";
|
||||||
import { GuildAntiraidLevels } from "../../data/GuildAntiraidLevels";
|
import { GuildAntiraidLevels } from "../../data/GuildAntiraidLevels";
|
||||||
import { GuildArchives } from "../../data/GuildArchives";
|
import { GuildArchives } from "../../data/GuildArchives";
|
||||||
import { GuildLogs } from "../../data/GuildLogs";
|
import { GuildLogs } from "../../data/GuildLogs";
|
||||||
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
|
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
|
||||||
import { Queue } from "../../Queue";
|
|
||||||
import { discardRegExpRunner, getRegExpRunner } from "../../regExpRunners";
|
import { discardRegExpRunner, getRegExpRunner } from "../../regExpRunners";
|
||||||
import { MINUTES, SECONDS } from "../../utils";
|
import { MINUTES, SECONDS } from "../../utils";
|
||||||
import { registerEventListenersFromMap } from "../../utils/registerEventListenersFromMap";
|
import { registerEventListenersFromMap } from "../../utils/registerEventListenersFromMap";
|
||||||
|
@ -19,9 +19,9 @@ import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
|
||||||
import { AntiraidClearCmd } from "./commands/AntiraidClearCmd";
|
import { AntiraidClearCmd } from "./commands/AntiraidClearCmd";
|
||||||
import { SetAntiraidCmd } from "./commands/SetAntiraidCmd";
|
import { SetAntiraidCmd } from "./commands/SetAntiraidCmd";
|
||||||
import { ViewAntiraidCmd } from "./commands/ViewAntiraidCmd";
|
import { ViewAntiraidCmd } from "./commands/ViewAntiraidCmd";
|
||||||
import { runAutomodOnCounterTrigger } from "./events/runAutomodOnCounterTrigger";
|
|
||||||
import { RunAutomodOnJoinEvt, RunAutomodOnLeaveEvt } from "./events/RunAutomodOnJoinLeaveEvt";
|
import { RunAutomodOnJoinEvt, RunAutomodOnLeaveEvt } from "./events/RunAutomodOnJoinLeaveEvt";
|
||||||
import { RunAutomodOnMemberUpdate } from "./events/RunAutomodOnMemberUpdate";
|
import { RunAutomodOnMemberUpdate } from "./events/RunAutomodOnMemberUpdate";
|
||||||
|
import { runAutomodOnCounterTrigger } from "./events/runAutomodOnCounterTrigger";
|
||||||
import { runAutomodOnMessage } from "./events/runAutomodOnMessage";
|
import { runAutomodOnMessage } from "./events/runAutomodOnMessage";
|
||||||
import { runAutomodOnModAction } from "./events/runAutomodOnModAction";
|
import { runAutomodOnModAction } from "./events/runAutomodOnModAction";
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {
|
||||||
zAllowedMentions,
|
zAllowedMentions,
|
||||||
zBoundedCharacters,
|
zBoundedCharacters,
|
||||||
zNullishToUndefined,
|
zNullishToUndefined,
|
||||||
zSnowflake
|
zSnowflake,
|
||||||
} from "../../../utils";
|
} from "../../../utils";
|
||||||
import { erisAllowedMentionsToDjsMentionOptions } from "../../../utils/erisAllowedMentionsToDjsMentionOptions";
|
import { erisAllowedMentionsToDjsMentionOptions } from "../../../utils/erisAllowedMentionsToDjsMentionOptions";
|
||||||
import { messageIsEmpty } from "../../../utils/messageIsEmpty";
|
import { messageIsEmpty } from "../../../utils/messageIsEmpty";
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
import { convertDelayStringToMS, nonNullish, unique, zBoundedCharacters, zDelayString, zSnowflake } from "../../../utils";
|
import {
|
||||||
|
convertDelayStringToMS,
|
||||||
|
nonNullish,
|
||||||
|
unique,
|
||||||
|
zBoundedCharacters,
|
||||||
|
zDelayString,
|
||||||
|
zSnowflake,
|
||||||
|
} from "../../../utils";
|
||||||
import { CaseArgs } from "../../Cases/types";
|
import { CaseArgs } from "../../Cases/types";
|
||||||
import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin";
|
import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin";
|
||||||
|
import { zNotify } from "../constants";
|
||||||
import { resolveActionContactMethods } from "../functions/resolveActionContactMethods";
|
import { resolveActionContactMethods } from "../functions/resolveActionContactMethods";
|
||||||
import { automodAction } from "../helpers";
|
import { automodAction } from "../helpers";
|
||||||
import { zNotify } from "../constants";
|
|
||||||
|
|
||||||
const configSchema = z.strictObject({
|
const configSchema = z.strictObject({
|
||||||
reason: zBoundedCharacters(0, 4000).nullable().default(null),
|
reason: zBoundedCharacters(0, 4000).nullable().default(null),
|
||||||
|
|
|
@ -68,10 +68,7 @@ export const ChangePermsAction = automodAction({
|
||||||
configSchema: z.strictObject({
|
configSchema: z.strictObject({
|
||||||
target: zBoundedCharacters(1, 2000),
|
target: zBoundedCharacters(1, 2000),
|
||||||
channel: zBoundedCharacters(1, 2000).nullable().default(null),
|
channel: zBoundedCharacters(1, 2000).nullable().default(null),
|
||||||
perms: z.record(
|
perms: z.record(z.enum(allPermissionNames), z.boolean().nullable()),
|
||||||
z.enum(allPermissionNames),
|
|
||||||
z.boolean().nullable(),
|
|
||||||
),
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
async apply({ pluginData, contexts, actionConfig }) {
|
async apply({ pluginData, contexts, actionConfig }) {
|
||||||
|
|
|
@ -2,9 +2,9 @@ import z from "zod";
|
||||||
import { asyncMap, nonNullish, resolveMember, unique, zBoundedCharacters, zSnowflake } from "../../../utils";
|
import { asyncMap, nonNullish, resolveMember, unique, zBoundedCharacters, zSnowflake } from "../../../utils";
|
||||||
import { CaseArgs } from "../../Cases/types";
|
import { CaseArgs } from "../../Cases/types";
|
||||||
import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin";
|
import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin";
|
||||||
|
import { zNotify } from "../constants";
|
||||||
import { resolveActionContactMethods } from "../functions/resolveActionContactMethods";
|
import { resolveActionContactMethods } from "../functions/resolveActionContactMethods";
|
||||||
import { automodAction } from "../helpers";
|
import { automodAction } from "../helpers";
|
||||||
import { zNotify } from "../constants";
|
|
||||||
|
|
||||||
export const KickAction = automodAction({
|
export const KickAction = automodAction({
|
||||||
configSchema: z.strictObject({
|
configSchema: z.strictObject({
|
||||||
|
|
|
@ -1,12 +1,19 @@
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
import { ERRORS, RecoverablePluginError } from "../../../RecoverablePluginError";
|
import { ERRORS, RecoverablePluginError } from "../../../RecoverablePluginError";
|
||||||
import { convertDelayStringToMS, nonNullish, unique, zBoundedCharacters, zDelayString, zSnowflake } from "../../../utils";
|
import {
|
||||||
|
convertDelayStringToMS,
|
||||||
|
nonNullish,
|
||||||
|
unique,
|
||||||
|
zBoundedCharacters,
|
||||||
|
zDelayString,
|
||||||
|
zSnowflake,
|
||||||
|
} from "../../../utils";
|
||||||
import { CaseArgs } from "../../Cases/types";
|
import { CaseArgs } from "../../Cases/types";
|
||||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||||
import { MutesPlugin } from "../../Mutes/MutesPlugin";
|
import { MutesPlugin } from "../../Mutes/MutesPlugin";
|
||||||
|
import { zNotify } from "../constants";
|
||||||
import { resolveActionContactMethods } from "../functions/resolveActionContactMethods";
|
import { resolveActionContactMethods } from "../functions/resolveActionContactMethods";
|
||||||
import { automodAction } from "../helpers";
|
import { automodAction } from "../helpers";
|
||||||
import { zNotify } from "../constants";
|
|
||||||
|
|
||||||
export const MuteAction = automodAction({
|
export const MuteAction = automodAction({
|
||||||
configSchema: z.strictObject({
|
configSchema: z.strictObject({
|
||||||
|
@ -14,8 +21,14 @@ export const MuteAction = automodAction({
|
||||||
duration: zDelayString.nullable().default(null),
|
duration: zDelayString.nullable().default(null),
|
||||||
notify: zNotify.nullable().default(null),
|
notify: zNotify.nullable().default(null),
|
||||||
notifyChannel: zSnowflake.nullable().default(null),
|
notifyChannel: zSnowflake.nullable().default(null),
|
||||||
remove_roles_on_mute: z.union([z.boolean(), z.array(zSnowflake)]).nullable().default(null),
|
remove_roles_on_mute: z
|
||||||
restore_roles_on_mute: z.union([z.boolean(), z.array(zSnowflake)]).nullable().default(null),
|
.union([z.boolean(), z.array(zSnowflake)])
|
||||||
|
.nullable()
|
||||||
|
.default(null),
|
||||||
|
restore_roles_on_mute: z
|
||||||
|
.union([z.boolean(), z.array(zSnowflake)])
|
||||||
|
.nullable()
|
||||||
|
.default(null),
|
||||||
postInCaseLog: z.boolean().nullable().default(null),
|
postInCaseLog: z.boolean().nullable().default(null),
|
||||||
hide_case: z.boolean().nullable().default(false),
|
hide_case: z.boolean().nullable().default(false),
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {
|
||||||
verboseChannelMention,
|
verboseChannelMention,
|
||||||
zBoundedCharacters,
|
zBoundedCharacters,
|
||||||
zDelayString,
|
zDelayString,
|
||||||
zMessageContent
|
zMessageContent,
|
||||||
} from "../../../utils";
|
} from "../../../utils";
|
||||||
import { hasDiscordPermissions } from "../../../utils/hasDiscordPermissions";
|
import { hasDiscordPermissions } from "../../../utils/hasDiscordPermissions";
|
||||||
import { messageIsEmpty } from "../../../utils/messageIsEmpty";
|
import { messageIsEmpty } from "../../../utils/messageIsEmpty";
|
||||||
|
|
|
@ -2,9 +2,9 @@ import z from "zod";
|
||||||
import { asyncMap, nonNullish, resolveMember, unique, zBoundedCharacters, zSnowflake } from "../../../utils";
|
import { asyncMap, nonNullish, resolveMember, unique, zBoundedCharacters, zSnowflake } from "../../../utils";
|
||||||
import { CaseArgs } from "../../Cases/types";
|
import { CaseArgs } from "../../Cases/types";
|
||||||
import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin";
|
import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin";
|
||||||
|
import { zNotify } from "../constants";
|
||||||
import { resolveActionContactMethods } from "../functions/resolveActionContactMethods";
|
import { resolveActionContactMethods } from "../functions/resolveActionContactMethods";
|
||||||
import { automodAction } from "../helpers";
|
import { automodAction } from "../helpers";
|
||||||
import { zNotify } from "../constants";
|
|
||||||
|
|
||||||
export const WarnAction = automodAction({
|
export const WarnAction = automodAction({
|
||||||
configSchema: z.strictObject({
|
configSchema: z.strictObject({
|
||||||
|
|
|
@ -20,7 +20,4 @@ export enum RecentActionType {
|
||||||
ThreadCreate,
|
ThreadCreate,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const zNotify = z.union([
|
export const zNotify = z.union([z.literal("dm"), z.literal("channel")]);
|
||||||
z.literal("dm"),
|
|
||||||
z.literal("channel"),
|
|
||||||
]);
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import z from "zod";
|
||||||
import { SavedMessage } from "../../../data/entities/SavedMessage";
|
import { SavedMessage } from "../../../data/entities/SavedMessage";
|
||||||
import { humanizeDurationShort } from "../../../humanizeDurationShort";
|
import { humanizeDurationShort } from "../../../humanizeDurationShort";
|
||||||
import { getBaseUrl } from "../../../pluginUtils";
|
import { getBaseUrl } from "../../../pluginUtils";
|
||||||
|
@ -7,7 +8,6 @@ import { automodTrigger } from "../helpers";
|
||||||
import { findRecentSpam } from "./findRecentSpam";
|
import { findRecentSpam } from "./findRecentSpam";
|
||||||
import { getMatchingMessageRecentActions } from "./getMatchingMessageRecentActions";
|
import { getMatchingMessageRecentActions } from "./getMatchingMessageRecentActions";
|
||||||
import { getMessageSpamIdentifier } from "./getSpamIdentifier";
|
import { getMessageSpamIdentifier } from "./getSpamIdentifier";
|
||||||
import z from "zod";
|
|
||||||
|
|
||||||
interface TMessageSpamMatchResultType {
|
interface TMessageSpamMatchResultType {
|
||||||
archiveId: string;
|
archiveId: string;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { GuildPluginData } from "knub";
|
import { GuildPluginData } from "knub";
|
||||||
|
import z, { ZodTypeAny } from "zod";
|
||||||
import { Awaitable } from "../../utils/typeUtils";
|
import { Awaitable } from "../../utils/typeUtils";
|
||||||
import { AutomodContext, AutomodPluginType } from "./types";
|
import { AutomodContext, AutomodPluginType } from "./types";
|
||||||
import z, { ZodTypeAny } from "zod";
|
|
||||||
|
|
||||||
interface BaseAutomodTriggerMatchResult {
|
interface BaseAutomodTriggerMatchResult {
|
||||||
extraContexts?: AutomodContext[];
|
extraContexts?: AutomodContext[];
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { automodTrigger } from "../helpers";
|
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
|
import { automodTrigger } from "../helpers";
|
||||||
|
|
||||||
interface AntiraidLevelTriggerResult {}
|
interface AntiraidLevelTriggerResult {}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Snowflake } from "discord.js";
|
import { Snowflake } from "discord.js";
|
||||||
|
import z from "zod";
|
||||||
import { verboseChannelMention } from "../../../utils";
|
import { verboseChannelMention } from "../../../utils";
|
||||||
import { automodTrigger } from "../helpers";
|
import { automodTrigger } from "../helpers";
|
||||||
import z from "zod";
|
|
||||||
|
|
||||||
interface AnyMessageResultType {}
|
interface AnyMessageResultType {}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { escapeInlineCode, Snowflake } from "discord.js";
|
import { escapeInlineCode, Snowflake } from "discord.js";
|
||||||
import z from "zod";
|
|
||||||
import { extname } from "path";
|
import { extname } from "path";
|
||||||
|
import z from "zod";
|
||||||
import { asSingleLine, messageSummary, verboseChannelMention } from "../../../utils";
|
import { asSingleLine, messageSummary, verboseChannelMention } from "../../../utils";
|
||||||
import { automodTrigger } from "../helpers";
|
import { automodTrigger } from "../helpers";
|
||||||
|
|
||||||
|
@ -9,28 +9,30 @@ interface MatchResultType {
|
||||||
mode: "blacklist" | "whitelist";
|
mode: "blacklist" | "whitelist";
|
||||||
}
|
}
|
||||||
|
|
||||||
const configSchema = z.strictObject({
|
const configSchema = z
|
||||||
filetype_blacklist: z.array(z.string().max(32)).max(255).default([]),
|
.strictObject({
|
||||||
blacklist_enabled: z.boolean().default(false),
|
filetype_blacklist: z.array(z.string().max(32)).max(255).default([]),
|
||||||
filetype_whitelist: z.array(z.string().max(32)).max(255).default([]),
|
blacklist_enabled: z.boolean().default(false),
|
||||||
whitelist_enabled: z.boolean().default(false),
|
filetype_whitelist: z.array(z.string().max(32)).max(255).default([]),
|
||||||
}).transform((parsed, ctx) => {
|
whitelist_enabled: z.boolean().default(false),
|
||||||
if (parsed.blacklist_enabled && parsed.whitelist_enabled) {
|
})
|
||||||
ctx.addIssue({
|
.transform((parsed, ctx) => {
|
||||||
code: z.ZodIssueCode.custom,
|
if (parsed.blacklist_enabled && parsed.whitelist_enabled) {
|
||||||
message: "Cannot have both blacklist and whitelist enabled",
|
ctx.addIssue({
|
||||||
});
|
code: z.ZodIssueCode.custom,
|
||||||
return z.NEVER;
|
message: "Cannot have both blacklist and whitelist enabled",
|
||||||
}
|
});
|
||||||
if (! parsed.blacklist_enabled && ! parsed.whitelist_enabled) {
|
return z.NEVER;
|
||||||
ctx.addIssue({
|
}
|
||||||
code: z.ZodIssueCode.custom,
|
if (!parsed.blacklist_enabled && !parsed.whitelist_enabled) {
|
||||||
message: "Must have either blacklist or whitelist enabled",
|
ctx.addIssue({
|
||||||
});
|
code: z.ZodIssueCode.custom,
|
||||||
return z.NEVER;
|
message: "Must have either blacklist or whitelist enabled",
|
||||||
}
|
});
|
||||||
return parsed;
|
return z.NEVER;
|
||||||
});
|
}
|
||||||
|
return parsed;
|
||||||
|
});
|
||||||
|
|
||||||
export const MatchAttachmentTypeTrigger = automodTrigger<MatchResultType>()({
|
export const MatchAttachmentTypeTrigger = automodTrigger<MatchResultType>()({
|
||||||
configSchema,
|
configSchema,
|
||||||
|
|
|
@ -26,12 +26,20 @@ const configSchema = z.strictObject({
|
||||||
include_subdomains: z.boolean().default(true),
|
include_subdomains: z.boolean().default(true),
|
||||||
include_words: z.array(z.string().max(2000)).max(700).optional(),
|
include_words: z.array(z.string().max(2000)).max(700).optional(),
|
||||||
exclude_words: z.array(z.string().max(2000)).max(700).optional(),
|
exclude_words: z.array(z.string().max(2000)).max(700).optional(),
|
||||||
include_regex: z.array(zRegex(z.string().max(2000))).max(512).optional(),
|
include_regex: z
|
||||||
exclude_regex: z.array(zRegex(z.string().max(2000))).max(512).optional(),
|
.array(zRegex(z.string().max(2000)))
|
||||||
phisherman: z.strictObject({
|
.max(512)
|
||||||
include_suspected: z.boolean().optional(),
|
.optional(),
|
||||||
include_verified: z.boolean().optional(),
|
exclude_regex: z
|
||||||
}).optional(),
|
.array(zRegex(z.string().max(2000)))
|
||||||
|
.max(512)
|
||||||
|
.optional(),
|
||||||
|
phisherman: z
|
||||||
|
.strictObject({
|
||||||
|
include_suspected: z.boolean().optional(),
|
||||||
|
include_verified: z.boolean().optional(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
only_real_links: z.boolean().default(true),
|
only_real_links: z.boolean().default(true),
|
||||||
match_messages: z.boolean().default(true),
|
match_messages: z.boolean().default(true),
|
||||||
match_embeds: z.boolean().default(true),
|
match_embeds: z.boolean().default(true),
|
||||||
|
|
|
@ -8,28 +8,30 @@ interface MatchResultType {
|
||||||
mode: "blacklist" | "whitelist";
|
mode: "blacklist" | "whitelist";
|
||||||
}
|
}
|
||||||
|
|
||||||
const configSchema = z.strictObject({
|
const configSchema = z
|
||||||
mime_type_blacklist: z.array(z.string().max(255)).max(255).default([]),
|
.strictObject({
|
||||||
blacklist_enabled: z.boolean().default(false),
|
mime_type_blacklist: z.array(z.string().max(255)).max(255).default([]),
|
||||||
mime_type_whitelist: z.array(z.string().max(255)).max(255).default([]),
|
blacklist_enabled: z.boolean().default(false),
|
||||||
whitelist_enabled: z.boolean().default(false),
|
mime_type_whitelist: z.array(z.string().max(255)).max(255).default([]),
|
||||||
}).transform((parsed, ctx) => {
|
whitelist_enabled: z.boolean().default(false),
|
||||||
if (parsed.blacklist_enabled && parsed.whitelist_enabled) {
|
})
|
||||||
ctx.addIssue({
|
.transform((parsed, ctx) => {
|
||||||
code: z.ZodIssueCode.custom,
|
if (parsed.blacklist_enabled && parsed.whitelist_enabled) {
|
||||||
message: "Cannot have both blacklist and whitelist enabled",
|
ctx.addIssue({
|
||||||
});
|
code: z.ZodIssueCode.custom,
|
||||||
return z.NEVER;
|
message: "Cannot have both blacklist and whitelist enabled",
|
||||||
}
|
});
|
||||||
if (! parsed.blacklist_enabled && ! parsed.whitelist_enabled) {
|
return z.NEVER;
|
||||||
ctx.addIssue({
|
}
|
||||||
code: z.ZodIssueCode.custom,
|
if (!parsed.blacklist_enabled && !parsed.whitelist_enabled) {
|
||||||
message: "Must have either blacklist or whitelist enabled",
|
ctx.addIssue({
|
||||||
});
|
code: z.ZodIssueCode.custom,
|
||||||
return z.NEVER;
|
message: "Must have either blacklist or whitelist enabled",
|
||||||
}
|
});
|
||||||
return parsed;
|
return z.NEVER;
|
||||||
});
|
}
|
||||||
|
return parsed;
|
||||||
|
});
|
||||||
|
|
||||||
export const MatchMimeTypeTrigger = automodTrigger<MatchResultType>()({
|
export const MatchMimeTypeTrigger = automodTrigger<MatchResultType>()({
|
||||||
configSchema,
|
configSchema,
|
||||||
|
|
|
@ -8,10 +8,7 @@ interface RoleAddedMatchResult {
|
||||||
matchedRoleId: string;
|
matchedRoleId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const configSchema = z.union([
|
const configSchema = z.union([zSnowflake, z.array(zSnowflake).max(255)]).default([]);
|
||||||
zSnowflake,
|
|
||||||
z.array(zSnowflake).max(255),
|
|
||||||
]).default([]);
|
|
||||||
|
|
||||||
export const RoleAddedTrigger = automodTrigger<RoleAddedMatchResult>()({
|
export const RoleAddedTrigger = automodTrigger<RoleAddedMatchResult>()({
|
||||||
configSchema,
|
configSchema,
|
||||||
|
|
|
@ -8,10 +8,7 @@ interface RoleAddedMatchResult {
|
||||||
matchedRoleId: string;
|
matchedRoleId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const configSchema = z.union([
|
const configSchema = z.union([zSnowflake, z.array(zSnowflake).max(255)]).default([]);
|
||||||
zSnowflake,
|
|
||||||
z.array(zSnowflake).max(255),
|
|
||||||
]).default([]);
|
|
||||||
|
|
||||||
export const RoleRemovedTrigger = automodTrigger<RoleAddedMatchResult>()({
|
export const RoleRemovedTrigger = automodTrigger<RoleAddedMatchResult>()({
|
||||||
configSchema,
|
configSchema,
|
||||||
|
|
|
@ -19,58 +19,62 @@ import { availableTriggers } from "./triggers/availableTriggers";
|
||||||
import Timeout = NodeJS.Timeout;
|
import Timeout = NodeJS.Timeout;
|
||||||
|
|
||||||
export type ZTriggersMapHelper = {
|
export type ZTriggersMapHelper = {
|
||||||
[TriggerName in keyof typeof availableTriggers]: typeof availableTriggers[TriggerName]["configSchema"];
|
[TriggerName in keyof typeof availableTriggers]: (typeof availableTriggers)[TriggerName]["configSchema"];
|
||||||
};
|
};
|
||||||
const zTriggersMap = z.strictObject(entries(availableTriggers).reduce((map, [triggerName, trigger]) => {
|
const zTriggersMap = z
|
||||||
map[triggerName] = trigger.configSchema;
|
.strictObject(
|
||||||
return map;
|
entries(availableTriggers).reduce((map, [triggerName, trigger]) => {
|
||||||
}, {} as ZTriggersMapHelper)).partial();
|
map[triggerName] = trigger.configSchema;
|
||||||
|
return map;
|
||||||
|
}, {} as ZTriggersMapHelper),
|
||||||
|
)
|
||||||
|
.partial();
|
||||||
|
|
||||||
type ZActionsMapHelper = {
|
type ZActionsMapHelper = {
|
||||||
[ActionName in keyof typeof availableActions]: typeof availableActions[ActionName]["configSchema"];
|
[ActionName in keyof typeof availableActions]: (typeof availableActions)[ActionName]["configSchema"];
|
||||||
};
|
};
|
||||||
const zActionsMap = z.strictObject(entries(availableActions).reduce((map, [actionName, action]) => {
|
const zActionsMap = z
|
||||||
// @ts-expect-error TS can't infer this properly but it works fine thanks to our helper
|
.strictObject(
|
||||||
map[actionName] = action.configSchema;
|
entries(availableActions).reduce((map, [actionName, action]) => {
|
||||||
return map;
|
// @ts-expect-error TS can't infer this properly but it works fine thanks to our helper
|
||||||
}, {} as ZActionsMapHelper)).partial();
|
map[actionName] = action.configSchema;
|
||||||
|
return map;
|
||||||
|
}, {} as ZActionsMapHelper),
|
||||||
|
)
|
||||||
|
.partial();
|
||||||
|
|
||||||
const zRule = z.strictObject({
|
const zRule = z.strictObject({
|
||||||
enabled: z.boolean().default(true),
|
enabled: z.boolean().default(true),
|
||||||
// Typed as "never" because you are not expected to supply this directly.
|
// Typed as "never" because you are not expected to supply this directly.
|
||||||
// The transform instead picks it up from the property key and the output type is a string.
|
// The transform instead picks it up from the property key and the output type is a string.
|
||||||
name: z.never().optional().transform((_, ctx) => {
|
name: z
|
||||||
const ruleName = String(ctx.path[ctx.path.length - 2]).trim();
|
.never()
|
||||||
if (! ruleName) {
|
.optional()
|
||||||
ctx.addIssue({
|
.transform((_, ctx) => {
|
||||||
code: z.ZodIssueCode.custom,
|
const ruleName = String(ctx.path[ctx.path.length - 2]).trim();
|
||||||
message: "Automod rules must have names",
|
if (!ruleName) {
|
||||||
});
|
ctx.addIssue({
|
||||||
return z.NEVER;
|
code: z.ZodIssueCode.custom,
|
||||||
}
|
message: "Automod rules must have names",
|
||||||
return ruleName;
|
});
|
||||||
}),
|
return z.NEVER;
|
||||||
|
}
|
||||||
|
return ruleName;
|
||||||
|
}),
|
||||||
presets: z.array(z.string().max(100)).max(25).default([]),
|
presets: z.array(z.string().max(100)).max(25).default([]),
|
||||||
affects_bots: z.boolean().default(false),
|
affects_bots: z.boolean().default(false),
|
||||||
affects_self: z.boolean().default(false),
|
affects_self: z.boolean().default(false),
|
||||||
cooldown: zDelayString.nullable().default(null),
|
cooldown: zDelayString.nullable().default(null),
|
||||||
allow_further_rules: z.boolean().default(false),
|
allow_further_rules: z.boolean().default(false),
|
||||||
triggers: z.array(zTriggersMap),
|
triggers: z.array(zTriggersMap),
|
||||||
actions: zActionsMap.refine(
|
actions: zActionsMap.refine((v) => !(v.clean && v.start_thread), {
|
||||||
(v) => ! (v.clean && v.start_thread),
|
message: "Cannot have both clean and start_thread active at the same time",
|
||||||
{
|
}),
|
||||||
message: "Cannot have both clean and start_thread active at the same time",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
export type TRule = z.infer<typeof zRule>;
|
export type TRule = z.infer<typeof zRule>;
|
||||||
|
|
||||||
export const zAutomodConfig = z.strictObject({
|
export const zAutomodConfig = z.strictObject({
|
||||||
rules: zBoundedRecord(
|
rules: zBoundedRecord(z.record(z.string().max(100), zRule), 0, 255),
|
||||||
z.record(z.string().max(100), zRule),
|
|
||||||
0,
|
|
||||||
255,
|
|
||||||
),
|
|
||||||
antiraid_levels: z.array(z.string().max(100)).max(10),
|
antiraid_levels: z.array(z.string().max(100)).max(10),
|
||||||
can_set_antiraid: z.boolean(),
|
can_set_antiraid: z.boolean(),
|
||||||
can_view_antiraid: z.boolean(),
|
can_view_antiraid: z.boolean(),
|
||||||
|
|
|
@ -2,13 +2,13 @@ import { ContextMenuCommandInteraction } from "discord.js";
|
||||||
import humanizeDuration from "humanize-duration";
|
import humanizeDuration from "humanize-duration";
|
||||||
import { GuildPluginData } from "knub";
|
import { GuildPluginData } from "knub";
|
||||||
import { ERRORS, RecoverablePluginError } from "../../../RecoverablePluginError";
|
import { ERRORS, RecoverablePluginError } from "../../../RecoverablePluginError";
|
||||||
|
import { canActOn } from "../../../pluginUtils";
|
||||||
import { convertDelayStringToMS } from "../../../utils";
|
import { convertDelayStringToMS } from "../../../utils";
|
||||||
import { CaseArgs } from "../../Cases/types";
|
import { CaseArgs } from "../../Cases/types";
|
||||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||||
|
import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin";
|
||||||
import { MutesPlugin } from "../../Mutes/MutesPlugin";
|
import { MutesPlugin } from "../../Mutes/MutesPlugin";
|
||||||
import { ContextMenuPluginType } from "../types";
|
import { ContextMenuPluginType } from "../types";
|
||||||
import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin";
|
|
||||||
import { canActOn } from "../../../pluginUtils";
|
|
||||||
|
|
||||||
export async function muteAction(
|
export async function muteAction(
|
||||||
pluginData: GuildPluginData<ContextMenuPluginType>,
|
pluginData: GuildPluginData<ContextMenuPluginType>,
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
import { EventEmitter } from "events";
|
import { EventEmitter } from "events";
|
||||||
import { PluginOptions } from "knub";
|
import { PluginOptions } from "knub";
|
||||||
import { GuildCounters } from "../../data/GuildCounters";
|
import { GuildCounters } from "../../data/GuildCounters";
|
||||||
import {
|
import { CounterTrigger, parseCounterConditionString } from "../../data/entities/CounterTrigger";
|
||||||
CounterTrigger,
|
|
||||||
parseCounterConditionString
|
|
||||||
} from "../../data/entities/CounterTrigger";
|
|
||||||
import { mapToPublicFn } from "../../pluginUtils";
|
import { mapToPublicFn } from "../../pluginUtils";
|
||||||
import { MINUTES, convertDelayStringToMS, values } from "../../utils";
|
import { MINUTES, convertDelayStringToMS, values } from "../../utils";
|
||||||
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
|
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
|
||||||
|
|
|
@ -2,33 +2,45 @@ import { EventEmitter } from "events";
|
||||||
import { BasePluginType } from "knub";
|
import { BasePluginType } from "knub";
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
import { GuildCounters } from "../../data/GuildCounters";
|
import { GuildCounters } from "../../data/GuildCounters";
|
||||||
import { CounterTrigger, buildCounterConditionString, getReverseCounterComparisonOp, parseCounterConditionString } from "../../data/entities/CounterTrigger";
|
import {
|
||||||
|
CounterTrigger,
|
||||||
|
buildCounterConditionString,
|
||||||
|
getReverseCounterComparisonOp,
|
||||||
|
parseCounterConditionString,
|
||||||
|
} from "../../data/entities/CounterTrigger";
|
||||||
import { zBoundedCharacters, zBoundedRecord, zDelayString } from "../../utils";
|
import { zBoundedCharacters, zBoundedRecord, zDelayString } from "../../utils";
|
||||||
import Timeout = NodeJS.Timeout;
|
import Timeout = NodeJS.Timeout;
|
||||||
|
|
||||||
const MAX_COUNTERS = 5;
|
const MAX_COUNTERS = 5;
|
||||||
const MAX_TRIGGERS_PER_COUNTER = 5;
|
const MAX_TRIGGERS_PER_COUNTER = 5;
|
||||||
|
|
||||||
export const zTrigger = z.strictObject({
|
export const zTrigger = z
|
||||||
// Dummy type because name gets replaced by the property key in transform()
|
.strictObject({
|
||||||
name: z.never().optional().transform(() => ""),
|
// Dummy type because name gets replaced by the property key in transform()
|
||||||
pretty_name: zBoundedCharacters(0, 100).nullable().default(null),
|
name: z
|
||||||
condition: zBoundedCharacters(1, 64).refine(
|
.never()
|
||||||
(str) => parseCounterConditionString(str) !== null,
|
.optional()
|
||||||
{ message: "Invalid counter trigger condition" },
|
.transform(() => ""),
|
||||||
),
|
pretty_name: zBoundedCharacters(0, 100).nullable().default(null),
|
||||||
reverse_condition: zBoundedCharacters(1, 64).refine(
|
condition: zBoundedCharacters(1, 64).refine((str) => parseCounterConditionString(str) !== null, {
|
||||||
(str) => parseCounterConditionString(str) !== null,
|
message: "Invalid counter trigger condition",
|
||||||
{ message: "Invalid counter trigger reverse condition" },
|
}),
|
||||||
).optional(),
|
reverse_condition: zBoundedCharacters(1, 64)
|
||||||
})
|
.refine((str) => parseCounterConditionString(str) !== null, {
|
||||||
|
message: "Invalid counter trigger reverse condition",
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
})
|
||||||
.transform((val, ctx) => {
|
.transform((val, ctx) => {
|
||||||
const ruleName = String(ctx.path[ctx.path.length - 2]).trim();
|
const ruleName = String(ctx.path[ctx.path.length - 2]).trim();
|
||||||
|
|
||||||
let reverseCondition = val.reverse_condition;
|
let reverseCondition = val.reverse_condition;
|
||||||
if (! reverseCondition) {
|
if (!reverseCondition) {
|
||||||
const parsedCondition = parseCounterConditionString(val.condition)!;
|
const parsedCondition = parseCounterConditionString(val.condition)!;
|
||||||
reverseCondition = buildCounterConditionString(getReverseCounterComparisonOp(parsedCondition[0]), parsedCondition[1]);
|
reverseCondition = buildCounterConditionString(
|
||||||
|
getReverseCounterComparisonOp(parsedCondition[0]),
|
||||||
|
parsedCondition[1],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -38,68 +50,65 @@ export const zTrigger = z.strictObject({
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const zTriggerFromString = zBoundedCharacters(0, 100)
|
const zTriggerFromString = zBoundedCharacters(0, 100).transform((val, ctx) => {
|
||||||
.transform((val, ctx) => {
|
const ruleName = String(ctx.path[ctx.path.length - 2]).trim();
|
||||||
const ruleName = String(ctx.path[ctx.path.length - 2]).trim();
|
const parsedCondition = parseCounterConditionString(val);
|
||||||
const parsedCondition = parseCounterConditionString(val);
|
if (!parsedCondition) {
|
||||||
if (!parsedCondition) {
|
ctx.addIssue({
|
||||||
ctx.addIssue({
|
code: z.ZodIssueCode.custom,
|
||||||
code: z.ZodIssueCode.custom,
|
message: "Invalid counter trigger condition",
|
||||||
message: "Invalid counter trigger condition",
|
});
|
||||||
});
|
return z.NEVER;
|
||||||
return z.NEVER;
|
}
|
||||||
}
|
return {
|
||||||
return {
|
name: ruleName,
|
||||||
name: ruleName,
|
pretty_name: null,
|
||||||
pretty_name: null,
|
condition: buildCounterConditionString(parsedCondition[0], parsedCondition[1]),
|
||||||
condition: buildCounterConditionString(parsedCondition[0], parsedCondition[1]),
|
reverse_condition: buildCounterConditionString(
|
||||||
reverse_condition: buildCounterConditionString(getReverseCounterComparisonOp(parsedCondition[0]), parsedCondition[1]),
|
getReverseCounterComparisonOp(parsedCondition[0]),
|
||||||
};
|
parsedCondition[1],
|
||||||
});
|
),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
const zTriggerInput = z.union([zTrigger, zTriggerFromString]);
|
const zTriggerInput = z.union([zTrigger, zTriggerFromString]);
|
||||||
|
|
||||||
export const zCounter = z.strictObject({
|
export const zCounter = z.strictObject({
|
||||||
// Typed as "never" because you are not expected to supply this directly.
|
// Typed as "never" because you are not expected to supply this directly.
|
||||||
// The transform instead picks it up from the property key and the output type is a string.
|
// The transform instead picks it up from the property key and the output type is a string.
|
||||||
name: z.never().optional().transform((_, ctx) => {
|
name: z
|
||||||
const ruleName = String(ctx.path[ctx.path.length - 2]).trim();
|
.never()
|
||||||
if (! ruleName) {
|
.optional()
|
||||||
ctx.addIssue({
|
.transform((_, ctx) => {
|
||||||
code: z.ZodIssueCode.custom,
|
const ruleName = String(ctx.path[ctx.path.length - 2]).trim();
|
||||||
message: "Counters must have names",
|
if (!ruleName) {
|
||||||
});
|
ctx.addIssue({
|
||||||
return z.NEVER;
|
code: z.ZodIssueCode.custom,
|
||||||
}
|
message: "Counters must have names",
|
||||||
return ruleName;
|
});
|
||||||
}),
|
return z.NEVER;
|
||||||
|
}
|
||||||
|
return ruleName;
|
||||||
|
}),
|
||||||
pretty_name: zBoundedCharacters(0, 100).nullable().default(null),
|
pretty_name: zBoundedCharacters(0, 100).nullable().default(null),
|
||||||
per_channel: z.boolean().default(false),
|
per_channel: z.boolean().default(false),
|
||||||
per_user: z.boolean().default(false),
|
per_user: z.boolean().default(false),
|
||||||
initial_value: z.number().default(0),
|
initial_value: z.number().default(0),
|
||||||
triggers: zBoundedRecord(
|
triggers: zBoundedRecord(z.record(zBoundedCharacters(0, 100), zTriggerInput), 1, MAX_TRIGGERS_PER_COUNTER),
|
||||||
z.record(
|
decay: z
|
||||||
zBoundedCharacters(0, 100),
|
.strictObject({
|
||||||
zTriggerInput,
|
amount: z.number(),
|
||||||
),
|
every: zDelayString,
|
||||||
1,
|
})
|
||||||
MAX_TRIGGERS_PER_COUNTER,
|
.nullable()
|
||||||
),
|
.default(null),
|
||||||
decay: z.strictObject({
|
|
||||||
amount: z.number(),
|
|
||||||
every: zDelayString,
|
|
||||||
}).nullable().default(null),
|
|
||||||
can_view: z.boolean().default(false),
|
can_view: z.boolean().default(false),
|
||||||
can_edit: z.boolean().default(false),
|
can_edit: z.boolean().default(false),
|
||||||
can_reset_all: z.boolean().default(false),
|
can_reset_all: z.boolean().default(false),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const zCountersConfig = z.strictObject({
|
export const zCountersConfig = z.strictObject({
|
||||||
counters: zBoundedRecord(
|
counters: zBoundedRecord(z.record(zBoundedCharacters(0, 100), zCounter), 0, MAX_COUNTERS),
|
||||||
z.record(zBoundedCharacters(0, 100), zCounter),
|
|
||||||
0,
|
|
||||||
MAX_COUNTERS,
|
|
||||||
),
|
|
||||||
can_view: z.boolean(),
|
can_view: z.boolean(),
|
||||||
can_edit: z.boolean(),
|
can_edit: z.boolean(),
|
||||||
can_reset_all: z.boolean(),
|
can_reset_all: z.boolean(),
|
||||||
|
|
|
@ -9,14 +9,16 @@ import { CustomEventsPluginType, TCustomEvent } from "../types";
|
||||||
export const zSetChannelPermissionOverridesAction = z.strictObject({
|
export const zSetChannelPermissionOverridesAction = z.strictObject({
|
||||||
type: z.literal("set_channel_permission_overrides"),
|
type: z.literal("set_channel_permission_overrides"),
|
||||||
channel: zSnowflake,
|
channel: zSnowflake,
|
||||||
overrides: z.array(
|
overrides: z
|
||||||
z.strictObject({
|
.array(
|
||||||
type: z.union([z.literal("member"), z.literal("role")]),
|
z.strictObject({
|
||||||
id: zSnowflake,
|
type: z.union([z.literal("member"), z.literal("role")]),
|
||||||
allow: z.number(),
|
id: zSnowflake,
|
||||||
deny: z.number(),
|
allow: z.number(),
|
||||||
}),
|
deny: z.number(),
|
||||||
).max(15),
|
}),
|
||||||
|
)
|
||||||
|
.max(15),
|
||||||
});
|
});
|
||||||
export type TSetChannelPermissionOverridesAction = z.infer<typeof zSetChannelPermissionOverridesAction>;
|
export type TSetChannelPermissionOverridesAction = z.infer<typeof zSetChannelPermissionOverridesAction>;
|
||||||
|
|
||||||
|
|
|
@ -36,11 +36,7 @@ export const zCustomEvent = z.strictObject({
|
||||||
export type TCustomEvent = z.infer<typeof zCustomEvent>;
|
export type TCustomEvent = z.infer<typeof zCustomEvent>;
|
||||||
|
|
||||||
export const zCustomEventsConfig = z.strictObject({
|
export const zCustomEventsConfig = z.strictObject({
|
||||||
events: zBoundedRecord(
|
events: zBoundedRecord(z.record(zBoundedCharacters(0, 100), zCustomEvent), 0, 100),
|
||||||
z.record(zBoundedCharacters(0, 100), zCustomEvent),
|
|
||||||
0,
|
|
||||||
100,
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export interface CustomEventsPluginType extends BasePluginType {
|
export interface CustomEventsPluginType extends BasePluginType {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { Guild } from "discord.js";
|
import { Guild } from "discord.js";
|
||||||
import { BasePluginType, GlobalPluginData, globalPluginEventListener } from "knub";
|
import { BasePluginType, GlobalPluginData, globalPluginEventListener } from "knub";
|
||||||
|
import z from "zod";
|
||||||
import { AllowedGuilds } from "../../data/AllowedGuilds";
|
import { AllowedGuilds } from "../../data/AllowedGuilds";
|
||||||
import { Configs } from "../../data/Configs";
|
import { Configs } from "../../data/Configs";
|
||||||
import { env } from "../../env";
|
import { env } from "../../env";
|
||||||
import { zeppelinGlobalPlugin } from "../ZeppelinPluginBlueprint";
|
import { zeppelinGlobalPlugin } from "../ZeppelinPluginBlueprint";
|
||||||
import z from "zod";
|
|
||||||
|
|
||||||
interface GuildAccessMonitorPluginType extends BasePluginType {
|
interface GuildAccessMonitorPluginType extends BasePluginType {
|
||||||
state: {
|
state: {
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { Guild } from "discord.js";
|
import { Guild } from "discord.js";
|
||||||
import { guildPluginEventListener } from "knub";
|
import { guildPluginEventListener } from "knub";
|
||||||
|
import z from "zod";
|
||||||
import { AllowedGuilds } from "../../data/AllowedGuilds";
|
import { AllowedGuilds } from "../../data/AllowedGuilds";
|
||||||
import { ApiPermissionAssignments } from "../../data/ApiPermissionAssignments";
|
import { ApiPermissionAssignments } from "../../data/ApiPermissionAssignments";
|
||||||
import { MINUTES } from "../../utils";
|
import { MINUTES } from "../../utils";
|
||||||
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
|
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
|
||||||
import { GuildInfoSaverPluginType } from "./types";
|
import { GuildInfoSaverPluginType } from "./types";
|
||||||
import z from "zod";
|
|
||||||
|
|
||||||
export const GuildInfoSaverPlugin = zeppelinGuildPlugin<GuildInfoSaverPluginType>()({
|
export const GuildInfoSaverPlugin = zeppelinGuildPlugin<GuildInfoSaverPluginType>()({
|
||||||
name: "guild_info_saver",
|
name: "guild_info_saver",
|
||||||
|
|
|
@ -32,7 +32,9 @@ export function logMessageDelete(pluginData: GuildPluginData<LogsPluginType>, da
|
||||||
// See comment on FORMAT_NO_TIMESTAMP in types.ts
|
// See comment on FORMAT_NO_TIMESTAMP in types.ts
|
||||||
const config = pluginData.config.get();
|
const config = pluginData.config.get();
|
||||||
const timestampFormat =
|
const timestampFormat =
|
||||||
(config.format.timestamp !== FORMAT_NO_TIMESTAMP ? config.format.timestamp : null) ?? config.timestamp_format ?? undefined;
|
(config.format.timestamp !== FORMAT_NO_TIMESTAMP ? config.format.timestamp : null) ??
|
||||||
|
config.timestamp_format ??
|
||||||
|
undefined;
|
||||||
|
|
||||||
return log(
|
return log(
|
||||||
pluginData,
|
pluginData,
|
||||||
|
|
|
@ -29,10 +29,12 @@ const MAX_BATCH_TIME = 5000;
|
||||||
type ZLogFormatsHelper = {
|
type ZLogFormatsHelper = {
|
||||||
-readonly [K in keyof typeof LogType]: typeof zMessageContent;
|
-readonly [K in keyof typeof LogType]: typeof zMessageContent;
|
||||||
};
|
};
|
||||||
export const zLogFormats = z.strictObject(keys(LogType).reduce((map, logType) => {
|
export const zLogFormats = z.strictObject(
|
||||||
map[logType] = zMessageContent;
|
keys(LogType).reduce((map, logType) => {
|
||||||
return map;
|
map[logType] = zMessageContent;
|
||||||
}, {} as ZLogFormatsHelper));
|
return map;
|
||||||
|
}, {} as ZLogFormatsHelper),
|
||||||
|
);
|
||||||
export type TLogFormats = z.infer<typeof zLogFormats>;
|
export type TLogFormats = z.infer<typeof zLogFormats>;
|
||||||
|
|
||||||
const zLogChannel = z.strictObject({
|
const zLogChannel = z.strictObject({
|
||||||
|
@ -58,10 +60,12 @@ export type TLogChannelMap = z.infer<typeof zLogChannelMap>;
|
||||||
|
|
||||||
export const zLogsConfig = z.strictObject({
|
export const zLogsConfig = z.strictObject({
|
||||||
channels: zLogChannelMap,
|
channels: zLogChannelMap,
|
||||||
format: zLogFormats.merge(z.strictObject({
|
format: zLogFormats.merge(
|
||||||
// Legacy/deprecated, use timestamp_format below instead
|
z.strictObject({
|
||||||
timestamp: zBoundedCharacters(0, 64).nullable(),
|
// Legacy/deprecated, use timestamp_format below instead
|
||||||
})),
|
timestamp: zBoundedCharacters(0, 64).nullable(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
// Legacy/deprecated, if below is false mentions wont actually ping. In case you really want the old behavior, set below to true
|
// Legacy/deprecated, if below is false mentions wont actually ping. In case you really want the old behavior, set below to true
|
||||||
ping_user: z.boolean(),
|
ping_user: z.boolean(),
|
||||||
allow_user_mentions: z.boolean(),
|
allow_user_mentions: z.boolean(),
|
||||||
|
|
|
@ -2,6 +2,10 @@ import { GuildPluginData } from "knub";
|
||||||
import { LogType } from "../../../data/LogType";
|
import { LogType } from "../../../data/LogType";
|
||||||
import { LogsPluginType } from "../types";
|
import { LogsPluginType } from "../types";
|
||||||
|
|
||||||
export function isLogIgnored(pluginData: GuildPluginData<LogsPluginType>, type: keyof typeof LogType, ignoreId: string) {
|
export function isLogIgnored(
|
||||||
|
pluginData: GuildPluginData<LogsPluginType>,
|
||||||
|
type: keyof typeof LogType,
|
||||||
|
ignoreId: string,
|
||||||
|
) {
|
||||||
return pluginData.state.guildLogs.isLogIgnored(type, ignoreId);
|
return pluginData.state.guildLogs.isLogIgnored(type, ignoreId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,9 @@ import humanizeDuration from "humanize-duration";
|
||||||
import { GuildPluginData } from "knub";
|
import { GuildPluginData } from "knub";
|
||||||
import moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
import { CaseTypes } from "../../../data/CaseTypes";
|
import { CaseTypes } from "../../../data/CaseTypes";
|
||||||
|
import { LogType } from "../../../data/LogType";
|
||||||
import { Tempban } from "../../../data/entities/Tempban";
|
import { Tempban } from "../../../data/entities/Tempban";
|
||||||
|
import { logger } from "../../../logger";
|
||||||
import { resolveUser } from "../../../utils";
|
import { resolveUser } from "../../../utils";
|
||||||
import { CasesPlugin } from "../../Cases/CasesPlugin";
|
import { CasesPlugin } from "../../Cases/CasesPlugin";
|
||||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||||
|
@ -11,8 +13,6 @@ import { IgnoredEventType, ModActionsPluginType } from "../types";
|
||||||
import { formatReasonWithAttachments } from "./formatReasonWithAttachments";
|
import { formatReasonWithAttachments } from "./formatReasonWithAttachments";
|
||||||
import { ignoreEvent } from "./ignoreEvent";
|
import { ignoreEvent } from "./ignoreEvent";
|
||||||
import { isBanned } from "./isBanned";
|
import { isBanned } from "./isBanned";
|
||||||
import { LogType } from "../../../data/LogType";
|
|
||||||
import { logger } from "../../../logger";
|
|
||||||
|
|
||||||
export async function clearTempban(pluginData: GuildPluginData<ModActionsPluginType>, tempban: Tempban) {
|
export async function clearTempban(pluginData: GuildPluginData<ModActionsPluginType>, tempban: Tempban) {
|
||||||
if (!(await isBanned(pluginData, tempban.user_id))) {
|
if (!(await isBanned(pluginData, tempban.user_id))) {
|
||||||
|
|
|
@ -22,10 +22,7 @@ export type PendingMemberRoleChanges = {
|
||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const zReactionRolePair = z.union([
|
const zReactionRolePair = z.union([z.tuple([z.string(), z.string(), z.string()]), z.tuple([z.string(), z.string()])]);
|
||||||
z.tuple([z.string(), z.string(), z.string()]),
|
|
||||||
z.tuple([z.string(), z.string()]),
|
|
||||||
]);
|
|
||||||
export type TReactionRolePair = z.infer<typeof zReactionRolePair>;
|
export type TReactionRolePair = z.infer<typeof zReactionRolePair>;
|
||||||
|
|
||||||
export interface ReactionRolesPluginType extends BasePluginType {
|
export interface ReactionRolesPluginType extends BasePluginType {
|
||||||
|
|
|
@ -11,89 +11,99 @@ const zRoleButtonOption = z.strictObject({
|
||||||
label: z.string().nullable().default(null),
|
label: z.string().nullable().default(null),
|
||||||
emoji: z.string().nullable().default(null),
|
emoji: z.string().nullable().default(null),
|
||||||
// https://discord.js.org/#/docs/discord.js/v13/typedef/MessageButtonStyle
|
// https://discord.js.org/#/docs/discord.js/v13/typedef/MessageButtonStyle
|
||||||
style: z.union([
|
style: z
|
||||||
z.literal(ButtonStyle.Primary),
|
.union([
|
||||||
z.literal(ButtonStyle.Secondary),
|
z.literal(ButtonStyle.Primary),
|
||||||
z.literal(ButtonStyle.Success),
|
z.literal(ButtonStyle.Secondary),
|
||||||
z.literal(ButtonStyle.Danger),
|
z.literal(ButtonStyle.Success),
|
||||||
|
z.literal(ButtonStyle.Danger),
|
||||||
|
|
||||||
// The following are deprecated
|
// The following are deprecated
|
||||||
z.literal("PRIMARY"),
|
z.literal("PRIMARY"),
|
||||||
z.literal("SECONDARY"),
|
z.literal("SECONDARY"),
|
||||||
z.literal("SUCCESS"),
|
z.literal("SUCCESS"),
|
||||||
z.literal("DANGER"),
|
z.literal("DANGER"),
|
||||||
// z.literal("LINK"), // Role buttons don't use link buttons, but adding this here so it's documented why it's not available
|
// z.literal("LINK"), // Role buttons don't use link buttons, but adding this here so it's documented why it's not available
|
||||||
]).nullable().default(null),
|
])
|
||||||
|
.nullable()
|
||||||
|
.default(null),
|
||||||
start_new_row: z.boolean().default(false),
|
start_new_row: z.boolean().default(false),
|
||||||
});
|
});
|
||||||
export type TRoleButtonOption = z.infer<typeof zRoleButtonOption>;
|
export type TRoleButtonOption = z.infer<typeof zRoleButtonOption>;
|
||||||
|
|
||||||
const zRoleButtonsConfigItem = z.strictObject({
|
const zRoleButtonsConfigItem = z
|
||||||
// Typed as "never" because you are not expected to supply this directly.
|
.strictObject({
|
||||||
// The transform instead picks it up from the property key and the output type is a string.
|
// Typed as "never" because you are not expected to supply this directly.
|
||||||
name: z.never().optional().transform((_, ctx) => {
|
// The transform instead picks it up from the property key and the output type is a string.
|
||||||
const ruleName = String(ctx.path[ctx.path.length - 2]).trim();
|
name: z
|
||||||
if (! ruleName) {
|
.never()
|
||||||
ctx.addIssue({
|
.optional()
|
||||||
code: z.ZodIssueCode.custom,
|
.transform((_, ctx) => {
|
||||||
message: "Role buttons must have names",
|
const ruleName = String(ctx.path[ctx.path.length - 2]).trim();
|
||||||
});
|
if (!ruleName) {
|
||||||
return z.NEVER;
|
ctx.addIssue({
|
||||||
}
|
code: z.ZodIssueCode.custom,
|
||||||
return ruleName;
|
message: "Role buttons must have names",
|
||||||
}),
|
});
|
||||||
message: z.union([
|
return z.NEVER;
|
||||||
z.strictObject({
|
}
|
||||||
channel_id: zSnowflake,
|
return ruleName;
|
||||||
message_id: zSnowflake,
|
}),
|
||||||
}),
|
message: z.union([
|
||||||
z.strictObject({
|
z.strictObject({
|
||||||
channel_id: zSnowflake,
|
channel_id: zSnowflake,
|
||||||
content: zMessageContent,
|
message_id: zSnowflake,
|
||||||
}),
|
}),
|
||||||
]),
|
z.strictObject({
|
||||||
options: z.array(zRoleButtonOption).max(25),
|
channel_id: zSnowflake,
|
||||||
exclusive: z.boolean().default(false),
|
content: zMessageContent,
|
||||||
})
|
}),
|
||||||
.refine((parsed) => {
|
]),
|
||||||
try {
|
options: z.array(zRoleButtonOption).max(25),
|
||||||
createButtonComponents(parsed);
|
exclusive: z.boolean().default(false),
|
||||||
} catch (err) {
|
})
|
||||||
if (err instanceof TooManyComponentsError) {
|
.refine(
|
||||||
return false;
|
(parsed) => {
|
||||||
|
try {
|
||||||
|
createButtonComponents(parsed);
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof TooManyComponentsError) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
}
|
}
|
||||||
throw err;
|
return true;
|
||||||
}
|
},
|
||||||
return true;
|
{
|
||||||
}, {
|
message: "Too many options; can only have max 5 buttons per row on max 5 rows.",
|
||||||
message: "Too many options; can only have max 5 buttons per row on max 5 rows."
|
},
|
||||||
});
|
);
|
||||||
export type TRoleButtonsConfigItem = z.infer<typeof zRoleButtonsConfigItem>;
|
export type TRoleButtonsConfigItem = z.infer<typeof zRoleButtonsConfigItem>;
|
||||||
|
|
||||||
export const zRoleButtonsConfig = z.strictObject({
|
export const zRoleButtonsConfig = z
|
||||||
buttons: zBoundedRecord(
|
.strictObject({
|
||||||
z.record(zBoundedCharacters(1, 16), zRoleButtonsConfigItem),
|
buttons: zBoundedRecord(z.record(zBoundedCharacters(1, 16), zRoleButtonsConfigItem), 0, 100),
|
||||||
0,
|
can_reset: z.boolean(),
|
||||||
100,
|
})
|
||||||
),
|
.refine(
|
||||||
can_reset: z.boolean(),
|
(parsed) => {
|
||||||
})
|
const seenMessages = new Set();
|
||||||
.refine((parsed) => {
|
for (const button of Object.values(parsed.buttons)) {
|
||||||
const seenMessages = new Set();
|
if (button.message) {
|
||||||
for (const button of Object.values(parsed.buttons)) {
|
if ("message_id" in button.message) {
|
||||||
if (button.message) {
|
if (seenMessages.has(button.message.message_id)) {
|
||||||
if ("message_id" in button.message) {
|
return false;
|
||||||
if (seenMessages.has(button.message.message_id)) {
|
}
|
||||||
return false;
|
seenMessages.add(button.message.message_id);
|
||||||
}
|
}
|
||||||
seenMessages.add(button.message.message_id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return true;
|
||||||
return true;
|
},
|
||||||
}, {
|
{
|
||||||
message: "Can't target the same message with two sets of role buttons",
|
message: "Can't target the same message with two sets of role buttons",
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export interface RoleButtonsPluginType extends BasePluginType {
|
export interface RoleButtonsPluginType extends BasePluginType {
|
||||||
config: z.infer<typeof zRoleButtonsConfig>;
|
config: z.infer<typeof zRoleButtonsConfig>;
|
||||||
|
|
|
@ -4,9 +4,10 @@ import { zBoundedCharacters, zBoundedRecord } from "../../utils";
|
||||||
|
|
||||||
const zRoleMap = z.record(
|
const zRoleMap = z.record(
|
||||||
zBoundedCharacters(1, 100),
|
zBoundedCharacters(1, 100),
|
||||||
z.array(zBoundedCharacters(1, 2000))
|
z
|
||||||
|
.array(zBoundedCharacters(1, 2000))
|
||||||
.max(100)
|
.max(100)
|
||||||
.transform((parsed) => parsed.map(v => v.toLowerCase())),
|
.transform((parsed) => parsed.map((v) => v.toLowerCase())),
|
||||||
);
|
);
|
||||||
|
|
||||||
const zSelfGrantableRoleEntry = z.strictObject({
|
const zSelfGrantableRoleEntry = z.strictObject({
|
||||||
|
|
|
@ -11,14 +11,8 @@ const zBaseSingleSpamConfig = z.strictObject({
|
||||||
count: z.number(),
|
count: z.number(),
|
||||||
mute: z.boolean().default(false),
|
mute: z.boolean().default(false),
|
||||||
mute_time: z.number().nullable().default(null),
|
mute_time: z.number().nullable().default(null),
|
||||||
remove_roles_on_mute: z.union([
|
remove_roles_on_mute: z.union([z.boolean(), z.array(zSnowflake)]).default(false),
|
||||||
z.boolean(),
|
restore_roles_on_mute: z.union([z.boolean(), z.array(zSnowflake)]).default(false),
|
||||||
z.array(zSnowflake),
|
|
||||||
]).default(false),
|
|
||||||
restore_roles_on_mute: z.union([
|
|
||||||
z.boolean(),
|
|
||||||
z.array(zSnowflake),
|
|
||||||
]).default(false),
|
|
||||||
clean: z.boolean().default(false),
|
clean: z.boolean().default(false),
|
||||||
});
|
});
|
||||||
export type TBaseSingleSpamConfig = z.infer<typeof zBaseSingleSpamConfig>;
|
export type TBaseSingleSpamConfig = z.infer<typeof zBaseSingleSpamConfig>;
|
||||||
|
|
|
@ -18,11 +18,7 @@ const zStarboardOpts = z.strictObject({
|
||||||
export type TStarboardOpts = z.infer<typeof zStarboardOpts>;
|
export type TStarboardOpts = z.infer<typeof zStarboardOpts>;
|
||||||
|
|
||||||
export const zStarboardConfig = z.strictObject({
|
export const zStarboardConfig = z.strictObject({
|
||||||
boards: zBoundedRecord(
|
boards: zBoundedRecord(z.record(z.string(), zStarboardOpts), 0, 100),
|
||||||
z.record(z.string(), zStarboardOpts),
|
|
||||||
0,
|
|
||||||
100,
|
|
||||||
),
|
|
||||||
can_migrate: z.boolean(),
|
can_migrate: z.boolean(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { MessageCreateOptions } from "discord.js";
|
import { MessageCreateOptions } from "discord.js";
|
||||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||||
|
import { logger } from "../../../logger";
|
||||||
import { sendErrorMessage } from "../../../pluginUtils";
|
import { sendErrorMessage } from "../../../pluginUtils";
|
||||||
import { TemplateParseError } from "../../../templateFormatter";
|
import { TemplateParseError } from "../../../templateFormatter";
|
||||||
import { memberToTemplateSafeMember, userToTemplateSafeUser } from "../../../utils/templateSafeObjects";
|
import { memberToTemplateSafeMember, userToTemplateSafeUser } from "../../../utils/templateSafeObjects";
|
||||||
import { tagsCmd } from "../types";
|
import { tagsCmd } from "../types";
|
||||||
import { renderTagBody } from "../util/renderTagBody";
|
import { renderTagBody } from "../util/renderTagBody";
|
||||||
import { logger } from "../../../logger";
|
|
||||||
|
|
||||||
export const TagEvalCmd = tagsCmd({
|
export const TagEvalCmd = tagsCmd({
|
||||||
trigger: "tag eval",
|
trigger: "tag eval",
|
||||||
|
@ -35,13 +35,11 @@ export const TagEvalCmd = tagsCmd({
|
||||||
|
|
||||||
msg.channel.send(rendered);
|
msg.channel.send(rendered);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const errorMessage = e instanceof TemplateParseError
|
const errorMessage = e instanceof TemplateParseError ? e.message : "Internal error";
|
||||||
? e.message
|
|
||||||
: "Internal error";
|
|
||||||
|
|
||||||
sendErrorMessage(pluginData, msg.channel, `Failed to render tag: ${errorMessage}`);
|
sendErrorMessage(pluginData, msg.channel, `Failed to render tag: ${errorMessage}`);
|
||||||
|
|
||||||
if (! (e instanceof TemplateParseError)) {
|
if (!(e instanceof TemplateParseError)) {
|
||||||
logger.warn(`Internal error evaluating tag in ${pluginData.guild.id}: ${e}`);
|
logger.warn(`Internal error evaluating tag in ${pluginData.guild.id}: ${e}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,52 +9,48 @@ import { zEmbedInput } from "../../utils";
|
||||||
export const zTag = z.union([z.string(), zEmbedInput]);
|
export const zTag = z.union([z.string(), zEmbedInput]);
|
||||||
export type TTag = z.infer<typeof zTag>;
|
export type TTag = z.infer<typeof zTag>;
|
||||||
|
|
||||||
export const zTagCategory = z.strictObject({
|
export const zTagCategory = z
|
||||||
prefix: z.string().nullable().default(null),
|
.strictObject({
|
||||||
delete_with_command: z.boolean().default(false),
|
prefix: z.string().nullable().default(null),
|
||||||
|
delete_with_command: z.boolean().default(false),
|
||||||
|
|
||||||
user_tag_cooldown: z.union([z.string(), z.number()]).nullable().default(null), // Per user, per tag
|
user_tag_cooldown: z.union([z.string(), z.number()]).nullable().default(null), // Per user, per tag
|
||||||
user_category_cooldown: z.union([z.string(), z.number()]).nullable().default(null), // Per user, per tag category
|
user_category_cooldown: z.union([z.string(), z.number()]).nullable().default(null), // Per user, per tag category
|
||||||
global_tag_cooldown: z.union([z.string(), z.number()]).nullable().default(null), // Any user, per tag
|
global_tag_cooldown: z.union([z.string(), z.number()]).nullable().default(null), // Any user, per tag
|
||||||
allow_mentions: z.boolean().nullable().default(null),
|
allow_mentions: z.boolean().nullable().default(null),
|
||||||
global_category_cooldown: z.union([z.string(), z.number()]).nullable().default(null), // Any user, per category
|
global_category_cooldown: z.union([z.string(), z.number()]).nullable().default(null), // Any user, per category
|
||||||
auto_delete_command: z.boolean().nullable().default(null),
|
auto_delete_command: z.boolean().nullable().default(null),
|
||||||
|
|
||||||
tags: z.record(z.string(), zTag),
|
tags: z.record(z.string(), zTag),
|
||||||
|
|
||||||
can_use: z.boolean().nullable().default(null),
|
can_use: z.boolean().nullable().default(null),
|
||||||
})
|
})
|
||||||
.refine(
|
.refine((parsed) => !(parsed.auto_delete_command && parsed.delete_with_command), {
|
||||||
(parsed) => ! (parsed.auto_delete_command && parsed.delete_with_command),
|
message: "Cannot have both (category specific) delete_with_command and auto_delete_command enabled",
|
||||||
{
|
});
|
||||||
message: "Cannot have both (category specific) delete_with_command and auto_delete_command enabled",
|
|
||||||
},
|
|
||||||
);
|
|
||||||
export type TTagCategory = z.infer<typeof zTagCategory>;
|
export type TTagCategory = z.infer<typeof zTagCategory>;
|
||||||
|
|
||||||
export const zTagsConfig = z.strictObject({
|
export const zTagsConfig = z
|
||||||
prefix: z.string(),
|
.strictObject({
|
||||||
delete_with_command: z.boolean(),
|
prefix: z.string(),
|
||||||
|
delete_with_command: z.boolean(),
|
||||||
|
|
||||||
user_tag_cooldown: z.union([z.string(), z.number()]).nullable(), // Per user, per tag
|
user_tag_cooldown: z.union([z.string(), z.number()]).nullable(), // Per user, per tag
|
||||||
global_tag_cooldown: z.union([z.string(), z.number()]).nullable(), // Any user, per tag
|
global_tag_cooldown: z.union([z.string(), z.number()]).nullable(), // Any user, per tag
|
||||||
user_cooldown: z.union([z.string(), z.number()]).nullable(), // Per user
|
user_cooldown: z.union([z.string(), z.number()]).nullable(), // Per user
|
||||||
allow_mentions: z.boolean(), // Per user
|
allow_mentions: z.boolean(), // Per user
|
||||||
global_cooldown: z.union([z.string(), z.number()]).nullable(), // Any tag use
|
global_cooldown: z.union([z.string(), z.number()]).nullable(), // Any tag use
|
||||||
auto_delete_command: z.boolean(), // Any tag
|
auto_delete_command: z.boolean(), // Any tag
|
||||||
|
|
||||||
categories: z.record(z.string(), zTagCategory),
|
categories: z.record(z.string(), zTagCategory),
|
||||||
|
|
||||||
can_create: z.boolean(),
|
can_create: z.boolean(),
|
||||||
can_use: z.boolean(),
|
can_use: z.boolean(),
|
||||||
can_list: z.boolean(),
|
can_list: z.boolean(),
|
||||||
})
|
})
|
||||||
.refine(
|
.refine((parsed) => !(parsed.auto_delete_command && parsed.delete_with_command), {
|
||||||
(parsed) => ! (parsed.auto_delete_command && parsed.delete_with_command),
|
|
||||||
{
|
|
||||||
message: "Cannot have both (category specific) delete_with_command and auto_delete_command enabled",
|
message: "Cannot have both (category specific) delete_with_command and auto_delete_command enabled",
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
export interface TagsPluginType extends BasePluginType {
|
export interface TagsPluginType extends BasePluginType {
|
||||||
config: z.infer<typeof zTagsConfig>;
|
config: z.infer<typeof zTagsConfig>;
|
||||||
|
|
|
@ -2,11 +2,11 @@ import { Snowflake, TextChannel } from "discord.js";
|
||||||
import { GuildPluginData } from "knub";
|
import { GuildPluginData } from "knub";
|
||||||
import { SavedMessage } from "../../../data/entities/SavedMessage";
|
import { SavedMessage } from "../../../data/entities/SavedMessage";
|
||||||
import { convertDelayStringToMS, resolveMember, zStrictMessageContent } from "../../../utils";
|
import { convertDelayStringToMS, resolveMember, zStrictMessageContent } from "../../../utils";
|
||||||
|
import { erisAllowedMentionsToDjsMentionOptions } from "../../../utils/erisAllowedMentionsToDjsMentionOptions";
|
||||||
import { messageIsEmpty } from "../../../utils/messageIsEmpty";
|
import { messageIsEmpty } from "../../../utils/messageIsEmpty";
|
||||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||||
import { TagsPluginType } from "../types";
|
import { TagsPluginType } from "../types";
|
||||||
import { matchAndRenderTagFromString } from "./matchAndRenderTagFromString";
|
import { matchAndRenderTagFromString } from "./matchAndRenderTagFromString";
|
||||||
import { erisAllowedMentionsToDjsMentionOptions } from "../../../utils/erisAllowedMentionsToDjsMentionOptions";
|
|
||||||
|
|
||||||
export async function onMessageCreate(pluginData: GuildPluginData<TagsPluginType>, msg: SavedMessage) {
|
export async function onMessageCreate(pluginData: GuildPluginData<TagsPluginType>, msg: SavedMessage) {
|
||||||
if (msg.is_bot) return;
|
if (msg.is_bot) return;
|
||||||
|
@ -85,7 +85,7 @@ export async function onMessageCreate(pluginData: GuildPluginData<TagsPluginType
|
||||||
}
|
}
|
||||||
|
|
||||||
const validated = zStrictMessageContent.safeParse(tagResult.renderedContent);
|
const validated = zStrictMessageContent.safeParse(tagResult.renderedContent);
|
||||||
if (! validated.success) {
|
if (!validated.success) {
|
||||||
pluginData.getPlugin(LogsPlugin).logBotAlert({
|
pluginData.getPlugin(LogsPlugin).logBotAlert({
|
||||||
body: `Rendering tag ${tagResult.tagName} resulted in an invalid message: ${validated.error.message}`,
|
body: `Rendering tag ${tagResult.tagName} resulted in an invalid message: ${validated.error.message}`,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { GuildMember } from "discord.js";
|
import { GuildMember } from "discord.js";
|
||||||
import { GuildPluginData } from "knub";
|
import { GuildPluginData } from "knub";
|
||||||
import { parseArguments } from "knub-command-manager";
|
import { parseArguments } from "knub-command-manager";
|
||||||
|
import { logger } from "../../../logger";
|
||||||
import { TemplateParseError } from "../../../templateFormatter";
|
import { TemplateParseError } from "../../../templateFormatter";
|
||||||
import { StrictMessageContent, validateAndParseMessageContent } from "../../../utils";
|
import { StrictMessageContent, validateAndParseMessageContent } from "../../../utils";
|
||||||
import { memberToTemplateSafeMember, userToTemplateSafeUser } from "../../../utils/templateSafeObjects";
|
import { memberToTemplateSafeMember, userToTemplateSafeUser } from "../../../utils/templateSafeObjects";
|
||||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||||
import { TTag, TagsPluginType } from "../types";
|
import { TTag, TagsPluginType } from "../types";
|
||||||
import { renderTagBody } from "./renderTagBody";
|
import { renderTagBody } from "./renderTagBody";
|
||||||
import { logger } from "../../../logger";
|
|
||||||
|
|
||||||
export async function renderTagFromString(
|
export async function renderTagFromString(
|
||||||
pluginData: GuildPluginData<TagsPluginType>,
|
pluginData: GuildPluginData<TagsPluginType>,
|
||||||
|
@ -36,14 +36,12 @@ export async function renderTagFromString(
|
||||||
return validateAndParseMessageContent(rendered);
|
return validateAndParseMessageContent(rendered);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const logs = pluginData.getPlugin(LogsPlugin);
|
const logs = pluginData.getPlugin(LogsPlugin);
|
||||||
const errorMessage = e instanceof TemplateParseError
|
const errorMessage = e instanceof TemplateParseError ? e.message : "Internal error";
|
||||||
? e.message
|
|
||||||
: "Internal error";
|
|
||||||
logs.logBotAlert({
|
logs.logBotAlert({
|
||||||
body: `Failed to render tag \`${prefix}${tagName}\`: ${errorMessage}`,
|
body: `Failed to render tag \`${prefix}${tagName}\`: ${errorMessage}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (! (e instanceof TemplateParseError)) {
|
if (!(e instanceof TemplateParseError)) {
|
||||||
logger.warn(`Internal error rendering tag ${tagName} in ${pluginData.guild.id}: ${e}`);
|
logger.warn(`Internal error rendering tag ${tagName} in ${pluginData.guild.id}: ${e}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,11 +79,7 @@ export const InfoCmd = utilityCmd({
|
||||||
const messageTarget = await resolveMessageTarget(pluginData, value);
|
const messageTarget = await resolveMessageTarget(pluginData, value);
|
||||||
if (messageTarget) {
|
if (messageTarget) {
|
||||||
if (canReadChannel(messageTarget.channel, message.member)) {
|
if (canReadChannel(messageTarget.channel, message.member)) {
|
||||||
const embed = await getMessageInfoEmbed(
|
const embed = await getMessageInfoEmbed(pluginData, messageTarget.channel.id, messageTarget.messageId);
|
||||||
pluginData,
|
|
||||||
messageTarget.channel.id,
|
|
||||||
messageTarget.messageId,
|
|
||||||
);
|
|
||||||
if (embed) {
|
if (embed) {
|
||||||
message.channel.send({ embeds: [embed] });
|
message.channel.send({ embeds: [embed] });
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -20,11 +20,7 @@ export const MessageInfoCmd = utilityCmd({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const embed = await getMessageInfoEmbed(
|
const embed = await getMessageInfoEmbed(pluginData, args.message.channel.id, args.message.messageId);
|
||||||
pluginData,
|
|
||||||
args.message.channel.id,
|
|
||||||
args.message.messageId,
|
|
||||||
);
|
|
||||||
if (!embed) {
|
if (!embed) {
|
||||||
sendErrorMessage(pluginData, message.channel, "Unknown message");
|
sendErrorMessage(pluginData, message.channel, "Unknown message");
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -6,10 +6,7 @@ import { UtilityPluginType } from "../types";
|
||||||
|
|
||||||
const MENTION_ICON = "https://cdn.discordapp.com/attachments/705009450855039042/839284872152481792/mention.png";
|
const MENTION_ICON = "https://cdn.discordapp.com/attachments/705009450855039042/839284872152481792/mention.png";
|
||||||
|
|
||||||
export async function getRoleInfoEmbed(
|
export async function getRoleInfoEmbed(pluginData: GuildPluginData<UtilityPluginType>, role: Role): Promise<APIEmbed> {
|
||||||
pluginData: GuildPluginData<UtilityPluginType>,
|
|
||||||
role: Role,
|
|
||||||
): Promise<APIEmbed> {
|
|
||||||
const embed: EmbedWith<"fields" | "author" | "color"> = {
|
const embed: EmbedWith<"fields" | "author" | "color"> = {
|
||||||
fields: [],
|
fields: [],
|
||||||
author: {
|
author: {
|
||||||
|
|
|
@ -4,10 +4,7 @@ import { snowflakeToTimestamp } from "../../../utils/snowflakeToTimestamp";
|
||||||
|
|
||||||
const SNOWFLAKE_ICON = "https://cdn.discordapp.com/attachments/740650744830623756/742020790471491668/snowflake.png";
|
const SNOWFLAKE_ICON = "https://cdn.discordapp.com/attachments/740650744830623756/742020790471491668/snowflake.png";
|
||||||
|
|
||||||
export async function getSnowflakeInfoEmbed(
|
export async function getSnowflakeInfoEmbed(snowflake: string, showUnknownWarning = false): Promise<APIEmbed> {
|
||||||
snowflake: string,
|
|
||||||
showUnknownWarning = false,
|
|
||||||
): Promise<APIEmbed> {
|
|
||||||
const embed: EmbedWith<"fields" | "author"> = {
|
const embed: EmbedWith<"fields" | "author"> = {
|
||||||
fields: [],
|
fields: [],
|
||||||
author: {
|
author: {
|
||||||
|
|
|
@ -14,7 +14,15 @@ import { ArgsFromSignatureOrArray, GuildPluginData } from "knub";
|
||||||
import moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
import { RegExpRunner, allowTimeout } from "../../RegExpRunner";
|
import { RegExpRunner, allowTimeout } from "../../RegExpRunner";
|
||||||
import { getBaseUrl, sendErrorMessage } from "../../pluginUtils";
|
import { getBaseUrl, sendErrorMessage } from "../../pluginUtils";
|
||||||
import { InvalidRegexError, MINUTES, inputPatternToRegExp, multiSorter, renderUserUsername, sorter, trimLines } from "../../utils";
|
import {
|
||||||
|
InvalidRegexError,
|
||||||
|
MINUTES,
|
||||||
|
inputPatternToRegExp,
|
||||||
|
multiSorter,
|
||||||
|
renderUserUsername,
|
||||||
|
sorter,
|
||||||
|
trimLines,
|
||||||
|
} from "../../utils";
|
||||||
import { asyncFilter } from "../../utils/async";
|
import { asyncFilter } from "../../utils/async";
|
||||||
import { hasDiscordPermissions } from "../../utils/hasDiscordPermissions";
|
import { hasDiscordPermissions } from "../../utils/hasDiscordPermissions";
|
||||||
import { banSearchSignature } from "./commands/BanSearchCmd";
|
import { banSearchSignature } from "./commands/BanSearchCmd";
|
||||||
|
|
|
@ -7,8 +7,8 @@ import {
|
||||||
GuildPluginBlueprint,
|
GuildPluginBlueprint,
|
||||||
GuildPluginData,
|
GuildPluginData,
|
||||||
} from "knub";
|
} from "knub";
|
||||||
import { TMarkdown } from "../types";
|
|
||||||
import { ZodTypeAny } from "zod";
|
import { ZodTypeAny } from "zod";
|
||||||
|
import { TMarkdown } from "../types";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GUILD PLUGINS
|
* GUILD PLUGINS
|
||||||
|
|
|
@ -87,8 +87,10 @@ export function isDiscordAPIError(err: Error | string): err is DiscordAPIError {
|
||||||
}
|
}
|
||||||
|
|
||||||
// null | undefined -> undefined
|
// null | undefined -> undefined
|
||||||
export function zNullishToUndefined<T extends z.ZodTypeAny>(type: T): ZodEffects<T, NonNullable<z.output<T>> | undefined> {
|
export function zNullishToUndefined<T extends z.ZodTypeAny>(
|
||||||
return type.transform(v => v ?? undefined);
|
type: T,
|
||||||
|
): ZodEffects<T, NonNullable<z.output<T>> | undefined> {
|
||||||
|
return type.transform((v) => v ?? undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getScalarDifference<T extends object>(
|
export function getScalarDifference<T extends object>(
|
||||||
|
@ -151,15 +153,18 @@ export type GroupDMInvite = Invite & {
|
||||||
};
|
};
|
||||||
|
|
||||||
export function zBoundedCharacters(min: number, max: number) {
|
export function zBoundedCharacters(min: number, max: number) {
|
||||||
return z.string().refine(str => {
|
return z.string().refine(
|
||||||
const len = [...str].length; // Unicode aware character split
|
(str) => {
|
||||||
return (len >= min && len <= max);
|
const len = [...str].length; // Unicode aware character split
|
||||||
}, {
|
return len >= min && len <= max;
|
||||||
message: `String must be between ${min} and ${max} characters long`,
|
},
|
||||||
});
|
{
|
||||||
|
message: `String must be between ${min} and ${max} characters long`,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const zSnowflake = z.string().refine(str => isSnowflake(str), {
|
export const zSnowflake = z.string().refine((str) => isSnowflake(str), {
|
||||||
message: "Invalid snowflake ID",
|
message: "Invalid snowflake ID",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -188,7 +193,7 @@ export function zRegex<T extends ZodString>(zStr: T) {
|
||||||
if (err instanceof InvalidRegexError) {
|
if (err instanceof InvalidRegexError) {
|
||||||
ctx.addIssue({
|
ctx.addIssue({
|
||||||
code: z.ZodIssueCode.custom,
|
code: z.ZodIssueCode.custom,
|
||||||
message: "Invalid regex"
|
message: "Invalid regex",
|
||||||
});
|
});
|
||||||
return z.NEVER;
|
return z.NEVER;
|
||||||
}
|
}
|
||||||
|
@ -343,8 +348,18 @@ function dropNullValuesRecursively(obj: any) {
|
||||||
*/
|
*/
|
||||||
export const zAllowedMentions = z.strictObject({
|
export const zAllowedMentions = z.strictObject({
|
||||||
everyone: zNullishToUndefined(z.boolean().nullable().optional()),
|
everyone: zNullishToUndefined(z.boolean().nullable().optional()),
|
||||||
users: zNullishToUndefined(z.union([z.boolean(), z.array(z.string())]).nullable().optional()),
|
users: zNullishToUndefined(
|
||||||
roles: zNullishToUndefined(z.union([z.boolean(), z.array(z.string())]).nullable().optional()),
|
z
|
||||||
|
.union([z.boolean(), z.array(z.string())])
|
||||||
|
.nullable()
|
||||||
|
.optional(),
|
||||||
|
),
|
||||||
|
roles: zNullishToUndefined(
|
||||||
|
z
|
||||||
|
.union([z.boolean(), z.array(z.string())])
|
||||||
|
.nullable()
|
||||||
|
.optional(),
|
||||||
|
),
|
||||||
replied_user: zNullishToUndefined(z.boolean().nullable().optional()),
|
replied_user: zNullishToUndefined(z.boolean().nullable().optional()),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -359,18 +374,28 @@ export function dropPropertiesByName(obj, propName) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function zBoundedRecord<TRecord extends ZodRecord<any, any>>(record: TRecord, minKeys: number, maxKeys: number): ZodEffects<TRecord> {
|
export function zBoundedRecord<TRecord extends ZodRecord<any, any>>(
|
||||||
return record.refine(data => {
|
record: TRecord,
|
||||||
const len = Object.keys(data).length;
|
minKeys: number,
|
||||||
return (len >= minKeys && len <= maxKeys);
|
maxKeys: number,
|
||||||
}, {
|
): ZodEffects<TRecord> {
|
||||||
message: `Object must have ${minKeys}-${maxKeys} keys`,
|
return record.refine(
|
||||||
});
|
(data) => {
|
||||||
|
const len = Object.keys(data).length;
|
||||||
|
return len >= minKeys && len <= maxKeys;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
message: `Object must have ${minKeys}-${maxKeys} keys`,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const zDelayString = z.string().max(32).refine(str => convertDelayStringToMS(str) !== null, {
|
export const zDelayString = z
|
||||||
message: "Invalid delay string",
|
.string()
|
||||||
});
|
.max(32)
|
||||||
|
.refine((str) => convertDelayStringToMS(str) !== null, {
|
||||||
|
message: "Invalid delay string",
|
||||||
|
});
|
||||||
|
|
||||||
// To avoid running into issues with the JS max date vaLue, we cap maximum delay strings *far* below that.
|
// To avoid running into issues with the JS max date vaLue, we cap maximum delay strings *far* below that.
|
||||||
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#The_ECMAScript_epoch_and_timestamps
|
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#The_ECMAScript_epoch_and_timestamps
|
||||||
|
@ -1487,9 +1512,11 @@ export function renderUserUsername(user: User | UnknownUser): string {
|
||||||
return renderUsername(user.username, user.discriminator);
|
return renderUsername(user.username, user.discriminator);
|
||||||
}
|
}
|
||||||
|
|
||||||
type Entries<T> = Array<{
|
type Entries<T> = Array<
|
||||||
[Key in keyof T]-?: [Key, T[Key]];
|
{
|
||||||
}[keyof T]>;
|
[Key in keyof T]-?: [Key, T[Key]];
|
||||||
|
}[keyof T]
|
||||||
|
>;
|
||||||
|
|
||||||
export function entries<T extends object>(object: T) {
|
export function entries<T extends object>(object: T) {
|
||||||
return Object.entries(object) as Entries<T>;
|
return Object.entries(object) as Entries<T>;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { ZodIssue } from "zod";
|
import { ZodIssue } from "zod";
|
||||||
|
|
||||||
export function formatZodIssue(issue: ZodIssue): string {
|
export function formatZodIssue(issue: ZodIssue): string {
|
||||||
const path = issue.path.join("/");
|
const path = issue.path.join("/");
|
||||||
return `${path}: ${issue.message}`;
|
return `${path}: ${issue.message}`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,10 @@ import { loadYamlSafely } from "./utils/loadYamlSafely";
|
||||||
import { ObjectAliasError } from "./utils/validateNoObjectAliases";
|
import { ObjectAliasError } from "./utils/validateNoObjectAliases";
|
||||||
|
|
||||||
function writeError(key: string, error: string) {
|
function writeError(key: string, error: string) {
|
||||||
const indented = error.split("\n").map(s => " ".repeat(64) + s).join("\n");
|
const indented = error
|
||||||
|
.split("\n")
|
||||||
|
.map((s) => " ".repeat(64) + s)
|
||||||
|
.join("\n");
|
||||||
const prefix = `Invalid config ${key}:`;
|
const prefix = `Invalid config ${key}:`;
|
||||||
const prefixed = prefix + indented.slice(prefix.length);
|
const prefixed = prefix + indented.slice(prefix.length);
|
||||||
console.log(prefixed + "\n\n");
|
console.log(prefixed + "\n\n");
|
||||||
|
|
Loading…
Add table
Reference in a new issue