3
0
Fork 0
mirror of https://github.com/ZeppelinBot/Zeppelin.git synced 2025-05-10 12:25:02 +00:00

refactor: replace io-ts with zod

This commit is contained in:
Dragory 2024-01-14 14:25:42 +00:00
parent fafaefa1fb
commit 28692962bc
No known key found for this signature in database
161 changed files with 1450 additions and 2105 deletions

View file

@ -1,6 +1,6 @@
import { PermissionFlagsBits, Snowflake } from "discord.js";
import * as t from "io-ts";
import { nonNullish, unique } from "../../../utils";
import z from "zod";
import { nonNullish, unique, zSnowflake } from "../../../utils";
import { canAssignRole } from "../../../utils/canAssignRole";
import { getMissingPermissions } from "../../../utils/getMissingPermissions";
import { missingPermissionError } from "../../../utils/missingPermissionError";
@ -11,9 +11,10 @@ import { automodAction } from "../helpers";
const p = PermissionFlagsBits;
const configSchema = z.array(zSnowflake);
export const AddRolesAction = automodAction({
configType: t.array(t.string),
defaultConfig: [],
configSchema,
async apply({ pluginData, contexts, actionConfig, ruleName }) {
const members = unique(contexts.map((c) => c.member).filter(nonNullish));

View file

@ -1,15 +1,16 @@
import * as t from "io-ts";
import z from "zod";
import { zBoundedCharacters } from "../../../utils";
import { CountersPlugin } from "../../Counters/CountersPlugin";
import { LogsPlugin } from "../../Logs/LogsPlugin";
import { automodAction } from "../helpers";
export const AddToCounterAction = automodAction({
configType: t.type({
counter: t.string,
amount: t.number,
}),
const configSchema = z.object({
counter: zBoundedCharacters(0, 100),
amount: z.number(),
});
defaultConfig: {},
export const AddToCounterAction = automodAction({
configSchema,
async apply({ pluginData, contexts, actionConfig, ruleName }) {
const countersPlugin = pluginData.getPlugin(CountersPlugin);

View file

@ -1,6 +1,6 @@
import { Snowflake } from "discord.js";
import * as t from "io-ts";
import { erisAllowedMentionsToDjsMentionOptions } from "src/utils/erisAllowedMentionsToDjsMentionOptions";
import z from "zod";
import { LogType } from "../../../data/LogType";
import {
createTypedTemplateSafeValueContainer,
@ -12,10 +12,12 @@ import {
chunkMessageLines,
isTruthy,
messageLink,
tAllowedMentions,
tNormalizedNullOptional,
validateAndParseMessageContent,
verboseChannelMention,
zAllowedMentions,
zBoundedCharacters,
zNullishToUndefined,
zSnowflake
} from "../../../utils";
import { messageIsEmpty } from "../../../utils/messageIsEmpty";
import { userToTemplateSafeUser } from "../../../utils/templateSafeObjects";
@ -23,14 +25,14 @@ import { InternalPosterPlugin } from "../../InternalPoster/InternalPosterPlugin"
import { LogsPlugin } from "../../Logs/LogsPlugin";
import { automodAction } from "../helpers";
export const AlertAction = automodAction({
configType: t.type({
channel: t.string,
text: t.string,
allowed_mentions: tNormalizedNullOptional(tAllowedMentions),
}),
const configSchema = z.object({
channel: zSnowflake,
text: zBoundedCharacters(1, 4000),
allowed_mentions: zNullishToUndefined(zAllowedMentions.nullable().default(null)),
});
defaultConfig: {},
export const AlertAction = automodAction({
configSchema,
async apply({ pluginData, contexts, actionConfig, ruleName, matchResult }) {
const channel = pluginData.guild.channels.cache.get(actionConfig.channel as Snowflake);

View file

@ -1,11 +1,12 @@
import { AnyThreadChannel } from "discord.js";
import * as t from "io-ts";
import z from "zod";
import { noop } from "../../../utils";
import { automodAction } from "../helpers";
const configSchema = z.strictObject({});
export const ArchiveThreadAction = automodAction({
configType: t.type({}),
defaultConfig: {},
configSchema,
async apply({ pluginData, contexts }) {
const threads = contexts

View file

@ -1,4 +1,3 @@
import * as t from "io-ts";
import { AutomodActionBlueprint } from "../helpers";
import { AddRolesAction } from "./addRoles";
import { AddToCounterAction } from "./addToCounter";
@ -19,7 +18,7 @@ import { SetSlowmodeAction } from "./setSlowmode";
import { StartThreadAction } from "./startThread";
import { WarnAction } from "./warn";
export const availableActions: Record<string, AutomodActionBlueprint<any>> = {
export const availableActions = {
clean: CleanAction,
warn: WarnAction,
mute: MuteAction,
@ -38,25 +37,4 @@ export const availableActions: Record<string, AutomodActionBlueprint<any>> = {
start_thread: StartThreadAction,
archive_thread: ArchiveThreadAction,
change_perms: ChangePermsAction,
};
export const AvailableActions = t.type({
clean: CleanAction.configType,
warn: WarnAction.configType,
mute: MuteAction.configType,
kick: KickAction.configType,
ban: BanAction.configType,
alert: AlertAction.configType,
change_nickname: ChangeNicknameAction.configType,
log: LogAction.configType,
add_roles: AddRolesAction.configType,
remove_roles: RemoveRolesAction.configType,
set_antiraid_level: SetAntiraidLevelAction.configType,
reply: ReplyAction.configType,
add_to_counter: AddToCounterAction.configType,
set_counter: SetCounterAction.configType,
set_slowmode: SetSlowmodeAction.configType,
start_thread: StartThreadAction.configType,
archive_thread: ArchiveThreadAction.configType,
change_perms: ChangePermsAction.configType,
});
} satisfies Record<string, AutomodActionBlueprint<any>>;

View file

@ -1,25 +1,23 @@
import * as t from "io-ts";
import { convertDelayStringToMS, nonNullish, tDelayString, tNullable, unique } from "../../../utils";
import z from "zod";
import { convertDelayStringToMS, nonNullish, unique, zBoundedCharacters, zDelayString, zSnowflake } from "../../../utils";
import { CaseArgs } from "../../Cases/types";
import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin";
import { resolveActionContactMethods } from "../functions/resolveActionContactMethods";
import { automodAction } from "../helpers";
import { zNotify } from "../types";
const configSchema = z.strictObject({
reason: zBoundedCharacters(0, 4000).nullable().default(null),
duration: zDelayString.nullable().default(null),
notify: zNotify.nullable().default(null),
notifyChannel: zSnowflake.nullable().default(null),
deleteMessageDays: z.number().nullable().default(null),
postInCaseLog: z.boolean().nullable().default(null),
hide_case: z.boolean().nullable().default(false),
});
export const BanAction = automodAction({
configType: t.type({
reason: tNullable(t.string),
duration: tNullable(tDelayString),
notify: tNullable(t.string),
notifyChannel: tNullable(t.string),
deleteMessageDays: tNullable(t.number),
postInCaseLog: tNullable(t.boolean),
hide_case: tNullable(t.boolean),
}),
defaultConfig: {
notify: null, // Use defaults from ModActions
hide_case: false,
},
configSchema,
async apply({ pluginData, contexts, actionConfig, matchResult }) {
const reason = actionConfig.reason || "Kicked automatically";

View file

@ -1,18 +1,16 @@
import * as t from "io-ts";
import { nonNullish, unique } from "../../../utils";
import z from "zod";
import { nonNullish, unique, zBoundedCharacters } from "../../../utils";
import { LogsPlugin } from "../../Logs/LogsPlugin";
import { automodAction } from "../helpers";
export const ChangeNicknameAction = automodAction({
configType: t.union([
t.string,
t.type({
name: t.string,
configSchema: z.union([
zBoundedCharacters(0, 32),
z.strictObject({
name: zBoundedCharacters(0, 32),
}),
]),
defaultConfig: {},
async apply({ pluginData, contexts, actionConfig }) {
const members = unique(contexts.map((c) => c.member).filter(nonNullish));

View file

@ -1,7 +1,8 @@
import { PermissionsBitField, PermissionsString } from "discord.js";
import * as t from "io-ts";
import { U } from "ts-toolbelt";
import z from "zod";
import { TemplateSafeValueContainer, renderTemplate } from "../../../templateFormatter";
import { isValidSnowflake, noop, tNullable, tPartialDictionary } from "../../../utils";
import { isValidSnowflake, keys, noop, zSnowflake } from "../../../utils";
import {
guildToTemplateSafeGuild,
savedMessageToTemplateSafeSavedMessage,
@ -59,16 +60,19 @@ const realToLegacyMap = Object.entries(legacyPermMap).reduce((map, pair) => {
return map;
}, {}) as Record<keyof typeof PermissionsBitField.Flags, keyof typeof legacyPermMap>;
const permissionNames = keys(PermissionsBitField.Flags) as U.ListOf<keyof typeof PermissionsBitField.Flags>;
const legacyPermissionNames = keys(legacyPermMap) as U.ListOf<keyof typeof legacyPermMap>;
const allPermissionNames = [...permissionNames, ...legacyPermissionNames] as const;
export const ChangePermsAction = automodAction({
configType: t.type({
target: t.string,
channel: tNullable(t.string),
perms: tPartialDictionary(
t.union([t.keyof(PermissionsBitField.Flags), t.keyof(legacyPermMap)]),
tNullable(t.boolean),
configSchema: z.strictObject({
target: zSnowflake,
channel: zSnowflake.nullable().default(null),
perms: z.record(
z.enum(allPermissionNames),
z.boolean().nullable(),
),
}),
defaultConfig: {},
async apply({ pluginData, contexts, actionConfig }) {
const user = contexts.find((c) => c.user)?.user;

View file

@ -1,12 +1,11 @@
import { GuildTextBasedChannel, Snowflake } from "discord.js";
import * as t from "io-ts";
import z from "zod";
import { LogType } from "../../../data/LogType";
import { noop } from "../../../utils";
import { automodAction } from "../helpers";
export const CleanAction = automodAction({
configType: t.boolean,
defaultConfig: false,
configSchema: z.boolean().default(false),
async apply({ pluginData, contexts, ruleName }) {
const messageIdsToDeleteByChannelId: Map<string, string[]> = new Map();

View file

@ -1,13 +1,12 @@
import * as t from "io-ts";
import z from "zod";
import { zBoundedCharacters } from "../../../utils";
import { automodAction } from "../helpers";
export const ExampleAction = automodAction({
configType: t.type({
someValue: t.string,
configSchema: z.strictObject({
someValue: zBoundedCharacters(0, 1000),
}),
defaultConfig: {},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async apply({ pluginData, contexts, actionConfig }) {
// TODO: Everything

View file

@ -1,24 +1,20 @@
import * as t from "io-ts";
import { asyncMap, nonNullish, resolveMember, tNullable, unique } from "../../../utils";
import z from "zod";
import { asyncMap, nonNullish, resolveMember, unique, zBoundedCharacters, zSnowflake } from "../../../utils";
import { CaseArgs } from "../../Cases/types";
import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin";
import { resolveActionContactMethods } from "../functions/resolveActionContactMethods";
import { automodAction } from "../helpers";
import { zNotify } from "../types";
export const KickAction = automodAction({
configType: t.type({
reason: tNullable(t.string),
notify: tNullable(t.string),
notifyChannel: tNullable(t.string),
postInCaseLog: tNullable(t.boolean),
hide_case: tNullable(t.boolean),
configSchema: z.strictObject({
reason: zBoundedCharacters(0, 4000).nullable().default(null),
notify: zNotify.nullable().default(null),
notifyChannel: zSnowflake.nullable().default(null),
postInCaseLog: z.boolean().nullable().default(null),
hide_case: z.boolean().nullable().default(false),
}),
defaultConfig: {
notify: null, // Use defaults from ModActions
hide_case: false,
},
async apply({ pluginData, contexts, actionConfig, matchResult }) {
const reason = actionConfig.reason || "Kicked automatically";
const contactMethods = actionConfig.notify ? resolveActionContactMethods(pluginData, actionConfig) : undefined;

View file

@ -1,11 +1,10 @@
import * as t from "io-ts";
import z from "zod";
import { isTruthy, unique } from "../../../utils";
import { LogsPlugin } from "../../Logs/LogsPlugin";
import { automodAction } from "../helpers";
export const LogAction = automodAction({
configType: t.boolean,
defaultConfig: true,
configSchema: z.boolean().default(true),
async apply({ pluginData, contexts, ruleName, matchResult }) {
const users = unique(contexts.map((c) => c.user)).filter(isTruthy);

View file

@ -1,29 +1,25 @@
import * as t from "io-ts";
import z from "zod";
import { ERRORS, RecoverablePluginError } from "../../../RecoverablePluginError";
import { convertDelayStringToMS, nonNullish, tDelayString, tNullable, unique } from "../../../utils";
import { convertDelayStringToMS, nonNullish, unique, zBoundedCharacters, zDelayString, zSnowflake } from "../../../utils";
import { CaseArgs } from "../../Cases/types";
import { LogsPlugin } from "../../Logs/LogsPlugin";
import { MutesPlugin } from "../../Mutes/MutesPlugin";
import { resolveActionContactMethods } from "../functions/resolveActionContactMethods";
import { automodAction } from "../helpers";
import { zNotify } from "../types";
export const MuteAction = automodAction({
configType: t.type({
reason: tNullable(t.string),
duration: tNullable(tDelayString),
notify: tNullable(t.string),
notifyChannel: tNullable(t.string),
remove_roles_on_mute: tNullable(t.union([t.boolean, t.array(t.string)])),
restore_roles_on_mute: tNullable(t.union([t.boolean, t.array(t.string)])),
postInCaseLog: tNullable(t.boolean),
hide_case: tNullable(t.boolean),
configSchema: z.strictObject({
reason: zBoundedCharacters(0, 4000).nullable().default(null),
duration: zDelayString.nullable().default(null),
notify: zNotify.nullable().default(null),
notifyChannel: zSnowflake.nullable().default(null),
remove_roles_on_mute: z.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),
hide_case: z.boolean().nullable().default(false),
}),
defaultConfig: {
notify: null, // Use defaults from ModActions
hide_case: false,
},
async apply({ pluginData, contexts, actionConfig, ruleName, matchResult }) {
const duration = actionConfig.duration ? convertDelayStringToMS(actionConfig.duration)! : undefined;
const reason = actionConfig.reason || "Muted automatically";

View file

@ -1,6 +1,6 @@
import { PermissionFlagsBits, Snowflake } from "discord.js";
import * as t from "io-ts";
import { nonNullish, unique } from "../../../utils";
import z from "zod";
import { nonNullish, unique, zSnowflake } from "../../../utils";
import { canAssignRole } from "../../../utils/canAssignRole";
import { getMissingPermissions } from "../../../utils/getMissingPermissions";
import { memberRolesLock } from "../../../utils/lockNameHelpers";
@ -12,9 +12,7 @@ import { automodAction } from "../helpers";
const p = PermissionFlagsBits;
export const RemoveRolesAction = automodAction({
configType: t.array(t.string),
defaultConfig: [],
configSchema: z.array(zSnowflake).default([]),
async apply({ pluginData, contexts, actionConfig, ruleName }) {
const members = unique(contexts.map((c) => c.member).filter(nonNullish));

View file

@ -1,16 +1,16 @@
import { GuildTextBasedChannel, MessageCreateOptions, PermissionsBitField, Snowflake, User } from "discord.js";
import * as t from "io-ts";
import z from "zod";
import { TemplateSafeValueContainer, renderTemplate } from "../../../templateFormatter";
import {
convertDelayStringToMS,
noop,
renderRecursively,
tDelayString,
tMessageContent,
tNullable,
unique,
validateAndParseMessageContent,
verboseChannelMention,
zBoundedCharacters,
zDelayString,
zMessageContent
} from "../../../utils";
import { hasDiscordPermissions } from "../../../utils/hasDiscordPermissions";
import { messageIsEmpty } from "../../../utils/messageIsEmpty";
@ -20,17 +20,15 @@ import { automodAction } from "../helpers";
import { AutomodContext } from "../types";
export const ReplyAction = automodAction({
configType: t.union([
t.string,
t.type({
text: tMessageContent,
auto_delete: tNullable(t.union([tDelayString, t.number])),
inline: tNullable(t.boolean),
configSchema: z.union([
zBoundedCharacters(0, 4000),
z.strictObject({
text: zMessageContent,
auto_delete: z.union([zDelayString, z.number()]).nullable().default(null),
inline: z.boolean().default(false),
}),
]),
defaultConfig: {},
async apply({ pluginData, contexts, actionConfig, ruleName }) {
const contextsWithTextChannels = contexts
.filter((c) => c.message?.channel_id)

View file

@ -1,11 +1,9 @@
import * as t from "io-ts";
import { tNullable } from "../../../utils";
import { zBoundedCharacters } from "../../../utils";
import { setAntiraidLevel } from "../functions/setAntiraidLevel";
import { automodAction } from "../helpers";
export const SetAntiraidLevelAction = automodAction({
configType: tNullable(t.string),
defaultConfig: "",
configSchema: zBoundedCharacters(0, 100).nullable(),
async apply({ pluginData, actionConfig }) {
setAntiraidLevel(pluginData, actionConfig ?? null);

View file

@ -1,16 +1,15 @@
import * as t from "io-ts";
import z from "zod";
import { zBoundedCharacters } from "../../../utils";
import { CountersPlugin } from "../../Counters/CountersPlugin";
import { LogsPlugin } from "../../Logs/LogsPlugin";
import { automodAction } from "../helpers";
export const SetCounterAction = automodAction({
configType: t.type({
counter: t.string,
value: t.number,
configSchema: z.strictObject({
counter: zBoundedCharacters(0, 100),
value: z.number(),
}),
defaultConfig: {},
async apply({ pluginData, contexts, actionConfig, ruleName }) {
const countersPlugin = pluginData.getPlugin(CountersPlugin);
if (!countersPlugin.counterExists(actionConfig.counter)) {

View file

@ -1,19 +1,15 @@
import { ChannelType, GuildTextBasedChannel, Snowflake } from "discord.js";
import * as t from "io-ts";
import { convertDelayStringToMS, isDiscordAPIError, tDelayString, tNullable } from "../../../utils";
import z from "zod";
import { convertDelayStringToMS, isDiscordAPIError, zDelayString, zSnowflake } from "../../../utils";
import { LogsPlugin } from "../../Logs/LogsPlugin";
import { automodAction } from "../helpers";
export const SetSlowmodeAction = automodAction({
configType: t.type({
channels: t.array(t.string),
duration: tNullable(tDelayString),
configSchema: z.strictObject({
channels: z.array(zSnowflake),
duration: zDelayString.nullable().default("10s"),
}),
defaultConfig: {
duration: "10s",
},
async apply({ pluginData, actionConfig }) {
const slowmodeMs = Math.max(actionConfig.duration ? convertDelayStringToMS(actionConfig.duration)! : 0, 0);

View file

@ -5,9 +5,9 @@ import {
ThreadAutoArchiveDuration,
ThreadChannel,
} from "discord.js";
import * as t from "io-ts";
import z from "zod";
import { TemplateSafeValueContainer, renderTemplate } from "../../../templateFormatter";
import { MINUTES, convertDelayStringToMS, noop, tDelayString, tNullable } from "../../../utils";
import { MINUTES, convertDelayStringToMS, noop, zBoundedCharacters, zDelayString } from "../../../utils";
import { savedMessageToTemplateSafeSavedMessage, userToTemplateSafeUser } from "../../../utils/templateSafeObjects";
import { automodAction } from "../helpers";
@ -19,18 +19,14 @@ const validThreadAutoArchiveDurations: ThreadAutoArchiveDuration[] = [
];
export const StartThreadAction = automodAction({
configType: t.type({
name: tNullable(t.string),
auto_archive: tDelayString,
private: tNullable(t.boolean),
slowmode: tNullable(tDelayString),
limit_per_channel: tNullable(t.number),
configSchema: z.strictObject({
name: zBoundedCharacters(1, 100).nullable(),
auto_archive: zDelayString,
private: z.boolean().default(false),
slowmode: zDelayString.nullable().default(null),
limit_per_channel: z.number().nullable().default(5),
}),
defaultConfig: {
limit_per_channel: 5,
},
async apply({ pluginData, contexts, actionConfig }) {
// check if the message still exists, we don't want to create threads for deleted messages
const threads = contexts.filter((c) => {

View file

@ -1,24 +1,20 @@
import * as t from "io-ts";
import { asyncMap, nonNullish, resolveMember, tNullable, unique } from "../../../utils";
import z from "zod";
import { asyncMap, nonNullish, resolveMember, unique, zBoundedCharacters, zSnowflake } from "../../../utils";
import { CaseArgs } from "../../Cases/types";
import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin";
import { resolveActionContactMethods } from "../functions/resolveActionContactMethods";
import { automodAction } from "../helpers";
import { zNotify } from "../types";
export const WarnAction = automodAction({
configType: t.type({
reason: tNullable(t.string),
notify: tNullable(t.string),
notifyChannel: tNullable(t.string),
postInCaseLog: tNullable(t.boolean),
hide_case: tNullable(t.boolean),
configSchema: z.strictObject({
reason: zBoundedCharacters(0, 4000).nullable().default(null),
notify: zNotify.nullable().default(null),
notifyChannel: zSnowflake.nullable().default(null),
postInCaseLog: z.boolean().nullable().default(null),
hide_case: z.boolean().nullable().default(false),
}),
defaultConfig: {
notify: null, // Use defaults from ModActions
hide_case: false,
},
async apply({ pluginData, contexts, actionConfig, matchResult }) {
const reason = actionConfig.reason || "Warned automatically";
const contactMethods = actionConfig.notify ? resolveActionContactMethods(pluginData, actionConfig) : undefined;