mirror of
https://github.com/xHyroM/roles-bot.git
synced 2024-11-21 16:11:04 +01:00
feat: add contexts
This commit is contained in:
parent
05aa790303
commit
6cf73a07a5
12 changed files with 131 additions and 57 deletions
|
@ -5,6 +5,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "node scripts/build.mjs",
|
"build": "node scripts/build.mjs",
|
||||||
"dev": "miniflare --watch --debug --port 8787",
|
"dev": "miniflare --watch --debug --port 8787",
|
||||||
|
"dev:tunnel": "cloudflared tunnel --url localhost:8787/",
|
||||||
"deploy": "cross-env NODE_ENV=production wrangler publish"
|
"deploy": "cross-env NODE_ENV=production wrangler publish"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
|
|
|
@ -1,15 +1,10 @@
|
||||||
import { InteractionResponseType } from "discord-api-types/v10";
|
|
||||||
import { Command } from "../structs/Command";
|
import { Command } from "../structs/Command";
|
||||||
import respond from "../utils/respond";
|
|
||||||
|
|
||||||
new Command({
|
new Command({
|
||||||
name: "setup",
|
name: "setup",
|
||||||
run: async () => {
|
run: async (ctx) => {
|
||||||
return respond({
|
await ctx.editReply({
|
||||||
type: InteractionResponseType.ChannelMessageWithSource,
|
content: "Setup",
|
||||||
data: {
|
|
||||||
content: "Setup",
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,17 +9,19 @@ import {
|
||||||
import { COMMANDS, COMPONENTS } from "./registers";
|
import { COMMANDS, COMPONENTS } from "./registers";
|
||||||
import { verify } from "./utils/verify";
|
import { verify } from "./utils/verify";
|
||||||
import respond from "./utils/respond";
|
import respond from "./utils/respond";
|
||||||
|
import { CommandContext } from "./structs/contexts/CommandContext";
|
||||||
|
import { ComponentContext } from "./structs/contexts/ComponentContext";
|
||||||
|
|
||||||
console.log(COMMANDS);
|
console.log(COMMANDS);
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
fetch: async (request: Request) => {
|
fetch: async (request: Request, env: Env) => {
|
||||||
if (
|
if (
|
||||||
!request.headers.get("X-Signature-Ed25519") ||
|
!request.headers.get("X-Signature-Ed25519") ||
|
||||||
!request.headers.get("X-Signature-Timestamp")
|
!request.headers.get("X-Signature-Timestamp")
|
||||||
)
|
)
|
||||||
return Response.redirect("https://xhyrom.dev");
|
return Response.redirect("https://xhyrom.dev");
|
||||||
if (!(await verify(request)))
|
if (!(await verify(request, env)))
|
||||||
return new Response("Invalid request signature", { status: 401 });
|
return new Response("Invalid request signature", { status: 401 });
|
||||||
|
|
||||||
const interaction = (await request.json()) as
|
const interaction = (await request.json()) as
|
||||||
|
@ -38,7 +40,14 @@ export default {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!command) return new Response("Unknown command", { status: 404 });
|
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(
|
const component = COMPONENTS.find(
|
||||||
|
@ -46,6 +55,13 @@ export default {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!component) return new Response("Unknown component", { status: 404 });
|
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));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
import { APIApplicationCommandInteraction } from "discord-api-types/v10";
|
|
||||||
import { registerCommand } from "../registers";
|
import { registerCommand } from "../registers";
|
||||||
|
import { CommandContext } from "./contexts/CommandContext";
|
||||||
|
|
||||||
interface CommandOptions {
|
interface CommandOptions {
|
||||||
name: string;
|
name: string;
|
||||||
run: (interaction: APIApplicationCommandInteraction) => Promise<Response>;
|
run: (interaction: CommandContext) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Command {
|
export class Command {
|
||||||
public name: string;
|
public name: string;
|
||||||
public run: (
|
public run: (interaction: CommandContext) => void;
|
||||||
interaction: APIApplicationCommandInteraction,
|
|
||||||
) => Promise<Response>;
|
|
||||||
|
|
||||||
constructor(options: CommandOptions) {
|
constructor(options: CommandOptions) {
|
||||||
this.name = options.name;
|
this.name = options.name;
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
import { APIMessageComponentInteraction } from "discord-api-types/v10";
|
|
||||||
import { registerComponent } from "../registers";
|
import { registerComponent } from "../registers";
|
||||||
|
import { ComponentContext } from "./contexts/ComponentContext";
|
||||||
|
|
||||||
interface ComponentOptions {
|
interface ComponentOptions {
|
||||||
id: string;
|
id: string;
|
||||||
run: (interaction: APIMessageComponentInteraction) => Promise<Response>;
|
run: (interaction: ComponentContext) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Component {
|
export class Component {
|
||||||
public id: string;
|
public id: string;
|
||||||
public run: (
|
public run: (interaction: ComponentContext) => void;
|
||||||
interaction: APIMessageComponentInteraction,
|
|
||||||
) => Promise<Response>;
|
|
||||||
|
|
||||||
constructor(options: ComponentOptions) {
|
constructor(options: ComponentOptions) {
|
||||||
this.id = options.id;
|
this.id = options.id;
|
||||||
|
|
12
packages/bot/src/structs/contexts/CommandContext.ts
Normal file
12
packages/bot/src/structs/contexts/CommandContext.ts
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
12
packages/bot/src/structs/contexts/ComponentContext.ts
Normal file
12
packages/bot/src/structs/contexts/ComponentContext.ts
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
33
packages/bot/src/structs/contexts/Context.ts
Normal file
33
packages/bot/src/structs/contexts/Context.ts
Normal file
|
@ -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}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
8
packages/bot/src/types.d.ts
vendored
8
packages/bot/src/types.d.ts
vendored
|
@ -1,3 +1,7 @@
|
||||||
// secrets: wrangler secret put <name>
|
// secrets: wrangler secret put <name>
|
||||||
declare const publicKey: string;
|
declare let MINIFLARE; // just check because algorithm is different
|
||||||
declare const token: string;
|
|
||||||
|
declare interface Env {
|
||||||
|
publicKey: string;
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
|
@ -10,28 +10,28 @@ function hex2bin(hex: string) {
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PUBLIC_KEY = crypto.subtle.importKey(
|
|
||||||
"raw",
|
|
||||||
hex2bin(publicKey),
|
|
||||||
{
|
|
||||||
name: "NODE-ED25519",
|
|
||||||
namedCurve: "NODE-ED25519",
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
["verify"],
|
|
||||||
);
|
|
||||||
|
|
||||||
const encoder = new TextEncoder();
|
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
|
// rome-ignore lint/style/noNonNullAssertion: its fine
|
||||||
const signature = hex2bin(request.headers.get("X-Signature-Ed25519")!);
|
const signature = hex2bin(request.headers.get("X-Signature-Ed25519")!);
|
||||||
const timestamp = request.headers.get("X-Signature-Timestamp");
|
const timestamp = request.headers.get("X-Signature-Timestamp");
|
||||||
const unknown = await request.clone().text();
|
const unknown = await request.clone().text();
|
||||||
|
|
||||||
return await crypto.subtle.verify(
|
return await crypto.subtle.verify(
|
||||||
"NODE-ED25519",
|
typeof MINIFLARE !== "undefined" ? "Ed25519" : "NODE-ED25519",
|
||||||
await PUBLIC_KEY,
|
subtle,
|
||||||
signature,
|
signature,
|
||||||
encoder.encode(timestamp + unknown),
|
encoder.encode(timestamp + unknown),
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,6 +3,8 @@ type = "javascript"
|
||||||
account_id = "294bee38d448e390dab3757215c63f03"
|
account_id = "294bee38d448e390dab3757215c63f03"
|
||||||
compatibility_date = "2022-07-12"
|
compatibility_date = "2022-07-12"
|
||||||
|
|
||||||
|
main = "dist/worker.mjs"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
command = "pnpm build"
|
command = "pnpm build"
|
||||||
[build.upload]
|
[build.upload]
|
||||||
|
|
41
turbo.json
41
turbo.json
|
@ -1,21 +1,24 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://turbo.build/schema.json",
|
"$schema": "https://turbo.build/schema.json",
|
||||||
"pipeline": {
|
"pipeline": {
|
||||||
"build": {
|
"build": {
|
||||||
"dependsOn": ["^build"],
|
"dependsOn": ["^build"],
|
||||||
"outputs": ["dist"]
|
"outputs": ["dist"]
|
||||||
},
|
},
|
||||||
"dev": {
|
"dev": {
|
||||||
"outputs": ["dist"]
|
"outputs": ["dist"]
|
||||||
},
|
},
|
||||||
"preview": {
|
"dev:tunnel": {
|
||||||
"outputs": ["dist"]
|
"outputs": ["dist"]
|
||||||
},
|
},
|
||||||
"deploy": {
|
"preview": {
|
||||||
"dependsOn": ["build", "test", "lint"]
|
"outputs": ["dist"]
|
||||||
},
|
},
|
||||||
"test": {
|
"deploy": {
|
||||||
"dependsOn": ["^build"]
|
"dependsOn": ["build", "test", "lint"]
|
||||||
}
|
},
|
||||||
}
|
"test": {
|
||||||
|
"dependsOn": ["^build"]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue