mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-06-08 00:05:01 +00:00
feat: update to djs 14.19.3, node 22, zod 4
This commit is contained in:
parent
595e1a0556
commit
09eb8e92f2
189 changed files with 1244 additions and 900 deletions
|
@ -1,20 +1,17 @@
|
|||
import {
|
||||
ChatInputCommandInteraction,
|
||||
Client,
|
||||
Message,
|
||||
MessageCreateOptions,
|
||||
MessageEditOptions,
|
||||
MessageReaction,
|
||||
PartialMessageReaction,
|
||||
PartialUser,
|
||||
User,
|
||||
User
|
||||
} from "discord.js";
|
||||
import { sendContextResponse } from "../pluginUtils.js";
|
||||
import { ContextResponseOptions, fetchContextChannel, GenericCommandSource } from "../pluginUtils.js";
|
||||
import { MINUTES, noop } from "../utils.js";
|
||||
import { Awaitable } from "./typeUtils.js";
|
||||
import Timeout = NodeJS.Timeout;
|
||||
|
||||
export type LoadPageFn = (page: number) => Awaitable<MessageCreateOptions & MessageEditOptions>;
|
||||
export type LoadPageFn = (page: number) => Awaitable<ContextResponseOptions>;
|
||||
|
||||
export interface PaginateMessageOpts {
|
||||
timeout: number;
|
||||
|
@ -28,14 +25,19 @@ const defaultOpts: PaginateMessageOpts = {
|
|||
|
||||
export async function createPaginatedMessage(
|
||||
client: Client,
|
||||
context: Message | User | ChatInputCommandInteraction,
|
||||
context: GenericCommandSource,
|
||||
totalPages: number,
|
||||
loadPageFn: LoadPageFn,
|
||||
opts: Partial<PaginateMessageOpts> = {},
|
||||
): Promise<Message> {
|
||||
const fullOpts = { ...defaultOpts, ...opts } as PaginateMessageOpts;
|
||||
const channel = await fetchContextChannel(context);
|
||||
if (!channel.isSendable()) {
|
||||
throw new Error("Context channel is not sendable");
|
||||
}
|
||||
|
||||
const firstPageContent = await loadPageFn(1);
|
||||
const message = await sendContextResponse(context, firstPageContent);
|
||||
const message = await channel.send(firstPageContent);
|
||||
|
||||
let page = 1;
|
||||
let pageLoadId = 0; // Used to avoid race conditions when rapidly switching pages
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ZodIssue } from "zod";
|
||||
import { ZodIssue } from "zod/v4";
|
||||
|
||||
export function formatZodIssue(issue: ZodIssue): string {
|
||||
const path = issue.path.join("/");
|
||||
|
|
|
@ -48,4 +48,8 @@ export const PERMISSION_NAMES = {
|
|||
UseExternalSounds: "Use External Sounds",
|
||||
UseSoundboard: "Use Soundboard",
|
||||
ViewCreatorMonetizationAnalytics: "View Creator Monetization Analytics",
|
||||
CreateGuildExpressions: "Create Guild Expressions",
|
||||
CreateEvents: "Create Events",
|
||||
SendPolls: "Send Polls",
|
||||
UseExternalApps: "Use External Apps",
|
||||
} as const satisfies Record<keyof typeof PermissionFlagsBits, string>;
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
GuildMember,
|
||||
Message,
|
||||
PartialGuildMember,
|
||||
PartialUser,
|
||||
Role,
|
||||
Snowflake,
|
||||
StageInstance,
|
||||
|
@ -242,15 +243,15 @@ export function guildToTemplateSafeGuild(guild: Guild): TemplateSafeGuild {
|
|||
});
|
||||
}
|
||||
|
||||
export function userToTemplateSafeUser(user: User | UnknownUser): TemplateSafeUser {
|
||||
if (user instanceof UnknownUser) {
|
||||
export function userToTemplateSafeUser(user: User | UnknownUser | PartialUser): TemplateSafeUser {
|
||||
if (user instanceof UnknownUser || user.partial) {
|
||||
return new TemplateSafeUser({
|
||||
id: user.id,
|
||||
username: "Unknown",
|
||||
discriminator: "0000",
|
||||
mention: `<@${user.id}>`,
|
||||
tag: "Unknown#0000",
|
||||
renderedUsername: renderUsername(user),
|
||||
renderedUsername: "Unknown",
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -2,20 +2,17 @@ import {
|
|||
ActionRowBuilder,
|
||||
ButtonBuilder,
|
||||
ButtonStyle,
|
||||
ChatInputCommandInteraction,
|
||||
Message,
|
||||
MessageActionRowComponentBuilder,
|
||||
MessageComponentInteraction,
|
||||
MessageCreateOptions,
|
||||
User,
|
||||
MessageCreateOptions
|
||||
} from "discord.js";
|
||||
import moment from "moment";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { isContextInteraction } from "../pluginUtils.js";
|
||||
import { GenericCommandSource, isContextInteraction, sendContextResponse } from "../pluginUtils.js";
|
||||
import { noop } from "../utils.js";
|
||||
|
||||
export async function waitForButtonConfirm(
|
||||
context: Message | User | ChatInputCommandInteraction,
|
||||
context: GenericCommandSource,
|
||||
toPost: Omit<MessageCreateOptions, "flags">,
|
||||
options?: WaitForOptions,
|
||||
): Promise<boolean> {
|
||||
|
@ -33,15 +30,7 @@ export async function waitForButtonConfirm(
|
|||
.setLabel(options?.cancelText || "Cancel")
|
||||
.setCustomId(`cancelButton:${idMod}:${uuidv4()}`),
|
||||
]);
|
||||
const sendMethod = () => {
|
||||
if (contextIsInteraction) {
|
||||
return context.replied ? context.editReply.bind(context) : context.reply.bind(context);
|
||||
} else {
|
||||
return "send" in context ? context.send.bind(context) : context.channel.send.bind(context.channel);
|
||||
}
|
||||
};
|
||||
const extraParameters = contextIsInteraction ? { fetchReply: true, ephemeral: true } : {};
|
||||
const message = (await sendMethod()({ ...toPost, components: [row], ...extraParameters })) as Message;
|
||||
const message = await sendContextResponse(context, { ...toPost, components: [row] }, true);
|
||||
|
||||
const collector = message.createMessageComponentCollector({ time: 10000 });
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import z from "zod";
|
||||
import z from "zod/v4";
|
||||
import { parseColor } from "./parseColor.js";
|
||||
import { rgbToInt } from "./rgbToInt.js";
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ZodString } from "zod";
|
||||
import { ZodString } from "zod/v4";
|
||||
import { isValidTimezone } from "./isValidTimezone.js";
|
||||
|
||||
export function zValidTimezone<Z extends ZodString>(z: Z) {
|
||||
|
|
165
backend/src/utils/zodDeepPartial.ts
Normal file
165
backend/src/utils/zodDeepPartial.ts
Normal file
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
Modified version of https://gist.github.com/jaens/7e15ae1984bb338c86eb5e452dee3010
|
||||
Original version's license:
|
||||
|
||||
Copyright 2024, Jaen - https://github.com/jaens
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { z } from "zod/v4";
|
||||
import { $ZodRecordKey, $ZodType } from "zod/v4/core";
|
||||
|
||||
const RESOLVING = Symbol("mapOnSchema/resolving");
|
||||
|
||||
export function mapOnSchema<T extends $ZodType, TResult extends $ZodType>(
|
||||
schema: T,
|
||||
fn: (schema: $ZodType) => TResult,
|
||||
): TResult;
|
||||
|
||||
/**
|
||||
* Applies {@link fn} to each element of the schema recursively, replacing every schema with its return value.
|
||||
* The rewriting is applied bottom-up (ie. {@link fn} will get called on "children" first).
|
||||
*/
|
||||
export function mapOnSchema(schema: $ZodType, fn: (schema: $ZodType) => $ZodType): $ZodType {
|
||||
// Cache results to support recursive schemas
|
||||
const results = new Map<$ZodType, $ZodType | typeof RESOLVING>();
|
||||
|
||||
function mapElement(s: $ZodType) {
|
||||
const value = results.get(s);
|
||||
if (value === RESOLVING) {
|
||||
throw new Error("Recursive schema access detected");
|
||||
} else if (value !== undefined) {
|
||||
return value;
|
||||
}
|
||||
|
||||
results.set(s, RESOLVING);
|
||||
const result = mapOnSchema(s, fn);
|
||||
results.set(s, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
function mapInner() {
|
||||
if (schema instanceof z.ZodObject) {
|
||||
const newShape: Record<string, $ZodType> = {};
|
||||
for (const [key, value] of Object.entries(schema.shape)) {
|
||||
newShape[key] = mapElement(value);
|
||||
}
|
||||
|
||||
return new z.ZodObject({
|
||||
...schema.def,
|
||||
shape: newShape,
|
||||
});
|
||||
} else if (schema instanceof z.ZodArray) {
|
||||
return new z.ZodArray({
|
||||
...schema.def,
|
||||
type: "array",
|
||||
element: mapElement(schema.def.element),
|
||||
});
|
||||
} else if (schema instanceof z.ZodMap) {
|
||||
return new z.ZodMap({
|
||||
...schema.def,
|
||||
keyType: mapElement(schema.def.keyType),
|
||||
valueType: mapElement(schema.def.valueType),
|
||||
});
|
||||
} else if (schema instanceof z.ZodSet) {
|
||||
return new z.ZodSet({
|
||||
...schema.def,
|
||||
valueType: mapElement(schema.def.valueType),
|
||||
});
|
||||
} else if (schema instanceof z.ZodOptional) {
|
||||
return new z.ZodOptional({
|
||||
...schema.def,
|
||||
innerType: mapElement(schema.def.innerType),
|
||||
});
|
||||
} else if (schema instanceof z.ZodNullable) {
|
||||
return new z.ZodNullable({
|
||||
...schema.def,
|
||||
innerType: mapElement(schema.def.innerType),
|
||||
});
|
||||
} else if (schema instanceof z.ZodDefault) {
|
||||
return new z.ZodDefault({
|
||||
...schema.def,
|
||||
innerType: mapElement(schema.def.innerType),
|
||||
});
|
||||
} else if (schema instanceof z.ZodReadonly) {
|
||||
return new z.ZodReadonly({
|
||||
...schema.def,
|
||||
innerType: mapElement(schema.def.innerType),
|
||||
});
|
||||
} else if (schema instanceof z.ZodLazy) {
|
||||
return new z.ZodLazy({
|
||||
...schema.def,
|
||||
// NB: This leaks `fn` into the schema, but there is no other way to support recursive schemas
|
||||
getter: () => mapElement(schema._def.getter()),
|
||||
});
|
||||
} else if (schema instanceof z.ZodPromise) {
|
||||
return new z.ZodPromise({
|
||||
...schema.def,
|
||||
innerType: mapElement(schema.def.innerType),
|
||||
});
|
||||
} else if (schema instanceof z.ZodCatch) {
|
||||
return new z.ZodCatch({
|
||||
...schema.def,
|
||||
innerType: mapElement(schema._def.innerType),
|
||||
});
|
||||
} else if (schema instanceof z.ZodTuple) {
|
||||
return new z.ZodTuple({
|
||||
...schema.def,
|
||||
items: schema.def.items.map((item: $ZodType) => mapElement(item)),
|
||||
rest: schema.def.rest && mapElement(schema.def.rest),
|
||||
});
|
||||
} else if (schema instanceof z.ZodDiscriminatedUnion) {
|
||||
return new z.ZodDiscriminatedUnion({
|
||||
...schema.def,
|
||||
options: schema.options.map((option) => mapOnSchema(option, fn)),
|
||||
});
|
||||
} else if (schema instanceof z.ZodUnion) {
|
||||
return new z.ZodUnion({
|
||||
...schema.def,
|
||||
options: schema.options.map((option) => mapOnSchema(option, fn)),
|
||||
});
|
||||
} else if (schema instanceof z.ZodIntersection) {
|
||||
return new z.ZodIntersection({
|
||||
...schema.def,
|
||||
right: mapElement(schema.def.right),
|
||||
left: mapElement(schema.def.left),
|
||||
});
|
||||
} else if (schema instanceof z.ZodRecord) {
|
||||
return new z.ZodRecord({
|
||||
...schema.def,
|
||||
keyType: mapElement(schema.def.keyType) as $ZodRecordKey,
|
||||
valueType: mapElement(schema.def.valueType),
|
||||
});
|
||||
} else {
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
return fn(mapInner());
|
||||
}
|
||||
|
||||
export function deepPartial<T extends z.ZodType>(schema: T): T {
|
||||
return mapOnSchema(schema, (s) => (s instanceof z.ZodObject ? s.partial() : s)) as T;
|
||||
}
|
||||
|
||||
/** Make all object schemas "strict" (ie. fail on unknown keys), except if they are marked as `.passthrough()` */
|
||||
export function deepStrict<T extends z.ZodType>(schema: T): T {
|
||||
return mapOnSchema(schema, (s) =>
|
||||
s instanceof z.ZodObject /* && s.def.unknownKeys !== "passthrough" */ ? s.strict() : s,
|
||||
) as T;
|
||||
}
|
||||
|
||||
export function deepStrictAll<T extends z.ZodType>(schema: T): T {
|
||||
return mapOnSchema(schema, (s) => (s instanceof z.ZodObject ? s.strict() : s)) as T;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue