Scenario generator for vore roleplay and story ideas. https://scenario-generator.deliciousreya.net/responses
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

101 lines
3.7 KiB

import { Database } from '../db/database';
import { CloudflareWorkerServer, SlashCreator } from 'slash-create/web';
import { isSnowflake, type Snowflake } from 'discord-snowflake';
import { AuthorCommand, GenerateCommand, ResponseCommand } from './commands';
import { type IRequestStrict, Router } from 'itty-router';
import { getQueryArray } from '../request/query';
function getAuthorization(username: string, password: string): string {
return btoa(username + ':' + password);
}
async function getToken(env: Pick<DiscordEnv, 'DISCORD_APP_ID' | 'DISCORD_APP_SECRET'>) {
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);
throw Error(text);
}
const json = await tokenResponse.json() as { access_token: string };
return 'Bearer ' + json.access_token;
}
export interface DiscordEnv {
readonly BASE_URL: string;
readonly DISCORD_APP_ID: string;
readonly DISCORD_APP_SECRET: string;
readonly DISCORD_PUBLIC_KEY: string;
readonly DISCORD_DEV_GUILD_IDS?: string;
}
interface SlashCreatorContext {
cfServer: CloudflareWorkerServer;
slashCreator: SlashCreator;
}
async function getSlashCreator(
{ DISCORD_APP_ID, DISCORD_APP_SECRET, DISCORD_PUBLIC_KEY, DISCORD_DEV_GUILD_IDS, BASE_URL }: DiscordEnv,
db: Database
): Promise<SlashCreatorContext> {
if (DISCORD_APP_ID === "" || DISCORD_APP_SECRET === "" || DISCORD_PUBLIC_KEY === "") {
throw Error("Discord is not configured on this build")
}
const server = new CloudflareWorkerServer();
const creator = new SlashCreator({
allowedMentions: { everyone: false, roles: false, users: false },
applicationID: DISCORD_APP_ID,
componentTimeouts: true,
defaultImageSize: 0,
disableTimeouts: false,
handleCommandsManually: false,
publicKey: DISCORD_PUBLIC_KEY,
unknownCommandResponse: true,
token: await getToken({ DISCORD_APP_ID, DISCORD_APP_SECRET })
});
const withGuilds: Snowflake[] = DISCORD_DEV_GUILD_IDS ? DISCORD_DEV_GUILD_IDS.split(',').flatMap(v => isSnowflake(v) ? [v] : []) : [];
creator.withServer(server);
creator.registerCommand(new GenerateCommand(creator, db));
creator.registerCommand(new AuthorCommand(creator, db));
creator.registerCommand(new ResponseCommand(creator, db, BASE_URL));
creator.registerCommand(new GenerateCommand(creator, db, withGuilds));
creator.registerCommand(new AuthorCommand(creator, db, withGuilds));
creator.registerCommand(new ResponseCommand(creator, db, BASE_URL, withGuilds));
return {
cfServer: server,
slashCreator: creator
};
}
export function discordRouter(base: string) {
const router = Router<IRequestStrict, [env: DiscordEnv, db: Database, ctx: ExecutionContext]>({ base });
router.all('/interactions', async (req, env, db, ctx) =>
(await getSlashCreator(env, db)).cfServer.fetch(req, null, ctx));
router.get('/sync', async (req, env, db, _ctx) => {
let servers = getQueryArray(req.query['server']);
const { slashCreator } = await getSlashCreator(env, db);
if (servers.length === 0) {
await slashCreator.syncCommands({
syncGuilds: true,
deleteCommands: true,
skipGuildErrors: false
});
} else {
for (const id of servers) {
await slashCreator.syncCommandsIn(id, true);
}
}
return new Response('Commands successfully synced!', {
status: 200,
statusText: 'OK'
});
});
return router;
}