From ccbe60ffb6d7f353205242e3e9ed2d42323eb952 Mon Sep 17 00:00:00 2001 From: MathMan05 Date: Thu, 5 Mar 2026 11:29:17 -0600 Subject: [PATCH] make some corrections --- src/api/util/handlers/Message.ts | 39 +++++++++++-------- src/schemas/api/messages/Components.ts | 8 ++-- src/util/config/Config.ts | 2 + .../config/types/ComponentConfiguration.ts | 4 ++ src/util/config/types/index.ts | 1 + 5 files changed, 35 insertions(+), 19 deletions(-) create mode 100644 src/util/config/types/ComponentConfiguration.ts diff --git a/src/api/util/handlers/Message.ts b/src/api/util/handlers/Message.ts index 5aa780ffe..391abc255 100644 --- a/src/api/util/handlers/Message.ts +++ b/src/api/util/handlers/Message.ts @@ -68,6 +68,7 @@ import { ReadStateType, UnfurledMediaItem, BaseMessageComponents, + v1CompTypes, } from "@spacebar/schemas"; const allow_empty = false; // TODO: check webhook, application, system author, stickers @@ -202,17 +203,24 @@ async function processMedia(media: UnfurledMediaItem, messageId: string, batchId } } export function handleComps(components: BaseMessageComponents[], flags: number) { + const conf = Config.get(); + const mediaGalleryLimit = conf.components.mediaGalleryLimit ?? 10; + const actionRowLimit = conf.components.actionRowLimit ?? 5; + const errors: Record = {}; const knownComponentIds: string[] = []; const compv2 = (flags || 0) & Number(MessageFlags.FLAGS.IS_COMPONENTS_V2); + if (!compv2) { + const bad = components.reduce((bad, comp) => bad || !v1CompTypes.has(comp.type), false); + if (bad) throw new HTTPError("Must be comp v2"); + } const medias: UnfurledMediaItem[] = []; for (const comp of components || []) { if (comp.type === MessageComponentType.ActionRow) { 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) { + if (comp.components.length < 1 || comp.components.length > actionRowLimit) { errors[`data.components[${components!.indexOf(comp)}].components`] = { code: "TOO_LONG", message: "Component list is too long", @@ -222,10 +230,9 @@ export function handleComps(components: BaseMessageComponents[], flags: number) medias.push(accessory.media); } } else if (comp.type === MessageComponentType.TextDisplay) { - if (!compv2) throw new HTTPError("Must be comp v2"); + //Here to make sure everything is checked } else if (comp.type === MessageComponentType.MediaGallery) { - if (!compv2) throw new HTTPError("Must be comp v2"); - if (comp.items.length < 1 || comp.items.length > 10) { + if (comp.items.length < 1 || comp.items.length > mediaGalleryLimit) { errors[`data.components[${components!.indexOf(comp)}].items`] = { code: "TOO_LONG", message: "Media list is too long", @@ -233,12 +240,10 @@ export function handleComps(components: BaseMessageComponents[], flags: number) } medias.push(...comp.items.map(({ media }) => media)); } else if (comp.type === MessageComponentType.File) { - if (!compv2) throw new HTTPError("Must be comp v2"); medias.push(comp.file); } else if (comp.type === MessageComponentType.Separator) { - if (!compv2) throw new HTTPError("Must be comp v2"); + //Here to make sure everything is checked } else if (comp.type === MessageComponentType.Container) { - if (!compv2) throw new HTTPError("Must be comp v2"); for (const elm of comp.components) { switch (elm.type) { case MessageComponentType.Separator: @@ -246,7 +251,7 @@ export function handleComps(components: BaseMessageComponents[], flags: number) break; case MessageComponentType.Section: { const accessory = elm.accessory; - if (elm.components.length < 1 || elm.components.length > 3) { + if (elm.components.length < 1 || elm.components.length > actionRowLimit) { errors[`data.components[${components!.indexOf(comp)}].components[${comp.components!.indexOf(elm)}].components`] = { code: "TOO_LONG", message: "Component list is too long", @@ -258,7 +263,7 @@ export function handleComps(components: BaseMessageComponents[], flags: number) break; } case MessageComponentType.MediaGallery: - if (elm.items.length < 1 || elm.items.length > 10) { + if (elm.items.length < 1 || elm.items.length > mediaGalleryLimit) { errors[`data.components[${components!.indexOf(comp)}].components[${comp.components!.indexOf(elm)}].items`] = { code: "TOO_LONG", message: "Media list is too long", @@ -291,6 +296,7 @@ export function handleComps(components: BaseMessageComponents[], flags: number) }; } export async function handleMessage(opts: MessageOptions): Promise { + const conf = Config.get(); const handle = opts.components ? handleComps(opts.components, opts.flags || 0) : undefined; const channel = await Channel.findOneOrFail({ @@ -374,10 +380,10 @@ export async function handleMessage(opts: MessageOptions): Promise { }, }); - const cloneResponse = await fetch(`${Config.get().cdn.endpointPrivate}/attachments/${attEnt.uploadFilename}/clone_to_message/${message.id}`, { + const cloneResponse = await fetch(`${conf.cdn.endpointPrivate}/attachments/${attEnt.uploadFilename}/clone_to_message/${message.id}`, { method: "POST", headers: { - signature: Config.get().security.requestSignature || "", + signature: conf.security.requestSignature || "", }, }); @@ -390,8 +396,8 @@ export async function handleMessage(opts: MessageOptions): Promise { const realAtt = Attachment.create({ filename: attEnt.userFilename, - url: `${Config.get().cdn.endpointPublic}/${cloneRespBody.new_path}`, - proxy_url: `${Config.get().cdn.endpointPublic}/${cloneRespBody.new_path}`, + url: `${conf.cdn.endpointPublic}/${cloneRespBody.new_path}`, + proxy_url: `${conf.cdn.endpointPublic}/${cloneRespBody.new_path}`, size: attEnt.size, height: attEnt.height, width: attEnt.width, @@ -409,7 +415,7 @@ export async function handleMessage(opts: MessageOptions): Promise { } // else console.log("[Message] No cloud attachments to process for message", message.id, ":", message.attachments); - if (message.content && message.content.length > Config.get().limits.message.maxCharacters) { + if (message.content && message.content.length > conf.limits.message.maxCharacters) { throw new HTTPError("Content length over max character limit"); } @@ -767,6 +773,7 @@ export async function handleMessage(opts: MessageOptions): Promise { // TODO: cache link result in db export async function postHandleMessage(message: Message) { + const conf = Config.get(); const content = message.content?.replace(/ *`[^)]*` */g, ""); // remove markdown const linkMatches = content?.match(LINK_REGEX) || []; @@ -860,7 +867,7 @@ export async function postHandleMessage(message: Message) { } // bit gross, but whatever! - const endpointPublic = Config.get().cdn.endpointPublic; // lol + const endpointPublic = conf.cdn.endpointPublic; // lol const handler = url.hostname === new URL(endpointPublic!).hostname ? EmbedHandlers["self"] : EmbedHandlers[url.hostname] || EmbedHandlers["default"]; try { diff --git a/src/schemas/api/messages/Components.ts b/src/schemas/api/messages/Components.ts index 4d2f43700..bf041b53b 100644 --- a/src/schemas/api/messages/Components.ts +++ b/src/schemas/api/messages/Components.ts @@ -104,7 +104,7 @@ export interface ButtonComponent extends MessageComponent { disabled?: boolean; } -export enum ButtonStyle { +export const enum ButtonStyle { Primary = 1, Secondary = 2, Success = 3, @@ -159,12 +159,12 @@ export interface TextInputComponent extends MessageComponent { placeholder?: string; } -export enum TextInputStyle { +export const enum TextInputStyle { Short = 1, Paragraph = 2, } -export enum MessageComponentType { +export const enum MessageComponentType { ActionRow = 1, Button = 2, StringSelect = 3, @@ -189,3 +189,5 @@ export enum MessageComponentType { CheckboxGroup = 22, Checkbox = 23, } + +export const v1CompTypes = new Set([MessageComponentType.ActionRow]); diff --git a/src/util/config/Config.ts b/src/util/config/Config.ts index 05a72f6f7..7fe879db6 100644 --- a/src/util/config/Config.ts +++ b/src/util/config/Config.ts @@ -19,6 +19,7 @@ import { ApiConfiguration, CdnConfiguration, + ComponentConfiguration, DefaultsConfiguration, EmailConfiguration, EndpointConfiguration, @@ -59,4 +60,5 @@ export class ConfigValue { passwordReset: PasswordResetConfiguration = new PasswordResetConfiguration(); user: UserConfiguration = new UserConfiguration(); offload: OffloadConfiguration = new OffloadConfiguration(); + components = new ComponentConfiguration(); } diff --git a/src/util/config/types/ComponentConfiguration.ts b/src/util/config/types/ComponentConfiguration.ts new file mode 100644 index 000000000..deb8ffc79 --- /dev/null +++ b/src/util/config/types/ComponentConfiguration.ts @@ -0,0 +1,4 @@ +export class ComponentConfiguration { + mediaGalleryLimit: undefined | number = undefined; + actionRowLimit: undefined | number = undefined; +} diff --git a/src/util/config/types/index.ts b/src/util/config/types/index.ts index 3b18cf483..9e689c242 100644 --- a/src/util/config/types/index.ts +++ b/src/util/config/types/index.ts @@ -36,3 +36,4 @@ export * from "./SecurityConfiguration"; export * from "./subconfigurations"; export * from "./TemplateConfiguration"; export * from "./UsersConfiguration"; +export * from "./ComponentConfiguration";