mirror of
https://github.com/spacebarchat/server.git
synced 2026-08-28 13:54:08 +00:00
final pk fixes
This commit is contained in:
@@ -3,7 +3,8 @@
|
||||
"MessageInteractionSchema"
|
||||
],
|
||||
"includeRe": [
|
||||
"^MessageComponentType\\..*"
|
||||
"^MessageComponentType\\..*",
|
||||
"^InteractionCallbackType\\..*"
|
||||
],
|
||||
"manual": [
|
||||
"DefaultSchema",
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { BaseMessageComponents, InteractionCallbackSchema, InteractionCallbackType, MessageType } from "@spacebar/schemas";
|
||||
import { BaseMessageComponents, InteractionCallbackSchema, InteractionCallbacksSchema, InteractionCallbackType, InteractionFailureReason, MessageType } from "@spacebar/schemas";
|
||||
import { handleComps, route, sendMessage } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { Config, emitEvent, InteractionSuccessEvent, Message, MessageUpdateEvent, pendingInteractions, User, InteractionFailureEvent } from "@spacebar/util";
|
||||
@@ -24,40 +24,46 @@ import { HTTPError } from "#util/util/lambert-server";
|
||||
|
||||
const router = Router({ mergeParams: true });
|
||||
|
||||
router.post("/", route({}), async (req: Request, res: Response) => {
|
||||
const body = req.body as InteractionCallbackSchema;
|
||||
router.post(
|
||||
"/",
|
||||
route({
|
||||
stripNulls: true,
|
||||
requestBody: "InteractionCallbacksSchema",
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const body = req.body as InteractionCallbacksSchema;
|
||||
|
||||
const interactionId = req.params.interaction_id as string;
|
||||
const interaction = pendingInteractions.get(req.params.interaction_id);
|
||||
const interactionId = req.params.interaction_id as string;
|
||||
const interaction = pendingInteractions.get(req.params.interaction_id);
|
||||
|
||||
if (!interaction) {
|
||||
return;
|
||||
}
|
||||
if (!interaction) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearTimeout(interaction.timeout);
|
||||
clearTimeout(interaction.timeout);
|
||||
|
||||
await emitEvent({
|
||||
event: "INTERACTION_SUCCESS",
|
||||
user_id: interaction?.userId,
|
||||
data: {
|
||||
id: interactionId,
|
||||
nonce: interaction?.nonce,
|
||||
},
|
||||
} as InteractionSuccessEvent);
|
||||
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: {
|
||||
const user = await User.findOneOrFail({ where: { id: interaction.userId } });
|
||||
/*
|
||||
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: {
|
||||
const user = await User.findOneOrFail({ where: { id: interaction.userId } });
|
||||
/*
|
||||
const files = (req.files as Express.Multer.File[]) ?? [];
|
||||
//I don't think traditional attachments are allowed anyways
|
||||
const attachments: (Attachment | MessageCreateAttachment | MessageCreateCloudAttachment)[] = [];
|
||||
@@ -70,123 +76,126 @@ router.post("/", route({}), async (req: Request, res: Response) => {
|
||||
}
|
||||
}
|
||||
*/
|
||||
await sendMessage({
|
||||
type: MessageType.APPLICATION_COMMAND,
|
||||
timestamp: new Date(),
|
||||
application_id: interaction.applicationId,
|
||||
channel_id: interaction.channelId,
|
||||
author_id: interaction.applicationId,
|
||||
nonce: interaction.nonce,
|
||||
content: body.data.content,
|
||||
components: body.data.components || [],
|
||||
tts: body.data.tts,
|
||||
embeds: body.data.embeds || [],
|
||||
attachments: body.data.attachments,
|
||||
poll: body.data.poll,
|
||||
flags: body.data.flags,
|
||||
reactions: [],
|
||||
// webhook_id: interaction.applicationId, // This one requires a webhook to be created first
|
||||
interaction: {
|
||||
id: interactionId,
|
||||
name: interaction.commandName,
|
||||
type: 2,
|
||||
user,
|
||||
},
|
||||
interaction_metadata: {
|
||||
id: interactionId,
|
||||
type: 2,
|
||||
user_id: interaction.userId,
|
||||
user,
|
||||
authorizing_integration_owners: {
|
||||
"1": interaction.userId,
|
||||
},
|
||||
name: interaction.commandName,
|
||||
command_type: interaction.commandType,
|
||||
},
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
case InteractionCallbackType.DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE:
|
||||
// TODO
|
||||
break;
|
||||
case InteractionCallbackType.DEFERRED_UPDATE_MESSAGE:
|
||||
//TODO keep track of state of this
|
||||
interaction.timeout = setTimeout(() => {
|
||||
emitEvent({
|
||||
event: "INTERACTION_FAILURE",
|
||||
user_id: req.user_id,
|
||||
data: {
|
||||
await sendMessage({
|
||||
type: MessageType.APPLICATION_COMMAND,
|
||||
timestamp: new Date(),
|
||||
application_id: interaction.applicationId,
|
||||
channel_id: interaction.channelId,
|
||||
author_id: interaction.applicationId,
|
||||
nonce: interaction.nonce,
|
||||
content: body.data.content,
|
||||
components: body.data.components || [],
|
||||
tts: body.data.tts,
|
||||
embeds: body.data.embeds || [],
|
||||
attachments: body.data.attachments,
|
||||
poll: body.data.poll,
|
||||
flags: body.data.flags,
|
||||
reactions: [],
|
||||
// webhook_id: interaction.applicationId, // This one requires a webhook to be created first
|
||||
interaction: {
|
||||
id: interactionId,
|
||||
nonce: interaction.nonce,
|
||||
reason_code: InteractionFailureReason.TIMEOUT,
|
||||
name: interaction.commandName,
|
||||
type: 2,
|
||||
user,
|
||||
},
|
||||
} as InteractionFailureEvent);
|
||||
}, 30000);
|
||||
pendingInteractions.delete(interactionId);
|
||||
res.sendStatus(204);
|
||||
return;
|
||||
case InteractionCallbackType.UPDATE_MESSAGE:
|
||||
{
|
||||
if (!interaction.messageId) throw new HTTPError("no. That was not a message");
|
||||
const message = await Message.findOneOrFail({
|
||||
relations: {
|
||||
author: true,
|
||||
webhook: true,
|
||||
application: true,
|
||||
mentions: true,
|
||||
mention_roles: true,
|
||||
mention_channels: true,
|
||||
sticker_items: true,
|
||||
attachments: true,
|
||||
thread: {
|
||||
recipients: {
|
||||
user: true,
|
||||
},
|
||||
interaction_metadata: {
|
||||
id: interactionId,
|
||||
type: 2,
|
||||
user_id: interaction.userId,
|
||||
user,
|
||||
authorizing_integration_owners: {
|
||||
"1": interaction.userId,
|
||||
},
|
||||
channel: true,
|
||||
},
|
||||
where: {
|
||||
id: interaction.messageId,
|
||||
name: interaction.commandName,
|
||||
command_type: interaction.commandType,
|
||||
},
|
||||
});
|
||||
if (body.data.content && body.data.content.length > Config.get().limits.message.maxCharacters) {
|
||||
throw new HTTPError("Content length over max character limit");
|
||||
}
|
||||
if (body.data.components) stripNull(body.data.components);
|
||||
message.embeds = body.data.embeds || [];
|
||||
const handle = body.data.components ? handleComps(body.data.components, message.flags) : undefined;
|
||||
await handle?.(message.id, message.author as User, message.channel);
|
||||
message.components = body.data.components;
|
||||
await message.save();
|
||||
emitEvent({
|
||||
event: "MESSAGE_UPDATE",
|
||||
channel_id: message.channel_id,
|
||||
data: message.toJSON(),
|
||||
} satisfies MessageUpdateEvent);
|
||||
}
|
||||
// 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);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case InteractionCallbackType.DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE:
|
||||
// TODO
|
||||
break;
|
||||
case InteractionCallbackType.DEFERRED_UPDATE_MESSAGE:
|
||||
//TODO keep track of state of this
|
||||
interaction.timeout = setTimeout(() => {
|
||||
emitEvent({
|
||||
event: "INTERACTION_FAILURE",
|
||||
user_id: req.user_id,
|
||||
data: {
|
||||
id: interactionId,
|
||||
nonce: interaction.nonce,
|
||||
reason_code: InteractionFailureReason.TIMEOUT,
|
||||
},
|
||||
} as InteractionFailureEvent);
|
||||
}, 30000);
|
||||
pendingInteractions.delete(interactionId);
|
||||
res.sendStatus(204);
|
||||
return;
|
||||
case InteractionCallbackType.UPDATE_MESSAGE:
|
||||
{
|
||||
if (!interaction.messageId) throw new HTTPError("no. That was not a message");
|
||||
const message = await Message.findOneOrFail({
|
||||
relations: {
|
||||
author: true,
|
||||
webhook: true,
|
||||
application: true,
|
||||
mentions: true,
|
||||
mention_roles: true,
|
||||
mention_channels: true,
|
||||
sticker_items: true,
|
||||
attachments: true,
|
||||
thread: {
|
||||
recipients: {
|
||||
user: true,
|
||||
},
|
||||
},
|
||||
channel: true,
|
||||
},
|
||||
where: {
|
||||
id: interaction.messageId,
|
||||
},
|
||||
});
|
||||
if (body.data.content && body.data.content.length > Config.get().limits.message.maxCharacters) {
|
||||
throw new HTTPError("Content length over max character limit");
|
||||
}
|
||||
if (body.data.components) stripNull(body.data.components);
|
||||
message.embeds = body.data.embeds || [];
|
||||
const handle = body.data.components ? handleComps(body.data.components, message.flags) : undefined;
|
||||
await handle?.(message.id, message.author as User, message.channel);
|
||||
message.components = body.data.components;
|
||||
await message.save();
|
||||
emitEvent({
|
||||
event: "MESSAGE_UPDATE",
|
||||
channel_id: message.channel_id,
|
||||
data: message.toJSON(),
|
||||
} satisfies MessageUpdateEvent);
|
||||
}
|
||||
// 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;
|
||||
function stripNull(components: BaseMessageComponents[]) {
|
||||
|
||||
@@ -20,6 +20,7 @@ import { DiscordApiErrors, EVENT, FieldErrors, PermissionResolvable, Permissions
|
||||
import { AnyValidateFunction } from "ajv/dist/core";
|
||||
import { NextFunction, Request, Response } from "express";
|
||||
import { ajv } from "@spacebar/schemas";
|
||||
import { BigNumber } from "bignumber.js";
|
||||
|
||||
const ignoredRequestSchemas = [
|
||||
// skip validation for settings proto JSON updates - TODO: figure out if this even possible to fix?
|
||||
@@ -98,6 +99,20 @@ export function followNullPath(obj1: any, nullObj: stripNulls) {
|
||||
}
|
||||
}
|
||||
}
|
||||
//It's pretty safe to assume numbers over the number limit aren't really meant to be numbers, so we turn them to strings.
|
||||
export function bigNumberToString(obj1: unknown) {
|
||||
if (obj1 && typeof obj1 === "object") {
|
||||
for (const [key, value] of Object.entries(obj1)) {
|
||||
if (typeof value === "object") {
|
||||
if (value instanceof BigNumber) {
|
||||
//@ts-expect-error this is fine lol
|
||||
obj1[key] = value.toString();
|
||||
}
|
||||
bigNumberToString(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
export function route(opts: RouteOptions) {
|
||||
let validate: AnyValidateFunction | undefined;
|
||||
if (opts.requestBody) {
|
||||
@@ -133,15 +148,16 @@ export function route(opts: RouteOptions) {
|
||||
throw SpacebarApiErrors.MISSING_RIGHTS.withParams(opts.right as string);
|
||||
}
|
||||
}
|
||||
bigNumberToString(req.body);
|
||||
|
||||
if (validate && !ignoredRequestSchemas.includes(opts.requestBody!)) {
|
||||
if (opts.stripNulls) {
|
||||
if (opts.stripNulls === true) stripNull(req.body);
|
||||
else followNullPath(req.body, opts.stripNulls);
|
||||
}
|
||||
|
||||
const valid = validate(req.body);
|
||||
if (!valid) {
|
||||
//console.log(JSON.stringify(req.body));
|
||||
const fields: Record<string, { code?: string; message: string }> = {};
|
||||
validate.errors?.forEach(
|
||||
(x) =>
|
||||
|
||||
@@ -18,8 +18,54 @@
|
||||
|
||||
import { Message } from "@spacebar/util";
|
||||
import { InteractionCallbackType } from "./InteractionCallbackType";
|
||||
import { AllowedMentions, BaseMessageComponents, Embed, MessageComponentType } from "../messages";
|
||||
import { MessageCreateAttachment, MessageCreateCloudAttachment, PollCreationSchema } from "#schemas/uncategorised";
|
||||
|
||||
export interface InteractionCallbackSchema {
|
||||
type: InteractionCallbackType;
|
||||
data: Message;
|
||||
data: unknown;
|
||||
}
|
||||
export interface PongCallback extends InteractionCallbackSchema {
|
||||
type: InteractionCallbackType.PONG;
|
||||
}
|
||||
export interface AckCallback extends InteractionCallbackSchema {
|
||||
type: InteractionCallbackType.ACKNOWLEDGE;
|
||||
}
|
||||
export interface MessageCallback extends InteractionCallbackSchema {
|
||||
type: InteractionCallbackType.CHANNEL_MESSAGE;
|
||||
}
|
||||
export interface MessageWSourceCallback extends InteractionCallbackSchema {
|
||||
type: InteractionCallbackType.CHANNEL_MESSAGE_WITH_SOURCE;
|
||||
data: InteractionMessage;
|
||||
}
|
||||
export interface MessageDWSourceCallback extends InteractionCallbackSchema {
|
||||
type: InteractionCallbackType.DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE;
|
||||
data: InteractionMessage;
|
||||
}
|
||||
export interface MessageUpdateCallback extends InteractionCallbackSchema {
|
||||
type: InteractionCallbackType.UPDATE_MESSAGE;
|
||||
data: InteractionMessage;
|
||||
}
|
||||
export interface MessageDUpdateCallback extends InteractionCallbackSchema {
|
||||
type: InteractionCallbackType.DEFERRED_UPDATE_MESSAGE;
|
||||
data: InteractionMessage;
|
||||
}
|
||||
export type InteractionCallbacksSchema =
|
||||
| PongCallback
|
||||
| AckCallback
|
||||
| MessageCallback
|
||||
| MessageWSourceCallback
|
||||
| MessageDWSourceCallback
|
||||
| MessageUpdateCallback
|
||||
| MessageDUpdateCallback;
|
||||
|
||||
export interface InteractionMessage {
|
||||
content?: string;
|
||||
tts?: boolean;
|
||||
embeds?: Embed[];
|
||||
allowed_mentions?: AllowedMentions;
|
||||
components?: BaseMessageComponents[];
|
||||
flags?: number;
|
||||
attachments?: (MessageCreateAttachment | MessageCreateCloudAttachment)[];
|
||||
poll?: PollCreationSchema;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user