dressed
Version:
A sleek, serverless-ready Discord bot framework.
154 lines • 6.11 kB
JavaScript
import ora from "ora";
import { verifySignature } from "./signature.js";
import { ApplicationWebhookType, InteractionType, } from "discord-api-types/v10";
import { createServer as createHttpServer } from "node:http";
import { stdout } from "node:process";
import { Buffer } from "node:buffer";
import { createInteraction } from "./extenders/interaction.js";
import { setupCommands } from "./handlers/commands.js";
import { setupComponents } from "./handlers/components.js";
import { setupEvents } from "./handlers/events.js";
/**
* Starts a server to handle interactions.
* @returns The server instance
*/
export function createServer(commands, components, events, config) {
var _a;
const server = createHttpServer((req, res) => {
var _a;
if (req.url !== ((_a = config.endpoint) !== null && _a !== void 0 ? _a : "/")) {
res.statusCode = 404;
res.end();
return;
}
else if (req.method !== "POST") {
res.statusCode = 405;
res.end();
return;
}
const chunks = [];
req
.on("data", (c) => chunks.push(c))
.on("end", async () => {
const handlerRes = await handleRequest(new Request("http://localhost", {
method: "POST",
body: Buffer.concat(chunks),
headers: req.headers,
}), commands, components, events, config);
res.statusCode = handlerRes.status;
res.setHeader("Content-Type", "application/json");
res.end(handlerRes.status === 200 ? '{"type":1}' : null);
});
});
const port = (_a = config.port) !== null && _a !== void 0 ? _a : 8000;
server.listen(port, "0.0.0.0", () => {
var _a;
console.log("Bot is now listening on", new URL((_a = config.endpoint) !== null && _a !== void 0 ? _a : "", `http://localhost:${port}`).href);
});
function shutdown() {
server.close(() => process.exit(0));
}
process.on("SIGTERM", shutdown);
process.on("SIGINT", shutdown);
return server;
}
/**
* Handles a request from Discord.
* @param req The request from Discord
* @param commands A list of commands or the function to run a command
* @param components A list of components or the function to run a component
* @param events A list of events or the function to run an event
* @param config Configuration for your server
* @returns The response to send back to Discord
*/
export async function handleRequest(req, commands, components, events, config) {
const reqLoader = ora({
stream: stdout,
text: "Validating new request",
}).start();
const body = await req.text();
if (!verifySignature(body, req.headers.get("x-signature-ed25519"), req.headers.get("x-signature-timestamp"))) {
reqLoader.fail("Invalid signature");
return new Response(null, { status: 401 });
}
reqLoader.stop();
try {
const json = JSON.parse(body);
let status = 500;
// The interaction response token
if ("token" in json) {
status = handleInteraction(commands, components, json, config === null || config === void 0 ? void 0 : config.middleware);
}
else {
status = handleEvent(events, json, config === null || config === void 0 ? void 0 : config.middleware);
}
return new Response(status === 200 ? '{"type":1}' : null, {
status,
});
}
catch (error) {
console.error("Failed to process request:", error);
return new Response(null, { status: 500 });
}
}
/**
* Runs an interaction, takes functions to run commands/components/middleware and the request body
*/
export function handleInteraction(commands, components, json, middleware) {
switch (json.type) {
case InteractionType.Ping: {
console.log("Received ping test");
return 200;
}
case InteractionType.ApplicationCommand: {
const command = json;
const interaction = createInteraction(command);
const runCommand = typeof commands === "function" ? commands : setupCommands(commands);
runCommand(interaction, middleware === null || middleware === void 0 ? void 0 : middleware.commands);
return 202;
}
case InteractionType.ApplicationCommandAutocomplete: {
const autocomplete = json;
const interaction = createInteraction(autocomplete);
const runCommand = typeof commands === "function" ? commands : setupCommands(commands);
runCommand(interaction, undefined, "autocomplete");
return 202;
}
case InteractionType.MessageComponent:
case InteractionType.ModalSubmit: {
const component = json;
const interaction = createInteraction(component);
const runComponent = typeof components === "function"
? components
: setupComponents(components);
runComponent(interaction, middleware === null || middleware === void 0 ? void 0 : middleware.components);
return 202;
}
default: {
console.error("Received unknown interaction type:", json.type);
return 404;
}
}
}
/**
* Runs an event, takes a function to run events/middleware and the request body
*/
export function handleEvent(events, json, middleware) {
switch (json.type) {
case ApplicationWebhookType.Ping: {
console.log("Received ping test");
return 200;
}
case ApplicationWebhookType.Event: {
const event = json.event;
const runEvent = typeof events === "function" ? events : setupEvents(events);
runEvent(event, middleware === null || middleware === void 0 ? void 0 : middleware.events);
return 202;
}
default: {
console.log("Received unknown event type:", json.type);
return 404;
}
}
}
//# sourceMappingURL=server.js.map