From 3b34a0bf6d0c1845ddaa44d1d68b71406f4a7e39 Mon Sep 17 00:00:00 2001 From: CyberL1 Date: Thu, 18 Dec 2025 21:31:43 +0100 Subject: [PATCH 1/4] feat: add message forwards (normal messages only) --- assets/openapi.json | Bin 872541 -> 877549 bytes assets/schemas.json | Bin 390650 -> 394841 bytes src/api/util/handlers/Message.ts | 40 +++++++++++++++++- src/schemas/api/messages/Message.ts | 21 ++++++++- .../uncategorised/MessageCreateSchema.ts | 1 + src/util/entities/Message.ts | 5 ++- .../1765185286988-message-snapshots.ts | 13 ++++++ 7 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 src/util/migration/postgres/1765185286988-message-snapshots.ts diff --git a/assets/openapi.json b/assets/openapi.json index 0b326c88e8d67d43856b22ee2565cab9fddf7e3e..ad4bd98aa2a31573ad5d24684df22b1b6d3aa869 100644 GIT binary patch delta 964 zcmah|O-NKx6lUJN?{A#y6*^P?%!~>;qlkl~{SY-R!a%E6LP1!Wfw&NzGF(K9Au2-% zCNBA5WRA2Dg8s;QLYs*e5tYp*qJ{7%h$N&bsE{JNb6+}>1T7A{d+&F?bH4AKHzb5&^9Y7q0cKMbiD#sO#i)){5v@Xpmky(TlI*h<=A{?4|Am3Wx|n?}%*>Ok zto=Ww^@eqR1DXDWZQmtL?e9Gr>+kNv-CBAP0UuG-wm&!9BTB>Y1*ILv0NDxCk`$!w z$v*t5rjP&5eKbXjaeY+rqi;2J56*_DH5-!KCa^Fk<@m~{ za##@r`m$HsMdMNd(uF=l?-g&BoZ|C4-dLN@TGfgFMHPNSr+XMl}cqFjFGTyGPLZVj{8 z7MU4m9KUg=kVKHYC3%pOlK8--JYDH%%%>1i60d0al9Wv__Npa_){rIy=LpfmD*qoMmC_Gdi8V{X^n@54woc4I*zX5JPOYr~z delta 160 zcmaER-1P1O(}ottEllD+rw7btmzpk6$5=BxK%IHVGy`Uq?GJu2C2J!j+JO>4NtRF3 z1B_UEraL&Wa!eNpWEG$OK$GRr^aa8!Y|||_vPn#j6=LC<&c2sbV!8k~3+MER2drX~ w3s~6N4YXOd8)&nNEuY@c%);G%{V^*LvjH(X5OV-AClGT1G57ZCk9ouz02eeoE&u=k diff --git a/assets/schemas.json b/assets/schemas.json index 3d734edf161c2aafbe46f6417373e46c7f793fe1..08eea064282b2e80505a1c447134d0c2b5b39aaf 100644 GIT binary patch delta 777 zcmZ8fO-NKx80G%?=4I;S&x~cms7af!hJF@N)(}y;@F$`_MI0BFgg8@Ags^ZRNogt{ zU3#ZlWKqy2(xP}mP^0Zy1Ql4#LR^FeNlmM$NV@Nhj@9no^PP9@Ip=#nKl|TbD2)%0 zNA%MWV%v#d&gdFq{p=g+*02UUUBQkJgpaf3cI$n5EzkaNl}3yIW!j2aL+tPDW%9T56SSp969nP zaQXWVTrOP%?1l(u5p8{bWS>g1Ej**M1pu>u(cUTrK*%BdzAE$e@%99;Nw5wOX8c_>VT`5;h@IIuQWwgabbQziO%Ns2vO9pVU zfoPcP{o89O&xfWd1*d#m8D2=_lQTPeoo)DJT-R~f5+&|oej6{g_K;kJ5==N%l`P&e za}A=@A*!eXp-G|3?tBl@???yItEeWg*Bx~K0<@&KlAYp1I9kW@WpwTkHr%G|4xWd{ c0Z{|fD^8)RjUA9*TXP4CX1QH7D?S$d0h`|z5&!@I delta 185 zcmccFBk}9CctZ>07N!>+(;GaP7EW(qWft82vy*A3G7_Ith1r^M`iK22j*}byDNdfi z#4~+?6^ja(vEewg0+{h3o-uU#`K?Tx)AMtfMSw!W(-lma&D`>N_PQO2!S$X=0GG@W)H9gEO)8|()v$a3( bVc!0{hh { if (!opts.message_reference.guild_id) opts.message_reference.guild_id = channel.guild_id; if (!opts.message_reference.channel_id) opts.message_reference.channel_id = opts.channel_id; - if (!guild.features.includes("CROSS_CHANNEL_REPLIES")) { + if (opts.message_reference.type != 1) { if (opts.message_reference.guild_id !== channel.guild_id) throw new HTTPError("You can only reference messages from this guild"); if (opts.message_reference.channel_id !== opts.channel_id) throw new HTTPError("You can only reference messages from this channel"); } @@ -257,7 +257,16 @@ export async function handleMessage(opts: MessageOptions): Promise { } // TODO: stickers/activity - if (!allow_empty && !opts.content && !opts.embeds?.length && !opts.attachments?.length && !opts.sticker_ids?.length && !opts.poll && !opts.components?.length) { + if ( + !allow_empty && + !opts.content && + !opts.embeds?.length && + !opts.attachments?.length && + !opts.sticker_ids?.length && + !opts.poll && + !opts.components?.length && + opts.message_reference?.type != 1 + ) { console.log("[Message] Rejecting empty message:", opts, message); throw new HTTPError("Empty messages are not allowed", 50006); } @@ -314,6 +323,33 @@ export async function handleMessage(opts: MessageOptions): Promise { }), ); } + + // FORWARD + if (message.message_reference.type === 1) { + message.type = MessageType.DEFAULT; + + if (message.referenced_message) { + const mention_roles: string[] = []; + const mentions: string[] = []; + + message.message_snapshots = [ + { + message: { + attachments: message.referenced_message.attachments, + components: message.referenced_message.components, + content: message.referenced_message.content!, + edited_timestamp: message.referenced_message.edited_timestamp, + embeds: message.referenced_message.embeds, + flags: message.referenced_message.flags, + mention_roles, + mentions, + timestamp: message.referenced_message.timestamp, + type: message.referenced_message.type, + }, + }, + ]; + } + } } // root@Rory - 20/02/2023 - This breaks channel mentions in test client. We're not sure this was used in older clients. diff --git a/src/schemas/api/messages/Message.ts b/src/schemas/api/messages/Message.ts index 69984e2ab..a100f266b 100644 --- a/src/schemas/api/messages/Message.ts +++ b/src/schemas/api/messages/Message.ts @@ -16,7 +16,8 @@ along with this program. If not, see . */ -import { PartialUser, Snowflake } from "@spacebar/schemas"; +import { Attachment, Sticker } from "@spacebar/util"; +import { Embed, MessageComponent, PartialUser, Snowflake } from "@spacebar/schemas"; export enum MessageType { DEFAULT = 0, @@ -94,3 +95,21 @@ export interface AllowedMentions { users?: Snowflake[]; replied_user?: boolean; } + +export interface MessageSnapshot { + message: { + content: string; + timestamp: Date; + edited_timestamp?: Date | null; + mentions: Snowflake[]; + mention_roles: Snowflake[]; + attachments?: Attachment[]; + embeds: Embed[]; + type: MessageType; + flags: number; + components?: MessageComponent[]; + resolved?: object[]; + sticker_items?: Sticker[]; + // soundboard_sounds?: object[]; // TODO: when soundboard is done + } +} diff --git a/src/schemas/uncategorised/MessageCreateSchema.ts b/src/schemas/uncategorised/MessageCreateSchema.ts index bdf2ab221..d9442e2fa 100644 --- a/src/schemas/uncategorised/MessageCreateSchema.ts +++ b/src/schemas/uncategorised/MessageCreateSchema.ts @@ -53,6 +53,7 @@ export interface MessageCreateSchema { channel_id?: string; guild_id?: string; fail_if_not_exists?: boolean; + type?: number; }; payload_json?: string; file?: { filename: string }; diff --git a/src/util/entities/Message.ts b/src/util/entities/Message.ts index 9935ae044..73d46cb19 100644 --- a/src/util/entities/Message.ts +++ b/src/util/entities/Message.ts @@ -29,7 +29,7 @@ import { Webhook } from "./Webhook"; import { Sticker } from "./Sticker"; import { Attachment } from "./Attachment"; import { NewUrlUserSignatureData } from "../Signing"; -import { ActionRowComponent, ApplicationCommandType, Embed, MessageType, PartialMessage, Poll, Reaction } from "@spacebar/schemas"; +import { ActionRowComponent, ApplicationCommandType, Embed, MessageSnapshot, MessageType, PartialMessage, Poll, Reaction } from "@spacebar/schemas"; import { MessageFlags } from "@spacebar/util"; @Entity({ @@ -202,6 +202,9 @@ export class Message extends BaseClass { @Column({ nullable: true }) avatar?: string; + @Column({ default: "[]", type: "simple-json" }) + message_snapshots: MessageSnapshot[]; + toJSON(): Message { return { ...this, diff --git a/src/util/migration/postgres/1765185286988-message-snapshots.ts b/src/util/migration/postgres/1765185286988-message-snapshots.ts new file mode 100644 index 000000000..c9047fc26 --- /dev/null +++ b/src/util/migration/postgres/1765185286988-message-snapshots.ts @@ -0,0 +1,13 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class MessageSnapshots1765185286988 implements MigrationInterface { + name = "MessageSnapshots1765185286988"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "messages" ADD "message_snapshots" text NOT NULL DEFAULT '[]'`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "messages" DROP COLUMN "message_snapshots"`); + } +} From 4553b1d12691b8722d65813fa430f64a3b4b6c53 Mon Sep 17 00:00:00 2001 From: CyberL1 Date: Thu, 18 Dec 2025 21:39:14 +0100 Subject: [PATCH 2/4] chore: run prettier --- scripts/schemaExclusions.json | 14 +++------ src/schemas/api/messages/Message.ts | 30 +++++++++---------- .../uncategorised/MessageCreateSchema.ts | 2 +- src/util/entities/Message.ts | 4 +-- .../1765185286988-message-snapshots.ts | 14 ++++----- 5 files changed, 29 insertions(+), 35 deletions(-) diff --git a/scripts/schemaExclusions.json b/scripts/schemaExclusions.json index 64e2bbf00..b28476707 100644 --- a/scripts/schemaExclusions.json +++ b/scripts/schemaExclusions.json @@ -1,10 +1,6 @@ { - "include": [ - "MessageInteractionSchema" - ], - "includeRe": [ - "^MessageComponentType\\..*" - ], + "include": ["MessageInteractionSchema"], + "includeRe": ["^MessageComponentType\\..*"], "manual": [ "DefaultSchema", "Schema", @@ -87,9 +83,7 @@ "^Job" ], "manualWarn": [], - "manualWarnRe": [ - ".*<.*>$" - ], + "manualWarnRe": [".*<.*>$"], "auto": [ { "value": "StringSchema", @@ -412,4 +406,4 @@ "reason": "Schema with only uppercase properties" } ] -} \ No newline at end of file +} diff --git a/src/schemas/api/messages/Message.ts b/src/schemas/api/messages/Message.ts index a100f266b..a46c8f10f 100644 --- a/src/schemas/api/messages/Message.ts +++ b/src/schemas/api/messages/Message.ts @@ -97,19 +97,19 @@ export interface AllowedMentions { } export interface MessageSnapshot { - message: { - content: string; - timestamp: Date; - edited_timestamp?: Date | null; - mentions: Snowflake[]; - mention_roles: Snowflake[]; - attachments?: Attachment[]; - embeds: Embed[]; - type: MessageType; - flags: number; - components?: MessageComponent[]; - resolved?: object[]; - sticker_items?: Sticker[]; - // soundboard_sounds?: object[]; // TODO: when soundboard is done - } + message: { + content: string; + timestamp: Date; + edited_timestamp?: Date | null; + mentions: Snowflake[]; + mention_roles: Snowflake[]; + attachments?: Attachment[]; + embeds: Embed[]; + type: MessageType; + flags: number; + components?: MessageComponent[]; + resolved?: object[]; + sticker_items?: Sticker[]; + // soundboard_sounds?: object[]; // TODO: when soundboard is done + }; } diff --git a/src/schemas/uncategorised/MessageCreateSchema.ts b/src/schemas/uncategorised/MessageCreateSchema.ts index d9442e2fa..345be32cf 100644 --- a/src/schemas/uncategorised/MessageCreateSchema.ts +++ b/src/schemas/uncategorised/MessageCreateSchema.ts @@ -53,7 +53,7 @@ export interface MessageCreateSchema { channel_id?: string; guild_id?: string; fail_if_not_exists?: boolean; - type?: number; + type?: number; }; payload_json?: string; file?: { filename: string }; diff --git a/src/util/entities/Message.ts b/src/util/entities/Message.ts index 73d46cb19..508ffd8ca 100644 --- a/src/util/entities/Message.ts +++ b/src/util/entities/Message.ts @@ -202,8 +202,8 @@ export class Message extends BaseClass { @Column({ nullable: true }) avatar?: string; - @Column({ default: "[]", type: "simple-json" }) - message_snapshots: MessageSnapshot[]; + @Column({ default: "[]", type: "simple-json" }) + message_snapshots: MessageSnapshot[]; toJSON(): Message { return { diff --git a/src/util/migration/postgres/1765185286988-message-snapshots.ts b/src/util/migration/postgres/1765185286988-message-snapshots.ts index c9047fc26..ca0c33eea 100644 --- a/src/util/migration/postgres/1765185286988-message-snapshots.ts +++ b/src/util/migration/postgres/1765185286988-message-snapshots.ts @@ -1,13 +1,13 @@ import { MigrationInterface, QueryRunner } from "typeorm"; export class MessageSnapshots1765185286988 implements MigrationInterface { - name = "MessageSnapshots1765185286988"; + name = "MessageSnapshots1765185286988"; - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "messages" ADD "message_snapshots" text NOT NULL DEFAULT '[]'`); - } + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "messages" ADD "message_snapshots" text NOT NULL DEFAULT '[]'`); + } - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "messages" DROP COLUMN "message_snapshots"`); - } + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "messages" DROP COLUMN "message_snapshots"`); + } } From c08765061c5bdc3e9a6c42225bfac62a69bc5a92 Mon Sep 17 00:00:00 2001 From: CyberL1 Date: Thu, 18 Dec 2025 21:50:48 +0100 Subject: [PATCH 3/4] add TODO --- scripts/schemaExclusions.json | 14 ++++++++++---- src/api/util/handlers/Message.ts | 2 ++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/scripts/schemaExclusions.json b/scripts/schemaExclusions.json index b28476707..64e2bbf00 100644 --- a/scripts/schemaExclusions.json +++ b/scripts/schemaExclusions.json @@ -1,6 +1,10 @@ { - "include": ["MessageInteractionSchema"], - "includeRe": ["^MessageComponentType\\..*"], + "include": [ + "MessageInteractionSchema" + ], + "includeRe": [ + "^MessageComponentType\\..*" + ], "manual": [ "DefaultSchema", "Schema", @@ -83,7 +87,9 @@ "^Job" ], "manualWarn": [], - "manualWarnRe": [".*<.*>$"], + "manualWarnRe": [ + ".*<.*>$" + ], "auto": [ { "value": "StringSchema", @@ -406,4 +412,4 @@ "reason": "Schema with only uppercase properties" } ] -} +} \ No newline at end of file diff --git a/src/api/util/handlers/Message.ts b/src/api/util/handlers/Message.ts index 84758c0bc..618f02f04 100644 --- a/src/api/util/handlers/Message.ts +++ b/src/api/util/handlers/Message.ts @@ -332,6 +332,8 @@ export async function handleMessage(opts: MessageOptions): Promise { const mention_roles: string[] = []; const mentions: string[] = []; + // TODO: mention_roles and mentions arrays - not needed it seems, but discord still returns that + message.message_snapshots = [ { message: { From 30dfb0841ab3661524a219f6c39929e2ccfb2a43 Mon Sep 17 00:00:00 2001 From: CyberL1 Date: Thu, 18 Dec 2025 22:03:41 +0100 Subject: [PATCH 4/4] chore: run prettier (again) --- scripts/schemaExclusions.json | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/scripts/schemaExclusions.json b/scripts/schemaExclusions.json index 64e2bbf00..b28476707 100644 --- a/scripts/schemaExclusions.json +++ b/scripts/schemaExclusions.json @@ -1,10 +1,6 @@ { - "include": [ - "MessageInteractionSchema" - ], - "includeRe": [ - "^MessageComponentType\\..*" - ], + "include": ["MessageInteractionSchema"], + "includeRe": ["^MessageComponentType\\..*"], "manual": [ "DefaultSchema", "Schema", @@ -87,9 +83,7 @@ "^Job" ], "manualWarn": [], - "manualWarnRe": [ - ".*<.*>$" - ], + "manualWarnRe": [".*<.*>$"], "auto": [ { "value": "StringSchema", @@ -412,4 +406,4 @@ "reason": "Schema with only uppercase properties" } ] -} \ No newline at end of file +}