feat: add things

This commit is contained in:
xHyroM 2023-04-06 21:32:43 +02:00
parent eebc948a58
commit 05aa790303
No known key found for this signature in database
GPG key ID: BE0423F386C436AA
12 changed files with 156 additions and 43 deletions

View file

@ -13,11 +13,13 @@
"devDependencies": { "devDependencies": {
"@cloudflare/workers-types": "^3.17.0", "@cloudflare/workers-types": "^3.17.0",
"@types/jest": "^29.1.2", "@types/jest": "^29.1.2",
"discord-api-types": "^0.37.14",
"esbuild": "^0.15.11", "esbuild": "^0.15.11",
"eslint": "^8.25.0", "eslint": "^8.25.0",
"jest": "^29.2.0", "jest": "^29.2.0",
"miniflare": "^2.10.0", "miniflare": "^2.10.0",
"typescript": "^4.8.4" "typescript": "^4.8.4"
},
"dependencies": {
"discord-api-types": "^0.37.37"
} }
} }

View file

@ -1,5 +1,15 @@
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 () => {
return respond({
type: InteractionResponseType.ChannelMessageWithSource,
data: {
content: "Setup",
},
});
},
}); });

View file

@ -1,11 +1,51 @@
import "./commands/setup"; import "./commands/setup";
import { COMMANDS } from "./registers"; import {
APIApplicationCommandInteraction,
APIMessageComponentInteraction,
APIPingInteraction,
InteractionResponseType,
InteractionType,
} from "discord-api-types/v10";
import { COMMANDS, COMPONENTS } from "./registers";
import { verify } from "./utils/verify";
import respond from "./utils/respond";
console.log(COMMANDS); console.log(COMMANDS);
export default { export default {
fetch: (request: Request) => { fetch: async (request: Request) => {
console.log(request); if (
return new Response("asda"); !request.headers.get("X-Signature-Ed25519") ||
!request.headers.get("X-Signature-Timestamp")
)
return Response.redirect("https://xhyrom.dev");
if (!(await verify(request)))
return new Response("Invalid request signature", { status: 401 });
const interaction = (await request.json()) as
| APIPingInteraction
| APIApplicationCommandInteraction
| APIMessageComponentInteraction;
if (interaction.type === InteractionType.Ping)
return respond({
type: InteractionResponseType.Pong,
});
if (interaction.type === InteractionType.ApplicationCommand) {
const command = COMMANDS.find(
(cmd) => cmd.name === interaction.data.name,
);
if (!command) return new Response("Unknown command", { status: 404 });
return command.run(interaction);
}
const component = COMPONENTS.find(
(cmp) => cmp.id === interaction.data.custom_id,
);
if (!component) return new Response("Unknown component", { status: 404 });
return component.run(interaction);
}, },
}; };

View file

@ -1,13 +1,13 @@
import { Command } from "./structs/Command"; import { Command } from "./structs/Command";
import { Listener } from "./structs/Listener"; import { Component } from "./structs/Component";
export const COMMANDS: Command[] = []; export const COMMANDS: Command[] = [];
export const LISTENERS: Listener[] = []; export const COMPONENTS: Component[] = [];
export const registerCommand = (command: Command) => { export const registerCommand = (command: Command) => {
COMMANDS.push(command); COMMANDS.push(command);
}; };
export const registerListener = (listener: Listener) => { export const registerComponent = (component: Component) => {
LISTENERS.push(listener); COMPONENTS.push(component);
}; };

View file

