diff --git a/.idea/runConfigurations/Start_API.xml b/.idea/runConfigurations/Start_API.xml
index edb214cf..df7b7470 100644
--- a/.idea/runConfigurations/Start_API.xml
+++ b/.idea/runConfigurations/Start_API.xml
@@ -5,7 +5,7 @@
-
+
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 16f2f173..5e704d48 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -43,50 +43,50 @@
- {
- "keyToString": {
- "NIXITCH_NIXPKGS_CONFIG": "/etc/nix/nixpkgs-config.nix",
- "NIXITCH_NIX_CONF_DIR": "",
- "NIXITCH_NIX_OTHER_STORES": "",
- "NIXITCH_NIX_PATH": "/home/Rory/.nix-defexpr/channels:nixpkgs=/nix/store/wb6agba4kfsxpbnb5hzlq58vkjzvbsk6-source",
- "NIXITCH_NIX_PROFILES": "/run/current-system/sw /nix/var/nix/profiles/default /etc/profiles/per-user/Rory /home/Rory/.local/state/nix/profile /nix/profile /home/Rory/.nix-profile",
- "NIXITCH_NIX_REMOTE": "",
- "NIXITCH_NIX_USER_PROFILE_DIR": "/nix/var/nix/profiles/per-user/Rory",
- "Node.js.Server.ts.executor": "Debug",
- "RunOnceActivity.ShowReadmeOnStart": "true",
- "RunOnceActivity.git.unshallow": "true",
- "javascript.nodejs.core.library.configured.version": "24.8.0",
- "javascript.nodejs.core.library.typings.version": "24.7.0",
- "last_opened_file_path": "/home/Rory/git/spacebar/server-master/src/schemas/api/guilds",
- "node.js.detected.package.eslint": "true",
- "node.js.selected.package.eslint": "(autodetect)",
- "node.js.selected.package.tslint": "(autodetect)",
- "nodejs_interpreter_path": "node",
- "nodejs_package_manager_path": "npm",
- "npm.Start API.executor": "Run",
- "npm.Start CDN.executor": "Run",
- "npm.Start Gateway.executor": "Run",
- "npm.build.executor": "Run",
- "npm.start.executor": "Debug",
- "prettierjs.PrettierConfiguration.Package": "/home/Rory/git/spacebar/server-master/node_modules/prettier",
- "settings.editor.selected.configurable": "preferences.pluginManager",
- "ts.external.directory.path": "/home/Rory/git/spacebar/server-master/node_modules/typescript/lib"
+
+}]]>
+
+
-
-
@@ -103,7 +103,7 @@
-
+
diff --git a/assets/openapi.json b/assets/openapi.json
index e6bf2c95..13441257 100644
Binary files a/assets/openapi.json and b/assets/openapi.json differ
diff --git a/assets/schemas.json b/assets/schemas.json
index 13f530e9..f0abe323 100644
Binary files a/assets/schemas.json and b/assets/schemas.json differ
diff --git a/src/schemas/api/bots/ApplicationCommandSchema.ts b/src/schemas/api/bots/ApplicationCommandSchema.ts
new file mode 100644
index 00000000..23c9722c
--- /dev/null
+++ b/src/schemas/api/bots/ApplicationCommandSchema.ts
@@ -0,0 +1,57 @@
+import { ApplicationCommandOption, Snowflake } from "@spacebar/schemas";
+
+export interface ApplicationCommandSchema {
+ id?: Snowflake;
+ type?: ApplicationCommandType;
+ application_id: Snowflake;
+ guild_id?: Snowflake;
+ name: string;
+ name_localizations?: Record;
+ name_localized?: string | null;
+ description: string;
+ description_localizations?: Record;
+ description_localized?: string | null;
+ options?: ApplicationCommandOption[];
+ default_member_permissions: string | null;
+ /*
+ * @deprecated
+ */
+ dm_permission?: boolean;
+ permissions?: ApplicationCommandIndexPermissions;
+ nsfw?: boolean;
+ integration_types?: ApplicationIntegrationType[];
+ global_popularity_rank?: number;
+ contexts?: InteractionContextType[];
+ version: Snowflake;
+ handler?: ApplicationCommandHandlerType;
+}
+
+export interface ApplicationCommandIndexPermissions {
+ user?: boolean;
+ roles?: Record;
+ channels?: Record;
+}
+
+export enum ApplicationCommandType {
+ CHAT_INPUT = 1,
+ USER = 2,
+ MESSAGE = 3,
+ PRIMARY_ENTRY_POINT = 4,
+}
+
+export enum ApplicationCommandHandlerType {
+ APP_HANDLER = 1,
+ DISCORD_LAUNCH_ACTIVITY = 2,
+ APP_HANDLER_LAUNCH_ACTIVITY = 3,
+}
+
+export enum InteractionContextType {
+ GUILD = 0,
+ BOT_DM = 1,
+ PRIVATE_CHANNEL = 2,
+}
+
+export enum ApplicationIntegrationType {
+ GUILD_INSTALL = 0,
+ USER_INSTALL = 1,
+}
diff --git a/src/schemas/api/bots/index.ts b/src/schemas/api/bots/index.ts
new file mode 100644
index 00000000..71e2047b
--- /dev/null
+++ b/src/schemas/api/bots/index.ts
@@ -0,0 +1,18 @@
+/*
+ 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 .
+*/
+export * from "./ApplicationCommandSchema";
diff --git a/src/schemas/api/index.ts b/src/schemas/api/index.ts
index 06ee4ea5..b0215da1 100644
--- a/src/schemas/api/index.ts
+++ b/src/schemas/api/index.ts
@@ -15,6 +15,7 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
+export * from "./bots";
export * from "./channels";
export * from "./developers";
export * from "./guilds";
diff --git a/src/util/entities/ApplicationCommand.ts b/src/util/entities/ApplicationCommand.ts
new file mode 100644
index 00000000..8506d87b
--- /dev/null
+++ b/src/util/entities/ApplicationCommand.ts
@@ -0,0 +1,88 @@
+/*
+ Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
+ Copyright (C) 2023 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 { Column, Entity } from "typeorm";
+import { BaseClass } from "./BaseClass";
+import {
+ ApplicationCommandHandlerType,
+ ApplicationCommandOption,
+ ApplicationCommandIndexPermissions,
+ ApplicationCommandType,
+ Snowflake,
+ ApplicationIntegrationType,
+ InteractionContextType,
+} from "@spacebar/schemas";
+
+@Entity({
+ name: "application_commands",
+})
+export class ApplicationCommand extends BaseClass {
+ @Column({ default: ApplicationCommandType.CHAT_INPUT })
+ type?: ApplicationCommandType;
+
+ @Column()
+ application_id: Snowflake;
+
+ @Column({ nullable: true })
+ guild_id?: Snowflake;
+
+ @Column()
+ name: string;
+
+ @Column({ nullable: true, type: "simple-json" })
+ name_localizations?: Record;
+
+ @Column()
+ description: string;
+
+ @Column({ nullable: true, type: "simple-json" })
+ description_localizations?: Record;
+
+ @Column({ nullable: true, type: "simple-json" })
+ options?: ApplicationCommandOption[];
+
+ @Column({ nullable: true, type: String })
+ default_member_permissions: string | null;
+
+ /*
+ * @deprecated
+ */
+ @Column({ default: true })
+ dm_permission?: boolean;
+
+ @Column({ nullable: true, type: "simple-json" })
+ permissions?: ApplicationCommandIndexPermissions;
+
+ @Column({ default: false })
+ nsfw?: boolean;
+
+ @Column({ nullable: true, type: "simple-json" })
+ integration_types?: ApplicationIntegrationType[];
+
+ @Column({ default: 0 })
+ global_popularity_rank?: number;
+
+ @Column({ nullable: true, type: "simple-json" })
+ contexts?: InteractionContextType[];
+
+ @Column({ default: 0 })
+ version: Snowflake;
+
+ @Column({ default: 0 })
+ handler?: ApplicationCommandHandlerType;
+}
diff --git a/src/util/entities/index.ts b/src/util/entities/index.ts
index 38190a44..11494d00 100644
--- a/src/util/entities/index.ts
+++ b/src/util/entities/index.ts
@@ -17,6 +17,7 @@
*/
export * from "./Application";
+export * from "./ApplicationCommand";
export * from "./Attachment";
export * from "./AuditLog";
export * from "./AutomodRule";
diff --git a/src/util/migration/postgres/1760622755598-ApplicationCommands.ts b/src/util/migration/postgres/1760622755598-ApplicationCommands.ts
new file mode 100644
index 00000000..f1a10737
--- /dev/null
+++ b/src/util/migration/postgres/1760622755598-ApplicationCommands.ts
@@ -0,0 +1,14 @@
+import { MigrationInterface, QueryRunner } from "typeorm";
+
+export class ApplicationCommands1760622755598 implements MigrationInterface {
+ name = 'ApplicationCommands1760622755598'
+
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`CREATE TABLE "application_commands" ("id" character varying NOT NULL, "type" integer NOT NULL DEFAULT '1', "application_id" character varying NOT NULL, "guild_id" character varying, "name" character varying NOT NULL, "name_localizations" text, "description" character varying NOT NULL, "description_localizations" text, "options" text, "default_member_permissions" character varying, "dm_permission" boolean NOT NULL DEFAULT true, "permissions" text, "nsfw" boolean NOT NULL DEFAULT false, "integration_types" text, "global_popularity_rank" integer NOT NULL DEFAULT '0', "contexts" text, "version" character varying NOT NULL DEFAULT '0', "handler" integer NOT NULL DEFAULT '0', CONSTRAINT "PK_0f73c2f025989c407947e1f75fe" PRIMARY KEY ("id"))`);
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`DROP TABLE "application_commands"`);
+ }
+
+}
diff --git a/src/util/migration/postgres/1760622755599-ReconcileMigrationAttempts.ts b/src/util/migration/postgres/1760622755599-ReconcileMigrationAttempts.ts
new file mode 100644
index 00000000..0138817d
--- /dev/null
+++ b/src/util/migration/postgres/1760622755599-ReconcileMigrationAttempts.ts
@@ -0,0 +1,74 @@
+// import { MigrationInterface, QueryRunner } from "typeorm";
+//
+// export class ApplicationCommands1760622755598 implements MigrationInterface {
+// name = 'ApplicationCommands1760622755598'
+//
+// public async up(queryRunner: QueryRunner): Promise {
+// await queryRunner.query(`ALTER TABLE "webhooks" DROP CONSTRAINT "fk_d64f38834fa676f6caa4786ddd6"`);
+// await queryRunner.query(`ALTER TABLE "webhooks" DROP COLUMN "source_channel_id"`);
+// await queryRunner.query(`ALTER TABLE "webhooks" ADD "source_channel_id" character varying`);
+// await queryRunner.query(`ALTER TABLE "messages" DROP COLUMN "username"`);
+// await queryRunner.query(`ALTER TABLE "messages" ADD "username" character varying`);
+// await queryRunner.query(`ALTER TABLE "messages" DROP COLUMN "avatar"`);
+// await queryRunner.query(`ALTER TABLE "messages" ADD "avatar" character varying`);
+// await queryRunner.query(`ALTER TABLE "channels" ALTER COLUMN "default_thread_rate_limit_per_user" DROP NOT NULL`);
+// await queryRunner.query(`ALTER TABLE "sessions" ALTER COLUMN "client_status" SET NOT NULL`);
+// await queryRunner.query(`ALTER TABLE "user_settings" ALTER COLUMN "friend_discovery_flags" DROP DEFAULT`);
+// await queryRunner.query(`ALTER TABLE "user_settings" ALTER COLUMN "view_nsfw_guilds" DROP DEFAULT`);
+// await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "flags"`);
+// await queryRunner.query(`ALTER TABLE "users" ADD "flags" integer NOT NULL`);
+// await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "public_flags"`);
+// await queryRunner.query(`ALTER TABLE "users" ADD "public_flags" integer NOT NULL`);
+// await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "purchased_flags"`);
+// await queryRunner.query(`ALTER TABLE "users" ADD "purchased_flags" integer NOT NULL`);
+// await queryRunner.query(`ALTER TABLE "team_members" DROP COLUMN "role"`);
+// await queryRunner.query(`ALTER TABLE "team_members" ADD "role" character varying NOT NULL`);
+// await queryRunner.query(`ALTER TABLE "applications" DROP COLUMN "guild_id"`);
+// await queryRunner.query(`ALTER TABLE "applications" ADD "guild_id" character varying`);
+// await queryRunner.query(`ALTER TABLE "applications" DROP COLUMN "custom_install_url"`);
+// await queryRunner.query(`ALTER TABLE "applications" ADD "custom_install_url" character varying`);
+// await queryRunner.query(`ALTER TABLE "categories" DROP COLUMN "icon"`);
+// await queryRunner.query(`ALTER TABLE "categories" ADD "icon" character varying`);
+// await queryRunner.query(`ALTER TABLE "user_settings_protos" DROP COLUMN "userSettings"`);
+// await queryRunner.query(`ALTER TABLE "user_settings_protos" ADD "userSettings" character varying`);
+// await queryRunner.query(`ALTER TABLE "user_settings_protos" DROP COLUMN "frecencySettings"`);
+// await queryRunner.query(`ALTER TABLE "user_settings_protos" ADD "frecencySettings" character varying`);
+// await queryRunner.query(`ALTER TABLE "webhooks" ADD CONSTRAINT "FK_4495b7032a33c6b8b605d030398" FOREIGN KEY ("source_channel_id") REFERENCES "channels"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
+// await queryRunner.query(`ALTER TABLE "applications" ADD CONSTRAINT "FK_e5bf78cdbbe9ba91062d74c5aba" FOREIGN KEY ("guild_id") REFERENCES "guilds"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
+// }
+//
+// public async down(queryRunner: QueryRunner): Promise {
+// await queryRunner.query(`ALTER TABLE "applications" DROP CONSTRAINT "FK_e5bf78cdbbe9ba91062d74c5aba"`);
+// await queryRunner.query(`ALTER TABLE "webhooks" DROP CONSTRAINT "FK_4495b7032a33c6b8b605d030398"`);
+// await queryRunner.query(`ALTER TABLE "user_settings_protos" DROP COLUMN "frecencySettings"`);
+// await queryRunner.query(`ALTER TABLE "user_settings_protos" ADD "frecencySettings" text`);
+// await queryRunner.query(`ALTER TABLE "user_settings_protos" DROP COLUMN "userSettings"`);
+// await queryRunner.query(`ALTER TABLE "user_settings_protos" ADD "userSettings" text`);
+// await queryRunner.query(`ALTER TABLE "categories" DROP COLUMN "icon"`);
+// await queryRunner.query(`ALTER TABLE "categories" ADD "icon" text`);
+// await queryRunner.query(`ALTER TABLE "applications" DROP COLUMN "custom_install_url"`);
+// await queryRunner.query(`ALTER TABLE "applications" ADD "custom_install_url" text`);
+// await queryRunner.query(`ALTER TABLE "applications" DROP COLUMN "guild_id"`);
+// await queryRunner.query(`ALTER TABLE "applications" ADD "guild_id" text`);
+// await queryRunner.query(`ALTER TABLE "team_members" DROP COLUMN "role"`);
+// await queryRunner.query(`ALTER TABLE "team_members" ADD "role" text NOT NULL`);
+// await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "purchased_flags"`);
+// await queryRunner.query(`ALTER TABLE "users" ADD "purchased_flags" bigint NOT NULL`);
+// await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "public_flags"`);
+// await queryRunner.query(`ALTER TABLE "users" ADD "public_flags" bigint NOT NULL`);
+// await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "flags"`);
+// await queryRunner.query(`ALTER TABLE "users" ADD "flags" bigint NOT NULL`);
+// await queryRunner.query(`ALTER TABLE "user_settings" ALTER COLUMN "view_nsfw_guilds" SET DEFAULT true`);
+// await queryRunner.query(`ALTER TABLE "user_settings" ALTER COLUMN "friend_discovery_flags" SET DEFAULT '0'`);
+// await queryRunner.query(`ALTER TABLE "sessions" ALTER COLUMN "client_status" DROP NOT NULL`);
+// await queryRunner.query(`ALTER TABLE "channels" ALTER COLUMN "default_thread_rate_limit_per_user" SET NOT NULL`);
+// await queryRunner.query(`ALTER TABLE "messages" DROP COLUMN "avatar"`);
+// await queryRunner.query(`ALTER TABLE "messages" ADD "avatar" text`);
+// await queryRunner.query(`ALTER TABLE "messages" DROP COLUMN "username"`);
+// await queryRunner.query(`ALTER TABLE "messages" ADD "username" text`);
+// await queryRunner.query(`ALTER TABLE "webhooks" DROP COLUMN "source_channel_id"`);
+// await queryRunner.query(`ALTER TABLE "webhooks" ADD "source_channel_id" character varying(255) DEFAULT NULL`);
+// await queryRunner.query(`ALTER TABLE "webhooks" ADD CONSTRAINT "fk_d64f38834fa676f6caa4786ddd6" FOREIGN KEY ("source_channel_id") REFERENCES "channels"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
+// }
+//
+// }