diff --git a/assets/openapi.json b/assets/openapi.json
index 0b326c8..ad4bd98 100644
--- a/assets/openapi.json
+++ b/assets/openapi.json
@@ -5460,6 +5460,9 @@
},
"fail_if_not_exists": {
"type": "boolean"
+ },
+ "type": {
+ "type": "integer"
}
},
"additionalProperties": false,
@@ -5750,6 +5753,9 @@
},
"fail_if_not_exists": {
"type": "boolean"
+ },
+ "type": {
+ "type": "integer"
}
},
"additionalProperties": false,
@@ -7615,6 +7621,12 @@
"avatar": {
"type": "string"
},
+ "message_snapshots": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/MessageSnapshot"
+ }
+ },
"id": {
"type": "string"
}
@@ -7627,6 +7639,7 @@
"mention_channels",
"mention_roles",
"mentions",
+ "message_snapshots",
"pinned",
"reactions",
"timestamp",
@@ -10835,6 +10848,122 @@
"me_voted"
]
},
+ "MessageSnapshot": {
+ "type": "object",
+ "properties": {
+ "message": {
+ "type": "object",
+ "properties": {
+ "content": {
+ "type": "string"
+ },
+ "timestamp": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "edited_timestamp": {
+ "anyOf": [
+ {
+ "type": "string",
+ "format": "date-time"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "mentions": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "mention_roles": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "attachments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Attachment"
+ }
+ },
+ "embeds": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Embed"
+ }
+ },
+ "type": {
+ "$ref": "#/components/schemas/MessageType"
+ },
+ "flags": {
+ "type": "integer"
+ },
+ "components": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/MessageComponent"
+ }
+ },
+ "resolved": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {},
+ "additionalProperties": true
+ }
+ },
+ "sticker_items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Sticker"
+ }
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "content",
+ "embeds",
+ "flags",
+ "mention_roles",
+ "mentions",
+ "timestamp",
+ "type"
+ ]
+ }
+ },
+ "required": [
+ "message"
+ ]
+ },
+ "MessageComponent": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "$ref": "#/components/schemas/MessageComponentType"
+ }
+ },
+ "required": [
+ "type"
+ ]
+ },
+ "MessageComponentType": {
+ "type": "number",
+ "enum": [
+ 0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8
+ ]
+ },
"InteractionGuild": {
"type": "object",
"properties": {
diff --git a/assets/schemas.json b/assets/schemas.json
index 3d734ed..08eea06 100644
--- a/assets/schemas.json
+++ b/assets/schemas.json
@@ -5807,6 +5807,9 @@
},
"fail_if_not_exists": {
"type": "boolean"
+ },
+ "type": {
+ "type": "integer"
}
},
"additionalProperties": false,
@@ -6103,6 +6106,9 @@
},
"fail_if_not_exists": {
"type": "boolean"
+ },
+ "type": {
+ "type": "integer"
}
},
"additionalProperties": false,
@@ -8092,6 +8098,12 @@
"avatar": {
"type": "string"
},
+ "message_snapshots": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/MessageSnapshot"
+ }
+ },
"id": {
"type": "string"
}
@@ -8105,6 +8117,7 @@
"mention_channels",
"mention_roles",
"mentions",
+ "message_snapshots",
"pinned",
"reactions",
"timestamp",
@@ -11460,6 +11473,127 @@
],
"$schema": "http://json-schema.org/draft-07/schema#"
},
+ "MessageSnapshot": {
+ "type": "object",
+ "properties": {
+ "message": {
+ "type": "object",
+ "properties": {
+ "content": {
+ "type": "string"
+ },
+ "timestamp": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "edited_timestamp": {
+ "anyOf": [
+ {
+ "type": "string",
+ "format": "date-time"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "mentions": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "mention_roles": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "attachments": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Attachment"
+ }
+ },
+ "embeds": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Embed"
+ }
+ },
+ "type": {
+ "$ref": "#/definitions/MessageType"
+ },
+ "flags": {
+ "type": "integer"
+ },
+ "components": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/MessageComponent"
+ }
+ },
+ "resolved": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {},
+ "additionalProperties": true
+ }
+ },
+ "sticker_items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Sticker"
+ }
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "content",
+ "embeds",
+ "flags",
+ "mention_roles",
+ "mentions",
+ "timestamp",
+ "type"
+ ]
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "message"
+ ],
+ "$schema": "http://json-schema.org/draft-07/schema#"
+ },
+ "MessageComponent": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "$ref": "#/definitions/MessageComponentType"
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "type"
+ ],
+ "$schema": "http://json-schema.org/draft-07/schema#"
+ },
+ "MessageComponentType": {
+ "type": "number",
+ "enum": [
+ 0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8
+ ],
+ "$schema": "http://json-schema.org/draft-07/schema#"
+ },
"InteractionGuild": {
"type": "object",
"properties": {
diff --git a/scripts/schemaExclusions.json b/scripts/schemaExclusions.json
index 64e2bbf..b284767 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/api/util/handlers/Message.ts b/src/api/util/handlers/Message.ts
index 915fe29..618f02f 100644
--- a/src/api/util/handlers/Message.ts
+++ b/src/api/util/handlers/Message.ts
@@ -232,7 +232,7 @@
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 @@
}
// 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,35 @@
}),
);
}
+
+ // FORWARD
+ if (message.message_reference.type === 1) {
+ message.type = MessageType.DEFAULT;
+
+ if (message.referenced_message) {
+ 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: {
+ 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 69984e2..a46c8f1 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 @@
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 bdf2ab2..345be32 100644
--- a/src/schemas/uncategorised/MessageCreateSchema.ts
+++ b/src/schemas/uncategorised/MessageCreateSchema.ts
@@ -53,6 +53,7 @@
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 9935ae0..508ffd8 100644
--- a/src/util/entities/Message.ts
+++ b/src/util/entities/Message.ts
@@ -29,7 +29,7 @@
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 @@
@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 0000000..ca0c33e
--- /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"`);
+ }
+}