Skip to content

Flexible router for building HTTP interaction-based Discord bots with ease.

License

Notifications You must be signed in to change notification settings

Disploy/disploy

Folders and files

NameName
Last commit message
Last commit date
Mar 25, 2023
Nov 27, 2022
Feb 15, 2023
Jul 16, 2023
Nov 15, 2022
Nov 9, 2022
Nov 5, 2022
Dec 2, 2022
Nov 5, 2022
Nov 27, 2022
Oct 23, 2022
Nov 9, 2022
Dec 13, 2022
Nov 27, 2022
Nov 27, 2022
Dec 26, 2022

Repository files navigation


disploy

Vercel

Visit disploy.dev for a detailed guide!


Disploy's Discord server Tests status

Warning: We're still in development, and packages are published to npm every 12 hours to the @dev tag. You can view our v1.0.0 milestone to see what features are planned for the first release and their current status.

Disploy is a flexible router for building HTTP interaction-based Discord bots with ease. It's designed to make it easy to build, test and deploy Discord bots.

Features

Disploy features a library and an opinionated framework with tooling inspired by Next.js.

Library

Disploy does not come included with a "server", that's up to you to implement. We have a guide showcasing you how to do so with Express (Node.js) and Deno's inbuilt server.

This is a slimmed-down guide to using Disploy with Next.js as your server.

Usage with Next.js

The API entry point:

// Entrypoint - pages/api/interactions.ts
import { createNextAdapter } from 'disploy';
import { ExampleApp } from '../../lib/main';

export default createNextAdapter(ExampleApp);

Note: An "adapter" is a function that transforms requests from your server implementation of choice and creates a TRequest that's fed into App#router#entry which returns a Promise<TResponse> which your adapter should transform and return to Discord.

Setting up the Disploy App:

// Main Bot - lib/core/main.ts
import { App } from 'disploy';
import commands from './commands/commands';

const clientId = process.env.DISCORD_CLIENT_ID;
const token = process.env.DISCORD_TOKEN;
const publicKey = process.env.DISCORD_PUBLIC_KEY;

if (!clientId || !token || !publicKey) {
	throw new Error('Missing environment variables');
}

export const ExampleApp = new App({
	logger: {
		debug: true,
	},
	commands,
});

ExampleApp.start({
	clientId,
	token,
	publicKey,
});

Setting up an array of commands:

// Command Array - lib/core/commands/commands.ts
import Ping from './core/ping';

const c = [Ping];

export default c;

Example command:

import type { ChatInputInteraction, Command } from 'disploy';

const Ping: Command = {
	name: 'ping',
	description: 'pong!',

	run(interaction: ChatInputInteraction) {
		interaction.reply({
			content: 'Hello World!',
		});
	},
};

export default Ping;

Framework

Disploy comes inbuilt with a CLI that can bundle your bot based on a file system structure, which is inspired by Next.js.

Use the "TypeScript Framework" boilerplate from create-disploy-app.

npx create-disploy-app@latest

Here are two examples, a command and a message component handler. Keep in mind none of this is exclusive to the framework, the only "framework exclusive" feature showcased here is the file structure and default exports.

// Example command - commands/ping.ts
import type { Command } from 'disploy';

export default {
	// Command "data"
	name: 'ping',
	description: 'pong!',

	// Command entrypoint
	async run(interaction) {
		if (!interaction.guild) {
			return void interaction.reply({
				content: 'You must use this in a guild.',
			});
		}

		interaction.deferReply(); // Synchronously reply to the incoming HTTP request
		const guild = await interaction.guild.fetch(); // BaseInteraction#guild is a ToBeFetched class, awaiting fetch on it will return the full structure

		// All our methods take in raw JSON (or our Message structure, coming soon)
		return void interaction.editReply({
			content: 'hello world!!!!!!!!',
			components: [
				{
					type: 1,
					components: [
						{
							type: 2,
							label: 'Click me!',
							style: 1,
							custom_id: `ping-${interaction.user.id}`, // You can handle message components with express-like routes.
						},
					],
				},
			],
		});
	},
} satisfies Command;
// Example message component handler - handlers/ping.ts
import type { ButtonHandler } from 'disploy';

export default {
	customId: 'ping-:userId',

	async run(interaction) {
		const originalUser = await interaction.params.getUserParam('userId'); // This fetches a user structure from the interaction's params, it would be better to use getParam in this use case, but we're showcasing the getUserParam method here.
		const clicker = interaction.user;

		return void interaction.reply({
			content: `hello world!!!!!!!! (clicked by ${clicker}) [made by ${originalUser}]`,
		});
	},
} satisfies ButtonHandler;
disploy dev # test your bot locally with hot-reloading and tunneling
disploy deploy # deploy your bot to Cloudflare Workers

The CLI bundles your app by taking in commands and message components and turning them into a single bundle. It accomplishes this by transforming your default exports into an array, creating an App instance, and attaching an adapter for your specified target.

Planned Features

Testing

@disploy/disbench will be a testing library that will allow you to test your bot in a similar way to how you would test a web app with a mocked Discord API. View the repository here.

Example usage (this is not final):

// Disbench demo snippet (fake code)
import { Disbench } from '@disploy/disbench';

const disbench = new Disbench({
	app: 'dist/bot.js',
});

await disbench.setup(); // This will start the bot and start communicating with the framework to "deploy" commands to the mocked API

const echoCommand = disbench.commands.find({ name: 'echo' });

const response = await disbench.interact(echoCommand, {
	options: {
		message: 'Hello World!',
	},
});

expect(response).toEqual('Hello World!');

Join our Discord server for support and updates!