diff --git a/src/api/routes/channels/#channel_id/messages/index.ts b/src/api/routes/channels/#channel_id/messages/index.ts
index 7687d8be..f842a3a5 100644
--- a/src/api/routes/channels/#channel_id/messages/index.ts
+++ b/src/api/routes/channels/#channel_id/messages/index.ts
@@ -502,15 +502,11 @@ router.post(
// no await as it shouldnt block the message send function and silently catch error
postHandleMessage(message).catch((e) => console.error("[Message] post-message handler failed", e));
-
- return res.json(
- message.withSignedAttachments(
- new NewUrlUserSignatureData({
- ip: req.ip,
- userAgent: req.headers["user-agent"] as string,
- }),
- ),
- );
+ const sign = new NewUrlUserSignatureData({
+ ip: req.ip,
+ userAgent: req.headers["user-agent"] as string,
+ });
+ return res.json(await Message.prototype.withSignedComponents.call(message.withSignedAttachments(sign), sign));
},
);
diff --git a/src/api/util/handlers/Message.ts b/src/api/util/handlers/Message.ts
index 0a50809a..54da86a8 100644
--- a/src/api/util/handlers/Message.ts
+++ b/src/api/util/handlers/Message.ts
@@ -163,8 +163,20 @@ async function processMedia(media: UnfurledMediaItem, messageId: string, batchId
}
const cloneRespBody = (await cloneResponse.json()) as { success: boolean; new_path: string };
+
+ const realAtt = Attachment.create({
+ filename: attEnt.userFilename,
+ url: `${Config.get().cdn.endpointPublic}/${cloneRespBody.new_path}`,
+ proxy_url: `${Config.get().cdn.endpointPublic}/${cloneRespBody.new_path}`,
+ size: attEnt.size,
+ height: attEnt.height,
+ width: attEnt.width,
+ content_type: attEnt.contentType || attEnt.userOriginalContentType,
+ });
+ await realAtt.save();
+
//TODO maybe this needs to be a new DB object? I don't see a reason to do this rn though, though this id *should* technically be different from the id of the attachment
- media.id = attEnt.id;
+ media.id = realAtt.id;
media.proxy_url = `${Config.get().cdn.endpointPublic}/${cloneRespBody.new_path}`;
if (url.protocol !== "attachment") media.url = media.proxy_url;
media.height = attEnt.height;
diff --git a/src/gateway/listener/listener.ts b/src/gateway/listener/listener.ts
index 112f55bb..e758849b 100644
--- a/src/gateway/listener/listener.ts
+++ b/src/gateway/listener/listener.ts
@@ -352,6 +352,17 @@ async function consume(this: WebSocket, opts: EventOpts) {
userAgent: this.userAgent,
}),
).attachments;
+ if (data["components"]) {
+ data["components"] = (
+ await Message.prototype.withSignedComponents.call(
+ data,
+ new NewUrlUserSignatureData({
+ ip: this.ipAddress,
+ userAgent: this.userAgent,
+ }),
+ )
+ ).components;
+ }
break;
default:
break;
diff --git a/src/util/entities/Message.ts b/src/util/entities/Message.ts
index 05e7dea5..cd89c6e2 100644
--- a/src/util/entities/Message.ts
+++ b/src/util/entities/Message.ts
@@ -29,7 +29,18 @@ import { Webhook } from "./Webhook";
import { Sticker } from "./Sticker";
import { Attachment } from "./Attachment";
import { NewUrlUserSignatureData } from "../Signing";
-import { ApplicationCommandType, BaseMessageComponents, Embed, MessageSnapshot, MessageType, PartialMessage, Poll, Reaction } from "@spacebar/schemas";
+import {
+ ApplicationCommandType,
+ BaseMessageComponents,
+ Embed,
+ MessageComponentType,
+ MessageSnapshot,
+ MessageType,
+ PartialMessage,
+ Poll,
+ Reaction,
+ UnfurledMediaItem,
+} from "@spacebar/schemas";
import { MessageFlags } from "@spacebar/util";
import { JsonRemoveEmpty } from "../util/Decorators";
@@ -304,6 +315,55 @@ export class Message extends BaseClass {
attachments: this.attachments?.map((attachment: Attachment) => Attachment.prototype.signUrls.call(attachment, data)),
};
}
+ async withSignedComponents(data: NewUrlUserSignatureData) {
+ if (!this.components || !(this.flags & Number(MessageFlags.FLAGS.IS_COMPONENTS_V2))) return { ...this };
+ function signMedia(media: UnfurledMediaItem) {
+ Object.assign(media, Attachment.prototype.signUrls.call(media, data));
+ }
+ return {
+ ...this,
+ components: this.components.map((comp) => {
+ comp = structuredClone(comp);
+ if (comp.type === MessageComponentType.Section) {
+ const accessory = comp.accessory;
+ if (accessory.type === MessageComponentType.Thumbnail) {
+ signMedia(accessory.media);
+ }
+ } else if (comp.type === MessageComponentType.MediaGallery) {
+ comp.items.forEach(({ media }) => signMedia(media));
+ } else if (comp.type === MessageComponentType.File) {
+ signMedia(comp.file);
+ } else if (comp.type === MessageComponentType.Container) {
+ for (const elm of comp.components) {
+ switch (elm.type) {
+ case MessageComponentType.Separator:
+ case MessageComponentType.TextDisplay:
+ case MessageComponentType.ActionRow:
+ break;
+ case MessageComponentType.Section: {
+ const accessory = elm.accessory;
+ if (accessory.type === MessageComponentType.Thumbnail) {
+ signMedia(accessory.media);
+ }
+ break;
+ }
+ case MessageComponentType.MediaGallery:
+ elm.items.forEach(({ media }) => signMedia(media));
+ break;
+ case MessageComponentType.File: {
+ signMedia(elm.file);
+ break;
+ }
+
+ default:
+ elm satisfies never;
+ }
+ }
+ }
+ return comp;
+ }),
+ };
+ }
static async createWithDefaults(opts: Partial<Message>): Promise<Message> {
const message = Message.create();
|