@ -1,14 +1,20 @@
import { APIApplicationCommandInteraction } from "discord-api-types/v10";
import { registerCommand } from "../registers"; import { registerCommand } from "../registers";
interface CommandOptions { interface CommandOptions {
name: string; name: string;
run: (interaction: APIApplicationCommandInteraction) => Promise<Response>;
} }
export class Command { export class Command {
public name: string; public name: string;
public run: (
interaction: APIApplicationCommandInteraction,
) => Promise<Response>;
constructor(options: CommandOptions) { constructor(options: CommandOptions) {
this.name = options.name; this.name = options.name;
this.run = options.run;
registerCommand(this); registerCommand(this);
} }

View file

@ -0,0 +1,21 @@
import { APIMessageComponentInteraction } from "discord-api-types/v10";
import { registerComponent } from "../registers";
interface ComponentOptions {
id: string;
run: (interaction: APIMessageComponentInteraction) => Promise<Response>;
}
export class Component {
public id: string;
public run: (
interaction: APIMessageComponentInteraction,
) => Promise<Response>;
constructor(options: ComponentOptions) {
this.id = options.id;
this.run = options.run;
registerComponent(this);
}
}

View file

@ -1,18 +0,0 @@
import { registerListener } from "../registers";
interface ListenerOptions {
name: string;
once: boolean | undefined;
}
export class Listener {
public name: string;
public once: boolean;
constructor(options: ListenerOptions) {
this.name = options.name;
this.once = options.once ?? false;
registerListener(this);
}
}

3
packages/bot/src/types.d.ts vendored Normal file
View file

@ -0,0 +1,3 @@
// secrets: wrangler secret put <name>
declare const publicKey: string;
declare const token: string;

View file

@ -0,0 +1,7 @@
import { APIInteractionResponse } from "discord-api-types/v10";
export default function (response: APIInteractionResponse) {
return new Response(JSON.stringify(response), {
headers: { "content-type": "application/json" },
});
}

View file

@ -0,0 +1,38 @@
// from https://gist.github.com/devsnek/77275f6e3f810a9545440931ed314dc1
"use strict";
function hex2bin(hex: string) {
const buf = new Uint8Array(Math.ceil(hex.length / 2));
for (let i = 0; i < buf.length; i++) {
buf[i] = parseInt(hex.substring(i * 2, i * 2 + 2), 16);
}
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) {
// 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,
signature,
encoder.encode(timestamp + unknown),
);
}

View file

@ -9,6 +9,10 @@ importers:
version: 12.0.0 version: 12.0.0
packages/bot: packages/bot:
dependencies:
discord-api-types:
specifier: ^0.37.37
version: 0.37.37
devDependencies: devDependencies:
'@cloudflare/workers-types': '@cloudflare/workers-types':
specifier: ^3.17.0 specifier: ^3.17.0
@ -16,9 +20,6 @@ importers:
'@types/jest': '@types/jest':
specifier: ^29.1.2 specifier: ^29.1.2
version: 29.1.2 version: 29.1.2
discord-api-types:
specifier: ^0.37.14
version: 0.37.14
esbuild: esbuild:
specifier: ^0.15.11 specifier: ^0.15.11
version: 0.15.11 version: 0.15.11
@ -2324,9 +2325,9 @@ packages:
path-type: 4.0.0 path-type: 4.0.0
dev: true dev: true
/discord-api-types@0.37.14: /discord-api-types@0.37.37:
resolution: {integrity: sha512-byBH7SfDCMJwxdqeS8k5sihltH88/YPhuwx+vF2cftSxFLdxyHyU/ZxDL3bq+LB2c4ls/TymE76/ISlLfniUXg==} resolution: {integrity: sha512-LDMBKzl/zbvHO/yCzno5hevuA6lFIXJwdKSJZQrB+1ToDpFfN9thK+xxgZNR4aVkI7GHRDja0p4Sl2oYVPnHYg==}
dev: true dev: false
/dlv@1.1.3: /dlv@1.1.3:
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}

View file

@ -3,6 +3,9 @@
"organizeImports": { "organizeImports": {
"enabled": false "enabled": false
}, },
"files": {
"ignore": ["dist/**"]
},
"linter": { "linter": {
"enabled": true, "enabled": true,
"rules": { "rules": {