From 6cf73a07a5f11201a71fc7e62c9d6e32e6cfd980 Mon Sep 17 00:00:00 2001 From: xHyroM Date: Fri, 7 Apr 2023 21:51:25 +0200 Subject: [PATCH] feat: add contexts --- packages/bot/package.json | 1 + packages/bot/src/commands/setup.ts | 11 ++--- packages/bot/src/index.ts | 24 +++++++++-- packages/bot/src/structs/Command.ts | 8 ++-- packages/bot/src/structs/Component.ts | 8 ++-- .../src/structs/contexts/CommandContext.ts | 12 ++++++ .../src/structs/contexts/ComponentContext.ts | 12 ++++++ packages/bot/src/structs/contexts/Context.ts | 33 +++++++++++++++ packages/bot/src/types.d.ts | 8 +++- packages/bot/src/utils/verify.ts | 28 ++++++------- packages/bot/wrangler.toml | 2 + turbo.json | 41 ++++++++++--------- 12 files changed, 131 insertions(+), 57 deletions(-) create mode 100644 packages/bot/src/structs/contexts/CommandContext.ts create mode 100644 packages/bot/src/structs/contexts/ComponentContext.ts create mode 100644 packages/bot/src/structs/contexts/Context.ts diff --git a/packages/bot/package.json b/packages/bot/package.json index c0a3645..4189b7c 100644 --- a/packages/bot/package.json +++ b/packages/bot/package.json @@ -5,6 +5,7 @@ "scripts": { "build": "node scripts/build.mjs", "dev": "miniflare --watch --debug --port 8787", + "dev:tunnel": "cloudflared tunnel --url localhost:8787/", "deploy": "cross-env NODE_ENV=production wrangler publish" }, "keywords": [], diff --git a/packages/bot/src/commands/setup.ts b/packages/bot/src/commands/setup.ts index 8c57b16..7801273 100644 --- a/packages/bot/src/commands/setup.ts +++ b/packages/bot/src/commands/setup.ts @@ -1,15 +1,10 @@ -import { InteractionResponseType } from "discord-api-types/v10"; import { Command } from "../structs/Command"; -import respond from "../utils/respond"; new Command({ name: "setup", - run: async () => { - return respond({ - type: InteractionResponseType.ChannelMessageWithSource, - data: { - content: "Setup", - }, + run: async (ctx) => { + await ctx.editReply({ + content: "Setup", }); }, }); diff --git a/packages/bot/src/index.ts b/packages/bot/src/index.ts index 6f6755f..4a0b335 100644 --- a/packages/bot/src/index.ts +++ b/packages/bot/src/index.ts @@ -9,17 +9,19 @@ import { import { COMMANDS, COMPONENTS } from "./registers"; import { verify } from "./utils/verify"; import respond from "./utils/respond"; +import { CommandContext } from "./structs/contexts/CommandContext"; +import { ComponentContext } from "./structs/contexts/ComponentContext"; console.log(COMMANDS); export default { - fetch: async (request: Request) => { + fetch: async (request: Request, env: Env) => { if ( !request.headers.get("X-Signature-Ed25519") || !request.headers.get("X-Signature-Timestamp") ) return Response.redirect("https://xhyrom.dev"); - if (!(await verify(request))) + if (!(await verify(request, env))) return new Response("Invalid request signature", { status: 401 }); const interaction = (await request.json()) as @@ -38,7 +40,14 @@ export default { ); if (!command) return new Response("Unknown command", { status: 404 }); - return command.run(interaction); + + try { + return respond({ + type: InteractionResponseType.DeferredChannelMessageWithSource, + }); + } finally { + command.run(new CommandContext(interaction, env)); + } } const component = COMPONENTS.find( @@ -46,6 +55,13 @@ export default { ); if (!component) return new Response("Unknown component", { status: 404 }); - return component.run(interaction); + + try { + return respond({ + type: InteractionResponseType.DeferredChannelMessageWithSource, + }); + } finally { + component.run(new ComponentContext(interaction, env)); + } }, }; diff --git a/packages/bot/src/structs/Command.ts b/packages/bot/src/structs/Command.ts index 7dee36d..3450083 100644 --- a/packages/bot/src/structs/Command.ts +++ b/packages/bot/src/structs/Command.ts @@ -1,16 +1,14 @@ -import { APIApplicationCommandInteraction } from "discord-api-types/v10"; import { registerCommand } from "../registers"; +import { CommandContext } from "./contexts/CommandContext"; interface CommandOptions { name: string; - run: (interaction: APIApplicationCommandInteraction) => Promise; + run: (interaction: CommandContext) => void; } export class Command { public name: string; - public run: ( - interaction: APIApplicationCommandInteraction, - ) => Promise; + public run: (interaction: CommandContext) => void; constructor(options: CommandOptions) { this.name = options.name; diff --git a/packages/bot/src/structs/Component.ts b/packages/bot/src/structs/Component.ts index 1348049..8c3290e 100644 --- a/packages/bot/src/structs/Component.ts +++ b/packages/bot/src/structs/Component.ts @@ -1,16 +1,14 @@ -import { APIMessageComponentInteraction } from "discord-api-types/v10"; import { registerComponent } from "../registers"; +import { ComponentContext } from "./contexts/ComponentContext"; interface ComponentOptions { id: string; - run: (interaction: APIMessageComponentInteraction) => Promise; + run: (interaction: ComponentContext) => void; } export class Component { public id: string; - public run: ( - interaction: APIMessageComponentInteraction, - ) => Promise; + public run: (interaction: ComponentContext) => void; constructor(options: ComponentOptions) { this.id = options.id; diff --git a/packages/bot/src/structs/contexts/CommandContext.ts b/packages/bot/src/structs/contexts/CommandContext.ts new file mode 100644 index 0000000..fc2cc6a --- /dev/null +++ b/packages/bot/src/structs/contexts/CommandContext.ts @@ -0,0 +1,12 @@ +import { APIApplicationCommandInteraction } from "discord-api-types/v10"; +import { Context } from "./Context"; + +export class CommandContext extends Context { + public interaction: APIApplicationCommandInteraction; + + constructor(interaction: APIApplicationCommandInteraction, env: Env) { + super(interaction, env); + + this.interaction = interaction; + } +} diff --git a/packages/bot/src/structs/contexts/ComponentContext.ts b/packages/bot/src/structs/contexts/ComponentContext.ts new file mode 100644 index 0000000..5e1afd1 --- /dev/null +++ b/packages/bot/src/structs/contexts/ComponentContext.ts @@ -0,0 +1,12 @@ +import { APIMessageComponentInteraction } from "discord-api-types/v10"; +import { Context } from "./Context"; + +export class ComponentContext extends Context { + public interaction: APIMessageComponentInteraction; + + constructor(interaction: APIMessageComponentInteraction, env: Env) { + super(interaction, env); + + this.interaction = interaction; + } +} diff --git a/packages/bot/src/structs/contexts/Context.ts b/packages/bot/src/structs/contexts/Context.ts new file mode 100644 index 0000000..4dd548e --- /dev/null +++ b/packages/bot/src/structs/contexts/Context.ts @@ -0,0 +1,33 @@ +import { + APIInteraction, + APIInteractionResponseCallbackData, + RouteBases, + Routes, +} from "discord-api-types/v10"; + +export class Context { + public interaction: APIInteraction; + public env: Env; + + constructor(interaction: APIInteraction, env: Env) { + this.interaction = interaction; + this.env = env; + } + + public async editReply(content: APIInteractionResponseCallbackData) { + return await fetch( + `${RouteBases.api}${Routes.webhookMessage( + this.interaction.application_id, + this.interaction.token, + )}`, + { + method: "PATCH", + body: JSON.stringify(content), + headers: { + "Content-Type": "application/json", + Authorization: `Bot ${this.env.token}`, + }, + }, + ); + } +} diff --git a/packages/bot/src/types.d.ts b/packages/bot/src/types.d.ts index 6369f0e..f7af31b 100644 --- a/packages/bot/src/types.d.ts +++ b/packages/bot/src/types.d.ts @@ -1,3 +1,7 @@ // secrets: wrangler secret put -declare const publicKey: string; -declare const token: string; +declare let MINIFLARE; // just check because algorithm is different + +declare interface Env { + publicKey: string; + token: string; +} diff --git a/packages/bot/src/utils/verify.ts b/packages/bot/src/utils/verify.ts index 2bf0bfb..1c7f77e 100644 --- a/packages/bot/src/utils/verify.ts +++ b/packages/bot/src/utils/verify.ts @@ -10,28 +10,28 @@ function hex2bin(hex: string) { return buf; } -const PUBLIC_KEY = crypto.subtle.importKey( - "raw", - hex2bin(publicKey), - { - name: "NODE-ED25519", - namedCurve: "NODE-ED25519", - }, - true, - ["verify"], -); - const encoder = new TextEncoder(); -export async function verify(request: Request) { +export async function verify(request: Request, env: Env) { + const subtle = await crypto.subtle.importKey( + "raw", + hex2bin(env.publicKey), + { + name: typeof MINIFLARE !== "undefined" ? "Ed25519" : "NODE-ED25519", + namedCurve: typeof MINIFLARE !== "undefined" ? "Ed25519" : "NODE-ED25519", + }, + true, + ["verify"], + ); + // rome-ignore lint/style/noNonNullAssertion: its fine const signature = hex2bin(request.headers.get("X-Signature-Ed25519")!); const timestamp = request.headers.get("X-Signature-Timestamp"); const unknown = await request.clone().text(); return await crypto.subtle.verify( - "NODE-ED25519", - await PUBLIC_KEY, + typeof MINIFLARE !== "undefined" ? "Ed25519" : "NODE-ED25519", + subtle, signature, encoder.encode(timestamp + unknown), ); diff --git a/packages/bot/wrangler.toml b/packages/bot/wrangler.toml index f5e23d4..2fce2ef 100644 --- a/packages/bot/wrangler.toml +++ b/packages/bot/wrangler.toml @@ -3,6 +3,8 @@ type = "javascript" account_id = "294bee38d448e390dab3757215c63f03" compatibility_date = "2022-07-12" +main = "dist/worker.mjs" + [build] command = "pnpm build" [build.upload] diff --git a/turbo.json b/turbo.json index cc47c28..601b7c4 100644 --- a/turbo.json +++ b/turbo.json @@ -1,21 +1,24 @@ { - "$schema": "https://turbo.build/schema.json", - "pipeline": { - "build": { - "dependsOn": ["^build"], - "outputs": ["dist"] - }, - "dev": { - "outputs": ["dist"] - }, - "preview": { - "outputs": ["dist"] - }, - "deploy": { - "dependsOn": ["build", "test", "lint"] - }, - "test": { - "dependsOn": ["^build"] - } - } + "$schema": "https://turbo.build/schema.json", + "pipeline": { + "build": { + "dependsOn": ["^build"], + "outputs": ["dist"] + }, + "dev": { + "outputs": ["dist"] + }, + "dev:tunnel": { + "outputs": ["dist"] + }, + "preview": { + "outputs": ["dist"] + }, + "deploy": { + "dependsOn": ["build", "test", "lint"] + }, + "test": { + "dependsOn": ["^build"] + } + } }