diff --git a/assets/openapi.json b/assets/openapi.json
index 09506ad..efd3a4c 100644
--- a/assets/openapi.json
+++ b/assets/openapi.json
@@ -5473,11 +5473,14 @@
"items": {
"type": "string"
}
+ },
+ "channel_ids": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
}
- },
- "required": [
- "channels"
- ]
+ }
},
"PruneSchema": {
"type": "object",
@@ -6638,6 +6641,72 @@
"$ref": "#/components/schemas/ApplicationCommandCreateSchema"
}
},
+ "CreateReportSchema": {
+ "type": "object",
+ "properties": {
+ "version": {
+ "type": "string"
+ },
+ "variant": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "language": {
+ "type": "string"
+ },
+ "breadcrumbs": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "elements": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "channel_id": {
+ "type": "string"
+ },
+ "message_id": {
+ "type": "string"
+ },
+ "guild_id": {
+ "type": "string"
+ },
+ "stage_instance_id": {
+ "type": "string"
+ },
+ "guild_scheduled_event_id": {
+ "type": "string"
+ },
+ "reported_user_id": {
+ "type": "string"
+ },
+ "application_id": {
+ "type": "string"
+ },
+ "user_id": {
+ "type": "string"
+ },
+ "widget_id": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "breadcrumbs",
+ "language",
+ "name",
+ "variant",
+ "version"
+ ]
+ },
"SessionsLogoutSchema": {
"type": "object",
"properties": {
@@ -16117,7 +16186,7 @@
]
}
},
- "/reporting/menu/": {
+ "/reporting/": {
"get": {
"security": [
{
@@ -16152,7 +16221,14 @@
"description": "Get reporting menu options for guild reports.",
"responses": {
"200": {
- "description": "No description available"
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ReportingMenuResponse"
+ }
+ }
+ }
},
"204": {
"description": "No description available"
@@ -16180,6 +16256,44 @@
]
}
},
+ "/reporting/guild": {
+ "post": {
+ "security": [
+ {
+ "bearer": []
+ }
+ ],
+ "description": "Get reporting menu options for guild reports.",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateReportSchema"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ReportingMenuResponse"
+ }
+ }
+ }
+ },
+ "204": {
+ "description": "No description available"
+ }
+ },
+ "tags": [
+ "reporting"
+ ]
+ }
+ },
"/reporting/menu/guild_discovery": {
"get": {
"security": [
@@ -16190,7 +16304,14 @@
"description": "Get reporting menu options for guild_discovery reports.",
"responses": {
"200": {
- "description": "No description available"
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ReportingMenuResponse"
+ }
+ }
+ }
},
"204": {
"description": "No description available"
@@ -16218,6 +16339,44 @@
]
}
},
+ "/reporting/guild_discovery": {
+ "post": {
+ "security": [
+ {
+ "bearer": []
+ }
+ ],
+ "description": "Get reporting menu options for guild_discovery reports.",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateReportSchema"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ReportingMenuResponse"
+ }
+ }
+ }
+ },
+ "204": {
+ "description": "No description available"
+ }
+ },
+ "tags": [
+ "reporting"
+ ]
+ }
+ },
"/reporting/menu/guild_directory_entry": {
"get": {
"security": [
@@ -16228,7 +16387,14 @@
"description": "Get reporting menu options for guild_directory_entry reports.",
"responses": {
"200": {
- "description": "No description available"
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ReportingMenuResponse"
+ }
+ }
+ }
},
"204": {
"description": "No description available"
@@ -16256,6 +16422,44 @@
]
}
},
+ "/reporting/guild_directory_entry": {
+ "post": {
+ "security": [
+ {
+ "bearer": []
+ }
+ ],
+ "description": "Get reporting menu options for guild_directory_entry reports.",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateReportSchema"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ReportingMenuResponse"
+ }
+ }
+ }
+ },
+ "204": {
+ "description": "No description available"
+ }
+ },
+ "tags": [
+ "reporting"
+ ]
+ }
+ },
"/reporting/menu/guild_scheduled_event": {
"get": {
"security": [
@@ -16266,7 +16470,14 @@
"description": "Get reporting menu options for guild_scheduled_event reports.",
"responses": {
"200": {
- "description": "No description available"
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ReportingMenuResponse"
+ }
+ }
+ }
},
"204": {
"description": "No description available"
@@ -16294,6 +16505,44 @@
]
}
},
+ "/reporting/guild_scheduled_event": {
+ "post": {
+ "security": [
+ {
+ "bearer": []
+ }
+ ],
+ "description": "Get reporting menu options for guild_scheduled_event reports.",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateReportSchema"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ReportingMenuResponse"
+ }
+ }
+ }
+ },
+ "204": {
+ "description": "No description available"
+ }
+ },
+ "tags": [
+ "reporting"
+ ]
+ }
+ },
"/reporting/menu/message": {
"get": {
"security": [
@@ -16304,7 +16553,14 @@
"description": "Get reporting menu options for message reports.",
"responses": {
"200": {
- "description": "No description available"
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ReportingMenuResponse"
+ }
+ }
+ }
},
"204": {
"description": "No description available"
@@ -16332,6 +16588,44 @@
]
}
},
+ "/reporting/message": {
+ "post": {
+ "security": [
+ {
+ "bearer": []
+ }
+ ],
+ "description": "Get reporting menu options for message reports.",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateReportSchema"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ReportingMenuResponse"
+ }
+ }
+ }
+ },
+ "204": {
+ "description": "No description available"
+ }
+ },
+ "tags": [
+ "reporting"
+ ]
+ }
+ },
"/reporting/menu/stage_channel": {
"get": {
"security": [
@@ -16342,7 +16636,14 @@
"description": "Get reporting menu options for stage_channel reports.",
"responses": {
"200": {
- "description": "No description available"
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ReportingMenuResponse"
+ }
+ }
+ }
},
"204": {
"description": "No description available"
@@ -16370,6 +16671,44 @@
]
}
},
+ "/reporting/stage_channel": {
+ "post": {
+ "security": [
+ {
+ "bearer": []
+ }
+ ],
+ "description": "Get reporting menu options for stage_channel reports.",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateReportSchema"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ReportingMenuResponse"
+ }
+ }
+ }
+ },
+ "204": {
+ "description": "No description available"
+ }
+ },
+ "tags": [
+ "reporting"
+ ]
+ }
+ },
"/reporting/menu/first_dm": {
"get": {
"security": [
@@ -16380,7 +16719,14 @@
"description": "Get reporting menu options for first_dm reports.",
"responses": {
"200": {
- "description": "No description available"
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ReportingMenuResponse"
+ }
+ }
+ }
},
"204": {
"description": "No description available"
@@ -16408,6 +16754,44 @@
]
}
},
+ "/reporting/first_dm": {
+ "post": {
+ "security": [
+ {
+ "bearer": []
+ }
+ ],
+ "description": "Get reporting menu options for first_dm reports.",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateReportSchema"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ReportingMenuResponse"
+ }
+ }
+ }
+ },
+ "204": {
+ "description": "No description available"
+ }
+ },
+ "tags": [
+ "reporting"
+ ]
+ }
+ },
"/reporting/menu/user": {
"get": {
"security": [
@@ -16418,7 +16802,14 @@
"description": "Get reporting menu options for user reports.",
"responses": {
"200": {
- "description": "No description available"
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ReportingMenuResponse"
+ }
+ }
+ }
},
"204": {
"description": "No description available"
@@ -16446,6 +16837,44 @@
]
}
},
+ "/reporting/user": {
+ "post": {
+ "security": [
+ {
+ "bearer": []
+ }
+ ],
+ "description": "Get reporting menu options for user reports.",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateReportSchema"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ReportingMenuResponse"
+ }
+ }
+ }
+ },
+ "204": {
+ "description": "No description available"
+ }
+ },
+ "tags": [
+ "reporting"
+ ]
+ }
+ },
"/reporting/menu/application": {
"get": {
"security": [
@@ -16456,7 +16885,14 @@
"description": "Get reporting menu options for application reports.",
"responses": {
"200": {
- "description": "No description available"
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ReportingMenuResponse"
+ }
+ }
+ }
},
"204": {
"description": "No description available"
@@ -16484,6 +16920,44 @@
]
}
},
+ "/reporting/application": {
+ "post": {
+ "security": [
+ {
+ "bearer": []
+ }
+ ],
+ "description": "Get reporting menu options for application reports.",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateReportSchema"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ReportingMenuResponse"
+ }
+ }
+ }
+ },
+ "204": {
+ "description": "No description available"
+ }
+ },
+ "tags": [
+ "reporting"
+ ]
+ }
+ },
"/reporting/menu/widget": {
"get": {
"security": [
@@ -16494,7 +16968,14 @@
"description": "Get reporting menu options for widget reports.",
"responses": {
"200": {
- "description": "No description available"
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ReportingMenuResponse"
+ }
+ }
+ }
},
"204": {
"description": "No description available"
@@ -16522,6 +17003,44 @@
]
}
},
+ "/reporting/widget": {
+ "post": {
+ "security": [
+ {
+ "bearer": []
+ }
+ ],
+ "description": "Get reporting menu options for widget reports.",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateReportSchema"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ReportingMenuResponse"
+ }
+ }
+ }
+ },
+ "204": {
+ "description": "No description available"
+ }
+ },
+ "tags": [
+ "reporting"
+ ]
+ }
+ },
"/read-states/ack-bulk/": {
"post": {
"security": [
diff --git a/assets/schemas.json b/assets/schemas.json
index d3b2404..a7d759f 100644
--- a/assets/schemas.json
+++ b/assets/schemas.json
@@ -5814,12 +5814,15 @@
"items": {
"type": "string"
}
+ },
+ "channel_ids": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
}
},
"additionalProperties": false,
- "required": [
- "channels"
- ],
"$schema": "http://json-schema.org/draft-07/schema#"
},
"PruneSchema": {
@@ -7074,6 +7077,74 @@
},
"$schema": "http://json-schema.org/draft-07/schema#"
},
+ "CreateReportSchema": {
+ "type": "object",
+ "properties": {
+ "version": {
+ "type": "string"
+ },
+ "variant": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "language": {
+ "type": "string"
+ },
+ "breadcrumbs": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ },
+ "elements": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "channel_id": {
+ "type": "string"
+ },
+ "message_id": {
+ "type": "string"
+ },
+ "guild_id": {
+ "type": "string"
+ },
+ "stage_instance_id": {
+ "type": "string"
+ },
+ "guild_scheduled_event_id": {
+ "type": "string"
+ },
+ "reported_user_id": {
+ "type": "string"
+ },
+ "application_id": {
+ "type": "string"
+ },
+ "user_id": {
+ "type": "string"
+ },
+ "widget_id": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "breadcrumbs",
+ "language",
+ "name",
+ "variant",
+ "version"
+ ],
+ "$schema": "http://json-schema.org/draft-07/schema#"
+ },
"SessionsLogoutSchema": {
"type": "object",
"properties": {
diff --git a/src/api/routes/channels/preload-messages.ts b/src/api/routes/channels/preload-messages.ts
index f50129f..09743cc 100644
--- a/src/api/routes/channels/preload-messages.ts
+++ b/src/api/routes/channels/preload-messages.ts
@@ -37,6 +37,7 @@
}),
async (req: Request, res: Response) => {
const body = req.body as PreloadMessagesRequestSchema;
+ body.channels ??= body.channel_ids ?? [];
if (body.channels.length > Config.get().limits.message.maxPreloadCount)
return res.status(400).send({
code: 400,
diff --git a/src/api/routes/reporting/index.ts b/src/api/routes/reporting/index.ts
new file mode 100644
index 0000000..f061cdf
--- /dev/null
+++ b/src/api/routes/reporting/index.ts
@@ -0,0 +1,202 @@
+/*
+ Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
+ Copyright (C) 2025 Spacebar and Spacebar Contributors
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+
+import { route } from "@spacebar/api";
+import { Request, Response, Router } from "express";
+import { ReportMenuType, ReportMenuTypeNames } from "../../../schemas/api/reports/ReportMenu";
+import path from "path";
+import { HTTPError } from "lambert-server";
+import { CreateReportSchema } from "../../../schemas/api/reports/CreateReport";
+import { FieldErrors } from "@spacebar/util";
+import fs from "fs";
+
+const router = Router({ mergeParams: true });
+
+console.log("[Server] Registering reporting menu routes...");
+router.get(
+ "/",
+ route({
+ description: "[EXT] Get available reporting menu types.",
+ responses: {
+ 200: {
+ body: "Array",
+ },
+ },
+ }),
+ (req: Request, res: Response) => {
+ res.json(Object.values(ReportMenuTypeNames));
+ },
+);
+
+for (const type of Object.values(ReportMenuTypeNames)) {
+ router.get(
+ `/menu/${type}`,
+ route({
+ description: `Get reporting menu options for ${type} reports.`,
+ query: {
+ variant: { type: "string", required: false, description: "Version variant of the menu to retrieve (max 256 characters, default active)" },
+ },
+ responses: {
+ 200: {
+ body: "ReportingMenuResponse",
+ },
+ 204: {},
+ },
+ }),
+ (req: Request, res: Response) => {
+ // TODO: implement
+ // res.send([] as ReportingMenuResponseSchema);
+ res.sendFile(path.join(__dirname, "..", "..", "..", "..", "assets", "temp_report_menu_responses", `${type}.json`));
+ },
+ );
+ console.log(`[Server] Route /reporting/menu/${type} registered (reports).`);
+ router.post(
+ `/${type}`,
+ route({
+ description: `Get reporting menu options for ${type} reports.`,
+ requestBody: "CreateReportSchema",
+ responses: {
+ 200: {
+ body: "ReportingMenuResponse",
+ },
+ 204: {},
+ },
+ }),
+ (req: Request, res: Response) => {
+ // TODO: implement
+ const body = req.body as CreateReportSchema;
+ if (body.name !== type)
+ throw FieldErrors({
+ name: {
+ message: `Expected report type ${type} but got ${body.name}`,
+ code: "INVALID_REPORT_TYPE",
+ },
+ });
+
+ const menuPath = path.join(__dirname, "..", "..", "..", "..", "assets", "temp_report_menu_responses", `${type}.json`);
+ const menuData = JSON.parse(fs.readFileSync(menuPath, "utf-8"));
+ if (body.version !== menuData.version) {
+ throw FieldErrors({
+ version: {
+ message: `Expected report menu version ${menuData.version} but got ${body.version}`,
+ code: "INVALID_REPORT_MENU_VERSION",
+ },
+ });
+ }
+
+ if (body.variant !== menuData.variant) {
+ throw FieldErrors({
+ variant: {
+ message: `Expected report menu variant ${menuData.variant} but got ${body.variant}`,
+ code: "INVALID_REPORT_MENU_VARIANT",
+ },
+ });
+ }
+
+ if (body.breadcrumbs != menuData.breadcrumbs) {
+ throw FieldErrors({
+ breadcrumbs: {
+ message: `Invalid report menu breadcrumbs.`,
+ code: "INVALID_REPORT_MENU_BREADCRUMBS",
+ },
+ });
+ }
+
+ const validateBreadcrumbs = (currentNode: unknown, breadcrumbs: number[]): boolean => {
+ // navigate via node.children ([name, id][]) according to breadcrumbs
+ let node: { children: [string, number][] } = currentNode as { children: [string, number][] };
+ for (const crumb of breadcrumbs) {
+ if (!node || !node.children || !Array.isArray(node.children)) return false;
+ const nextNode = node.children.find((child: [string, number]) => child[1] === crumb);
+ if (!nextNode) return false;
+ // load next node
+ const nextNodeData = menuData.nodes[crumb];
+ if (!nextNodeData) return false;
+ node = nextNodeData;
+ }
+ return true;
+ };
+
+ if (!validateBreadcrumbs(menuData.nodes[menuData.root_node_id], body.breadcrumbs))
+ throw FieldErrors({
+ breadcrumbs: {
+ message: `Invalid report menu breadcrumbs path.`,
+ code: "INVALID_REPORT_MENU_BREADCRUMBS_PATH",
+ },
+ });
+
+ const requireFields = (obj: CreateReportSchema, fields: string[]) => {
+ const missingFields: string[] = [];
+ for (const field of fields) if (!(field in obj)) missingFields.push(field);
+
+ if (missingFields.length > 0)
+ throw FieldErrors(
+ Object.fromEntries(
+ missingFields.map((f) => [
+ f,
+ {
+ message: `Missing required field ${f}.`,
+ code: "MISSING_FIELD",
+ },
+ ]),
+ ),
+ );
+ };
+
+ const t = Number(Object.entries(ReportMenuTypeNames).find((x) => x[1] === type)?.[0]) as ReportMenuType;
+ // TODO: did i miss anything?
+ switch (t) {
+ case ReportMenuType.GUILD:
+ case ReportMenuType.GUILD_DISCOVERY:
+ requireFields(body, ["guild_id"]);
+ break;
+ case ReportMenuType.GUILD_DIRECTORY_ENTRY:
+ requireFields(body, ["guild_id", "channel_id"]);
+ break;
+ case ReportMenuType.GUILD_SCHEDULED_EVENT:
+ requireFields(body, ["guild_id", "scheduled_event_id"]);
+ break;
+ case ReportMenuType.MESSAGE:
+ requireFields(body, ["channel_id", "message_id"]);
+ // NOTE: is body.guild_id set if the channel is in a guild? is body.user_id ever set????
+ break;
+ case ReportMenuType.STAGE_CHANNEL:
+ requireFields(body, ["channel_id", "guild_id", "stage_instance_id"]);
+ break;
+ case ReportMenuType.FIRST_DM:
+ requireFields(body, ["user_id", "channel_id"]);
+ break;
+ case ReportMenuType.USER:
+ requireFields(body, ["reported_user_id"]);
+ break;
+ case ReportMenuType.APPLICATION:
+ requireFields(body, ["application_id"]);
+ break;
+ case ReportMenuType.WIDGET:
+ requireFields(body, ["user_id", "widget_id"]);
+ break;
+ default:
+ throw new HTTPError("Unknown report menu type", 400);
+ }
+
+ throw new HTTPError("Validation success - implementation TODO", 418);
+ },
+ );
+ console.log(`[Server] Route /reporting/${type} registered (reports).`);
+}
+export default router;
diff --git a/src/api/routes/reporting/menu.ts b/src/api/routes/reporting/menu.ts
deleted file mode 100644
index 3cff084..0000000
--- a/src/api/routes/reporting/menu.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
- Copyright (C) 2025 Spacebar and Spacebar Contributors
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published
- by the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-import { route } from "@spacebar/api";
-import { Request, Response, Router } from "express";
-import { ReportMenuTypeNames } from "../../../schemas/api/reports/ReportMenu";
-import path from "path";
-
-const router = Router({ mergeParams: true });
-
-console.log("[Server] Registering reporting menu routes...");
-router.get(
- "/",
- route({
- description: "[EXT] Get available reporting menu types.",
- responses: {
- 200: {
- body: "Array",
- },
- },
- }),
- (req: Request, res: Response) => {
- res.json(Object.values(ReportMenuTypeNames));
- },
-);
-
-for (const type of Object.values(ReportMenuTypeNames)) {
- console.log(`[Server] Route /reporting/menu/${type} registered (reports).`);
- router.get(
- `/${type}`,
- route({
- description: `Get reporting menu options for ${type} reports.`,
- query: {
- variant: { type: "string", required: false, description: "Version variant of the menu to retrieve (max 256 characters, default active)" },
- },
- responses: {
- 200: {
- // body: "ReportingMenuResponse",
- },
- 204: {},
- },
- }),
- (req: Request, res: Response) => {
- // TODO: implement
- // res.send([] as ReportingMenuResponseSchema);
- res.sendFile(path.join(__dirname, "..", "..", "..", "..", "assets", "temp_report_menu_responses", `${type}.json`));
- },
- );
-}
-export default router;
diff --git a/src/schemas/api/reports/CreateReport.ts b/src/schemas/api/reports/CreateReport.ts
new file mode 100644
index 0000000..7c5606d
--- /dev/null
+++ b/src/schemas/api/reports/CreateReport.ts
@@ -0,0 +1,36 @@
+/*
+ Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
+ Copyright (C) 2025 Spacebar and Spacebar Contributors
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+
+// TODO: check
+export interface CreateReportSchema {
+ version: string;
+ variant: string;
+ name: string;
+ language: string;
+ breadcrumbs: number[];
+ elements?: { [key: string]: string[] };
+ channel_id?: string; // snowflake
+ message_id?: string; // snowflake
+ guild_id?: string; // snowflake
+ stage_instance_id?: string; // snowflake
+ guild_scheduled_event_id?: string; // snowflake
+ reported_user_id?: string; // snowflake
+ application_id?: string; // snowflake
+ user_id?: string; // snowflake
+ widget_id?: string; // snowflake
+}
diff --git a/src/schemas/uncategorised/PreloadMessagesRequestSchema.ts b/src/schemas/uncategorised/PreloadMessagesRequestSchema.ts
index 1dc1eec..5705e7f 100644
--- a/src/schemas/uncategorised/PreloadMessagesRequestSchema.ts
+++ b/src/schemas/uncategorised/PreloadMessagesRequestSchema.ts
@@ -17,5 +17,6 @@
*/
export interface PreloadMessagesRequestSchema {
- channels: string[];
+ channels?: string[];
+ channel_ids?: string[];
}
diff --git a/src/util/entities/BaseClass.ts b/src/util/entities/BaseClass.ts
index 5338837..1aaf147 100644
--- a/src/util/entities/BaseClass.ts
+++ b/src/util/entities/BaseClass.ts
@@ -16,7 +16,7 @@
along with this program. If not, see .
*/
-import { BaseEntity, BeforeInsert, BeforeUpdate, FindOptionsWhere, InsertResult, ObjectIdColumn, ObjectLiteral, PrimaryColumn } from "typeorm";
+import { BaseEntity, BeforeInsert, BeforeUpdate, Column, ColumnOptions, FindOptionsWhere, InsertResult, ObjectIdColumn, ObjectLiteral, PrimaryColumn } from "typeorm";
import { Snowflake } from "../util/Snowflake";
import { getDatabase } from "../util/Database";
import { OrmUtils } from "../imports/OrmUtils";
@@ -83,3 +83,5 @@
if (!this.id) this.id = Snowflake.generate();
}
}
+
+export const ArrayColumn = (opts: ColumnOptions) => (process.env.DATABASE?.startsWith("postgres") ? Column({ ...opts, array: true }) : Column({ ...opts, type: "simple-array" }));
diff --git a/src/util/entities/ReportMenu.ts b/src/util/entities/ReportMenu.ts
new file mode 100644
index 0000000..26d66ad
--- /dev/null
+++ b/src/util/entities/ReportMenu.ts
@@ -0,0 +1,43 @@
+/*
+ Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
+ Copyright (C) 2024 Spacebar and Spacebar Contributors
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+
+import { BaseClass, BaseClassWithoutId } from "./BaseClass";
+import { Entity, JoinColumn, ManyToOne, Column } from "typeorm";
+import { User } from "./User";
+import { AutomodAction, AutomodRuleActionType, AutomodRuleEventType, AutomodRuleTriggerMetadata, AutomodRuleTriggerType } from "@spacebar/schemas";
+import { ReportMenuType } from "../../schemas/api/reports/ReportMenu";
+
+@Entity({
+ name: "report_menus",
+})
+export class ReportMenu extends BaseClass {
+ @Column()
+ type: ReportMenuType;
+
+ @Column()
+ variant: string;
+
+ @Column()
+ isCurrent: boolean;
+
+ @Column({ nullable: true })
+ inherits?: string;
+
+ @Column({ type: "jsonb" })
+ content: unknown;
+}