fixes for pluralkit

This commit is contained in:
MathMan05
2026-03-18 05:13:51 +01:00
committed by Rory&
parent 8149f90345
commit dc66bcf247
6 changed files with 122 additions and 24 deletions
+18 -13
View File
@@ -67,6 +67,7 @@ import {
Reaction,
ReadStateType,
UnfurledMediaItem,
BaseMessageComponents,
} from "@spacebar/schemas";
const allow_empty = false;
// TODO: check webhook, application, system author, stickers
@@ -201,20 +202,19 @@ async function processMedia(media: UnfurledMediaItem, messageId: string, batchId
});
}
}
export async function handleMessage(opts: MessageOptions): Promise<Message> {
export function handleComps(components: BaseMessageComponents[], flags: number) {
const errors: Record<string, { code?: string; message: string }> = {};
const knownComponentIds: string[] = [];
const compv2 = (opts.flags || 0) & Number(MessageFlags.FLAGS.IS_COMPONENTS_V2);
const compv2 = (flags || 0) & Number(MessageFlags.FLAGS.IS_COMPONENTS_V2);
const medias: UnfurledMediaItem[] = [];
for (const comp of opts.components || []) {
for (const comp of components || []) {
if (comp.type === MessageComponentType.ActionRow) {
checkActionRow(comp, knownComponentIds, errors, opts.components!.indexOf(comp));
checkActionRow(comp, knownComponentIds, errors, components!.indexOf(comp));
} else if (comp.type === MessageComponentType.Section) {
if (!compv2) throw new HTTPError("Must be comp v2");
const accessory = comp.accessory;
if (comp.components.length < 1 || comp.components.length > 3) {
errors[`data.components[${opts.components!.indexOf(comp)}].components`] = {
errors[`data.components[${components!.indexOf(comp)}].components`] = {
code: "TOO_LONG",
message: "Component list is too long",
};
@@ -227,7 +227,7 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
} else if (comp.type === MessageComponentType.MediaGallery) {
if (!compv2) throw new HTTPError("Must be comp v2");
if (comp.items.length < 1 || comp.items.length > 10) {
errors[`data.components[${opts.components!.indexOf(comp)}].items`] = {
errors[`data.components[${components!.indexOf(comp)}].items`] = {
code: "TOO_LONG",
message: "Media list is too long",
};
@@ -248,7 +248,7 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
case MessageComponentType.Section: {
const accessory = elm.accessory;
if (elm.components.length < 1 || elm.components.length > 3) {
errors[`data.components[${opts.components!.indexOf(comp)}].components[${comp.components!.indexOf(elm)}].components`] = {
errors[`data.components[${components!.indexOf(comp)}].components[${comp.components!.indexOf(elm)}].components`] = {
code: "TOO_LONG",
message: "Component list is too long",
};
@@ -260,7 +260,7 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
}
case MessageComponentType.MediaGallery:
if (elm.items.length < 1 || elm.items.length > 10) {
errors[`data.components[${opts.components!.indexOf(comp)}].components[${comp.components!.indexOf(elm)}].items`] = {
errors[`data.components[${components!.indexOf(comp)}].components[${comp.components!.indexOf(elm)}].items`] = {
code: "TOO_LONG",
message: "Media list is too long",
};
@@ -272,7 +272,7 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
break;
}
case MessageComponentType.ActionRow:
checkActionRow(elm, knownComponentIds, errors, opts.components!.indexOf(elm));
checkActionRow(elm, knownComponentIds, errors, components!.indexOf(elm));
break;
default:
elm satisfies never;
@@ -286,6 +286,13 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
if (Object.keys(errors).length > 0) {
throw FieldErrors(errors);
}
return async (messageId: string, user: User, channel: Channel) => {
const batchId = `CLOUD_compUploads_${randomString(128)}`;
(await Promise.all(medias.map((m, index) => processMedia(m, messageId, batchId, user, channel, index + "")))).forEach((_) => _?.());
};
}
export async function handleMessage(opts: MessageOptions): Promise<Message> {
const handle = opts.components ? handleComps(opts.components, opts.flags || 0) : undefined;
const channel = await Channel.findOneOrFail({
where: { id: opts.channel_id },
@@ -335,7 +342,6 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
components: opts.components ?? undefined, // Fix Discord-Go?
});
message.channel = channel;
const batchId = `CLOUD_${message.author_id}_${randomString(128)}`;
if (opts.author_id) {
message.author = await User.findOneOrFail({
@@ -345,8 +351,6 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
rights.hasThrow("SEND_MESSAGES");
}
(await Promise.all(medias.map((m, index) => processMedia(m, message.id, batchId, message.author as User, channel, index + "")))).forEach((_) => _?.());
const ephermal = (message.flags & (1 << 6)) !== 0;
if (!ephermal && channel.type === ChannelType.GUILD_PUBLIC_THREAD) {
const rep = Channel.getRepository();
@@ -360,6 +364,7 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
if (cloudAttachments && cloudAttachments.length > 0) {
console.log("[Message] Processing attachments for message", message.id, ":", message.attachments);
handle?.(message.id, message.author as User, message.channel);
const uploadedAttachments = await Promise.all(
cloudAttachments.map(async (att) => {
const cAtt = att.attachment;
+31 -3
View File
@@ -41,7 +41,7 @@ export type RouteResponse = {
body?: `${string}Response`;
headers?: Record<string, string>;
};
export type stripNulls = { [key: string]: true | stripNulls };
export interface RouteOptions {
permission?: PermissionResolvable;
right?: RightResolvable;
@@ -52,6 +52,7 @@ export interface RouteOptions {
body?: string;
};
};
stripNulls?: stripNulls;
event?: EVENT | EVENT[];
summary?: string;
description?: string;
@@ -73,7 +74,31 @@ export interface RouteOptions {
// headers?: Record<string, string>;
// };
}
export function stripNull(obj: object) {
console.log(Object.entries(obj));
for (const [key, value] of Object.entries(obj)) {
if (value instanceof Object || (value && !value.__proto__)) {
stripNull(value);
} else if (value === null) {
//@ts-expect-error this is fine
delete obj[key];
}
}
}
// eslint-disable-next-line
export function followNullPath(obj1: any, nullObj: stripNulls) {
for (const [key, value] of Object.entries(nullObj)) {
if (key in obj1)
if (value instanceof Object) {
if (obj1[key] instanceof Object)
//@ts-expect-error this works lol
followNullPath(obj1[key], nullObj[key]);
else delete obj1[key];
} else if (obj1[key] instanceof Object) {
stripNull(obj1[key]);
}
}
}
export function route(opts: RouteOptions) {
let validate: AnyValidateFunction | undefined;
if (opts.requestBody) {
@@ -111,9 +136,12 @@ export function route(opts: RouteOptions) {
}
if (validate && !ignoredRequestSchemas.includes(opts.requestBody!)) {
if (opts.stripNulls) {
followNullPath(req.body, opts.stripNulls);
}
const valid = validate(req.body);
if (!valid) {
console.log(req.body);
console.log(JSON.stringify(req.body));
const fields: Record<string, { code?: string; message: string }> = {};
validate.errors?.forEach(
(x) =>