feat: interactions (incomplete)

This commit is contained in:
CyberL1
2025-10-17 13:01:36 +02:00
committed by Rory&
parent 863e7f6ff3
commit 81d4f1f310
15 changed files with 352 additions and 16 deletions
+3 -7
View File
@@ -32,6 +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/,
// Public information endpoints
"GET /ping",
"GET /gateway",
@@ -72,11 +73,7 @@ declare global {
}
}
export async function Authentication(
req: Request,
res: Response,
next: NextFunction,
) {
export async function Authentication(req: Request, res: Response, next: NextFunction) {
if (req.method === "OPTIONS") return res.sendStatus(204);
const url = req.url.replace(API_PREFIX, "");
if (
@@ -104,8 +101,7 @@ export async function Authentication(
})
)
return next();
if (!req.headers.authorization)
return next(new HTTPError("Missing Authorization Header", 401));
if (!req.headers.authorization) return next(new HTTPError("Missing Authorization Header", 401));
try {
const { decoded, user } = await checkToken(req.headers.authorization);
@@ -0,0 +1,90 @@
/*
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 <https://www.gnu.org/licenses/>.
*/
import { InteractionCallbackSchema, InteractionCallbackType, MessageType } from "@spacebar/schemas";
import { route } from "@spacebar/api";
import { Request, Response, Router } from "express";
import { ApplicationCommand, emitEvent, InteractionSuccessEvent, Message, MessageCreateEvent, pendingInteractions } from "@spacebar/util";
const router = Router({ mergeParams: true });
router.post("/", route({}), async (req: Request, res: Response) => {
const body = req.body as InteractionCallbackSchema;
const interactionId = req.params.interaction_id;
const interaction = pendingInteractions.get(req.params.interaction_id);
if (!interaction) {
return;
}
clearTimeout(interaction.timeout);
emitEvent({
event: "INTERACTION_SUCCESS",
user_id: interaction?.userId,
data: {
id: interactionId,
nonce: interaction?.nonce,
},
} as InteractionSuccessEvent);
switch (body.type) {
case InteractionCallbackType.PONG:
// TODO
break;
case InteractionCallbackType.ACKNOWLEDGE:
// Deprected
break;
case InteractionCallbackType.CHANNEL_MESSAGE:
// TODO
break;
case InteractionCallbackType.CHANNEL_MESSAGE_WITH_SOURCE:
// TODO
break;
case InteractionCallbackType.DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE:
// TODO
break;
case InteractionCallbackType.DEFERRED_UPDATE_MESSAGE:
// TODO
break;
case InteractionCallbackType.UPDATE_MESSAGE:
// TODO
break;
case InteractionCallbackType.APPLICATION_COMMAND_AUTOCOMPLETE_RESULT:
// TODO
break;
case InteractionCallbackType.MODAL:
// TODO
break;
case InteractionCallbackType.PREMIUM_REQUIRED:
// Deprecated
break;
case InteractionCallbackType.IFRAME_MODAL:
// TODO
break;
case InteractionCallbackType.LAUNCH_ACTIVITY:
// TODO
break;
}
pendingInteractions.delete(interactionId);
res.sendStatus(204);
});
export default router;
+82
View File
@@ -0,0 +1,82 @@
/*
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 <https://www.gnu.org/licenses/>.
*/
import { randomBytes } from "crypto";
import { InteractionSchema } from "@spacebar/schemas";
import { route } from "@spacebar/api";
import { Request, Response, Router } from "express";
import { Application, ApplicationCommand, emitEvent, InteractionCreateEvent, InteractionFailureEvent, Snowflake } from "@spacebar/util";
import { pendingInteractions } from "../../../util/imports/Interactions";
const router = Router({ mergeParams: true });
router.post("/", route({}), async (req: Request, res: Response) => {
const body = req.body as InteractionSchema;
const interactionId = Snowflake.generate();
const interactionToken = randomBytes(24).toString("base64url");
emitEvent({
event: "INTERACTION_CREATE",
user_id: req.user_id,
data: {
id: interactionId,
nonce: body.nonce,
},
} as InteractionCreateEvent);
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,
},
} as InteractionCreateEvent);
const interactionTimeout = setTimeout(() => {
emitEvent({
event: "INTERACTION_FAILURE",
user_id: req.user_id,
data: {
id: interactionId,
nonce: body.nonce,
reason_code: 2, // when types are done: InteractionFailureReason.TIMEOUT,
},
} as InteractionFailureEvent);
}, 3000);
pendingInteractions.set(interactionId, {
timeout: interactionTimeout,
nonce: body.nonce,
userId: req.user_id,
guildId: body.guild_id,
channelId: body.channel_id,
type: body.type,
commandName: body.data.name,
});
res.sendStatus(204);
});
export default router;