diff --git a/src/api/middlewares/Authentication.ts b/src/api/middlewares/Authentication.ts
index 83b98925..0821f811 100644
--- a/src/api/middlewares/Authentication.ts
+++ b/src/api/middlewares/Authentication.ts
@@ -32,7 +32,7 @@ export const NO_AUTHORIZATION_ROUTES = [
"GET /invites/",
// Routes with a seperate auth system
/^(POST|HEAD|GET|PATCH|DELETE) \/webhooks\/\d+\/\w+\/?/, // no token requires auth
- // /^POST \/interactions\/\d+\/\w+\/callback/, // This is marked as Unauthoricated route on https://discord.food. But when it is then not all requests have req.user_id?
+ /^POST \/interactions\/\d+\/[A-Za-z0-9_-]+\/callback/,
// Public information endpoints
"GET /ping",
"GET /gateway",
diff --git a/src/api/routes/interactions/#interaction_id/#interaction_token/callback.ts b/src/api/routes/interactions/#interaction_id/#interaction_token/callback.ts
index 1860e0b3..77f0112f 100644
--- a/src/api/routes/interactions/#interaction_id/#interaction_token/callback.ts
+++ b/src/api/routes/interactions/#interaction_id/#interaction_token/callback.ts
@@ -55,13 +55,13 @@ router.post("/", route({}), async (req: Request, res: Response) => {
// TODO
break;
case InteractionCallbackType.CHANNEL_MESSAGE_WITH_SOURCE: {
- const message = Message.create({
+ const message = await Message.createWithDefaults({
type: MessageType.APPLICATION_COMMAND,
timestamp: new Date(),
- application_id: req.user_id,
+ application_id: interaction.applicationId,
guild_id: interaction.guildId,
channel_id: interaction.channelId,
- author_id: req.user_id,
+ author_id: interaction.applicationId,
content: body.data.content,
tts: body.data.tts,
embeds: body.data.embeds || [],
@@ -69,7 +69,7 @@ router.post("/", route({}), async (req: Request, res: Response) => {
poll: body.data.poll,
flags: body.data.flags,
reactions: [],
- // webhook_id: req.user_id, // This one requires a webhook to be created first
+ // webhook_id: interaction.applicationId, // This one requires a webhook to be created first
interaction: {
id: interactionId,
name: interaction.commandName,
@@ -98,9 +98,9 @@ router.post("/", route({}), async (req: Request, res: Response) => {
event: "MESSAGE_CREATE",
channel_id: interaction.channelId,
data: {
- application_id: message.application_id,
+ application_id: interaction.applicationId,
attachments: message.attachments,
- author: (await User.findOneOrFail({ where: { id: message.author_id } })).toPublicUser(),
+ author: message.author?.toPublicUser(),
channel_id: message.channel_id,
channel_type: 0,
components: message.components,
@@ -131,7 +131,7 @@ router.post("/", route({}), async (req: Request, res: Response) => {
timestamp: message.timestamp,
tss: message.tts,
type: message.type,
- webhook_id: req.user_id,
+ webhook_id: interaction.applicationId,
} as MessageCreateSchema,
} as MessageCreateEvent);
break;
diff --git a/src/api/routes/interactions/index.ts b/src/api/routes/interactions/index.ts
index 8789b291..05571ace 100644
--- a/src/api/routes/interactions/index.ts
+++ b/src/api/routes/interactions/index.ts
@@ -20,8 +20,9 @@ import { randomBytes } from "crypto";
import { InteractionSchema } from "@spacebar/schemas";
import { route } from "@spacebar/api";
import { Request, Response, Router } from "express";
-import { emitEvent, InteractionCreateEvent, InteractionFailureEvent, Snowflake } from "@spacebar/util";
-import { pendingInteractions } from "../../../util/imports/Interactions";
+import { emitEvent, Guild, InteractionCreateEvent, InteractionFailureEvent, InteractionType, Member, Message, Snowflake, User } from "@spacebar/util";
+import { pendingInteractions } from "@spacebar/util/imports/Interactions";
+import { InteractionCreateSchema } from "@spacebar/schemas/api/bots/InteractionCreateSchema";
const router = Router({ mergeParams: true });
@@ -40,18 +41,56 @@ router.post("/", route({}), async (req: Request, res: Response) => {
},
} as InteractionCreateEvent);
+ const user = await User.findOneOrFail({ where: { id: req.user_id } });
+
+ const interactionData: Partial<InteractionCreateSchema> = {
+ id: interactionId,
+ application_id: body.application_id,
+ channel_id: body.channel_id,
+ type: body.type,
+ token: interactionToken,
+ version: 1,
+ app_permissions: "0", // TODO: add this later
+ entitlements: [],
+ authorizing_integration_owners: { "0": req.user_id },
+ context: 0, // TODO: add this later
+ attachment_size_limit: 0, // TODO: add this later
+ };
+
+ if (body.type === InteractionType.ApplicationCommand || body.data.type === InteractionType.MessageComponent || body.data.type === InteractionType.ModalSubmit) {
+ interactionData.data = body.data;
+ }
+
+ if (body.type != InteractionType.Ping) {
+ interactionData.locale = user?.settings?.locale;
+ }
+
+ if (body.guild_id) {
+ interactionData.guild_id = body.guild_id;
+
+ const guild = await Guild.findOneOrFail({ where: { id: body.guild_id } });
+ const member = await Member.findOneOrFail({ where: { guild_id: body.guild_id, id: req.user_id }, relations: ["user"] });
+
+ interactionData.guild = {
+ id: guild.id,
+ features: guild.features,
+ locale: guild.preferred_locale!,
+ };
+
+ interactionData.guild_locale = guild.preferred_locale;
+ interactionData.member = member.toPublicMember();
+ } else {
+ interactionData.user = user.toPublicUser();
+ }
+
+ if (body.type === InteractionType.MessageComponent || body.data.type === InteractionType.ModalSubmit) {
+ interactionData.message = await Message.findOneOrFail({ where: { id: body.message_id } });
+ }
+
emitEvent({
event: "INTERACTION_CREATE",
user_id: body.application_id,
- data: {
- channel_id: body.channel_id,
- guild_id: body.guild_id,
- id: interactionId,
- member_id: req.user_id,
- token: interactionToken,
- type: body.type,
- nonce: body.nonce,
- },
+ data: interactionData,
} as InteractionCreateEvent);
const interactionTimeout = setTimeout(() => {
@@ -69,6 +108,7 @@ router.post("/", route({}), async (req: Request, res: Response) => {
pendingInteractions.set(interactionId, {
timeout: interactionTimeout,
nonce: body.nonce,
+ applicationId: body.application_id,
userId: req.user_id,
guildId: body.guild_id,
channelId: body.channel_id,
diff --git a/src/schemas/api/bots/InteractionCallbackSchema.ts b/src/schemas/api/bots/InteractionCallbackSchema.ts
index bbf68e80..9bc27217 100644
--- a/src/schemas/api/bots/InteractionCallbackSchema.ts
+++ b/src/schemas/api/bots/InteractionCallbackSchema.ts
@@ -1,7 +1,7 @@
-import { MessageCreateSchema } from "@spacebar/schemas";
+import { Message } from "@spacebar/util";
import { InteractionCallbackType } from "./InteractionCallbackType";
export interface InteractionCallbackSchema {
type: InteractionCallbackType;
- data: MessageCreateSchema;
+ data: Message;
}
diff --git a/src/schemas/api/bots/InteractionCreateSchema.ts b/src/schemas/api/bots/InteractionCreateSchema.ts
new file mode 100644
index 00000000..4bcf09ab
--- /dev/null
+++ b/src/schemas/api/bots/InteractionCreateSchema.ts
@@ -0,0 +1,32 @@
+import { PublicMember, PublicUser, Snowflake } from "@spacebar/schemas";
+import { Channel, InteractionType, Message } from "@spacebar/util";
+
+export interface InteractionCreateSchema {
+ version: number; // TODO: types?
+ id: Snowflake;
+ application_id: Snowflake;
+ type: InteractionType;
+ token: string;
+ data?: object; // TODO: types?
+ guild?: InteractionGuild;
+ guild_id?: Snowflake;
+ guild_locale?: string;
+ channel?: Channel; // TODO: is this right?
+ channel_id?: Snowflake;
+ member?: PublicMember; // TODO: is this right?
+ user?: PublicUser; // TODO: is this right?
+ locale?: string;
+ message?: Message; // TODO: is this right?
+ app_permissions: string;
+ entitlements?: object[]; // TODO: types?
+ entitlement_sku_ids?: Snowflake[]; // DEPRECATED
+ authorizing_integration_owners?: Record<number, Snowflake>; // TODO: types?
+ context?: number;
+ attachment_size_limit: number;
+}
+
+interface InteractionGuild {
+ id: Snowflake;
+ features: string[];
+ locale: string;
+}
diff --git a/src/schemas/api/bots/index.ts b/src/schemas/api/bots/index.ts
index b9d0b396..a08972f1 100644
--- a/src/schemas/api/bots/index.ts
+++ b/src/schemas/api/bots/index.ts
@@ -20,6 +20,7 @@ export * from "./ApplicationCommandSchema";
export * from "./InteractionSchema";
export * from "./InteractionCallbackSchema";
export * from "./InteractionCallbackType";
+export * from "./InteractionCreateSchema";
export * from "./SendableApplicationCommandDataSchema";
export * from "./SendableMessageComponentDataSchema";
export * from "./SendableModalSubmitDataSchema";
diff --git a/src/util/imports/Interactions.ts b/src/util/imports/Interactions.ts
index dd3ca515..49ec4933 100644
--- a/src/util/imports/Interactions.ts
+++ b/src/util/imports/Interactions.ts
@@ -21,6 +21,7 @@ import { InteractionType, Snowflake } from "@spacebar/util";
interface PendingInteraction {
timeout: NodeJS.Timeout;
+ applicationId: string;
userId: string;
channelId?: string;
guildId?: string;
diff --git a/src/util/interfaces/Interaction.ts b/src/util/interfaces/Interaction.ts
index 29fc931d..09068df8 100644
--- a/src/util/interfaces/Interaction.ts
+++ b/src/util/interfaces/Interaction.ts
@@ -30,9 +30,11 @@ export interface Interaction {
}
export enum InteractionType {
- SelfCommand = 0,
Ping = 1,
ApplicationCommand = 2,
+ MessageComponent = 3,
+ ApplicationCommandAutocomplete = 4,
+ ModalSubmit = 5,
}
export enum InteractionResponseType {
|