/** * Welcome to Cloudflare Workers! This is your first worker. * * - Run `npm run dev` in your terminal to start a development server * - Open a browser tab at http://localhost:8787/ to see your worker in action * - Run `npm run deploy` to publish your worker * * Learn more at https://developers.cloudflare.com/workers/ */ import { CloudflareWorkerServer, SlashCreator } from 'slash-create/web'; import { GenerateCommand, ResponseCommand } from './commands.js'; import { DbAccess } from './dbAccess.js'; import { isSnowflake, type Snowflake } from 'discord-snowflake'; import { RollTableOrder } from './rolltable.js'; import { RollTableEmbedTitles, RollTableEmoji, RollTableNames } from './generated.js'; export interface Env { BASE_URL: string; DISCORD_APP_ID: string DISCORD_APP_SECRET: string DISCORD_PUBLIC_KEY: string DISCORD_DEV_GUILD_IDS: string DB: D1Database } function getHandler(env: Env, token?: string) { const dbAccess = new DbAccess(env.DB) const server = new CloudflareWorkerServer() const creator = new SlashCreator({ allowedMentions: {everyone: false, roles: false, users: false}, applicationID: env.DISCORD_APP_ID, componentTimeouts: true, defaultImageSize: 0, disableTimeouts: false, endpointPath: '/discord/interactions', handleCommandsManually: false, publicKey: env.DISCORD_PUBLIC_KEY, unknownCommandResponse: true, token: token, }) const withGuilds: Snowflake[] = env.DISCORD_DEV_GUILD_IDS ? env.DISCORD_DEV_GUILD_IDS.split(",").flatMap(v => isSnowflake(v) ? [v] : []) : [] creator.withServer(server) creator.registerCommand(new GenerateCommand(creator, dbAccess)) creator.registerCommand(new ResponseCommand(creator, dbAccess, env.BASE_URL)) creator.registerCommand(new GenerateCommand(creator, dbAccess, withGuilds)) creator.registerCommand(new ResponseCommand(creator, dbAccess, env.BASE_URL, withGuilds)) return { fetch: server.fetch.bind(server), syncCommands: creator.syncCommands.bind(creator), db: dbAccess, } } function getAuthorization(username: string, password: string): string { return btoa(username + ":" + password) } export default { async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise { const tokenRequest = new Request(`https://discord.com/api/v10/oauth2/token`, { headers: new Headers({ "Content-Type": "application/x-www-form-urlencoded", "Authorization": `Basic ${getAuthorization(env.DISCORD_APP_ID, env.DISCORD_APP_SECRET)}`, }), body: new URLSearchParams({"grant_type": "client_credentials", "scope": "applications.commands.update"}), method: "POST" }) const tokenResponse = await fetch(tokenRequest) if (tokenResponse.status !== 200) { const text = await tokenResponse.text() console.error(`Failed getting token`, text) return new Response(`Could not sync commands: Failed getting token: ${tokenResponse.status} ${tokenResponse.statusText}\n${text}`, {status: 500}) } const json = await tokenResponse.json() as {access_token: string} const handler = getHandler(env, "Bearer " + json.access_token) const url = new URL(request.url) if (url.pathname === "/discord/interactions") { try { return handler.fetch(request, env, ctx) } catch (e) { console.error("Failed to respond to interactions endpoint", e); return new Response(`Could not respond to interaction: ${e}`, { status: 500 }) } } else if (url.pathname === "/discord/sync") { try { await handler.syncCommands({ deleteCommands: true, syncGuilds: true, }) } catch (e) { console.error("Failed to respond to sync endpoint", e) return new Response(`Could not sync commands: ${e}`, { status: 500, }) } return new Response(`Synced commands!`, { status: 200, }) } else if (url.pathname === "/responses") { try { const response = [] const server = url.searchParams.get("server") const tables = await (server === null ? handler.db.getGlobalResponses() : handler.db.getResponsesInServer(server)) for (const table of RollTableOrder) { response.push(`${RollTableNames[table]} - ${RollTableEmoji[table]} ${RollTableEmbedTitles[table]}`) for (const value of tables[table]) { response.push(` * ${value}`) } response.push('') } return new Response(response.join('\n'), {status: 200}) } catch (e) { console.error("Failed to respond to list endpoint", e) return new Response(`Could not list responses: ${e}`, { status: 500, }) } } else { return new Response(`Invalid path ${url.pathname}`, {status: 404}) } }, };