3
0
Fork 0
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:
Dragory 2025-05-22 22:35:48 +00:00
parent 595e1a0556
commit 09eb8e92f2
No known key found for this signature in database
189 changed files with 1244 additions and 900 deletions

View file

@ -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

View file

@ -1,4 +1,4 @@
import { ZodIssue } from "zod";
import { ZodIssue } from "zod/v4";
export function formatZodIssue(issue: ZodIssue): string {
const path = issue.path.join("/");

View file

@ -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>;

View file

@ -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",
});
}

View file

@ -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 });

View file

@ -1,4 +1,4 @@
import z from "zod";
import z from "zod/v4";
import { parseColor } from "./parseColor.js";
import { rgbToInt } from "./rgbToInt.js";

View file

@ -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) {

View 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;
}