Readme

A simple Discord Bot scaffolding, a slight rev on the one in the valtown guide.

The discordBot function takes in an object where each key is a Discord command and the value is a function to handle the command. If the function returns a Promise, it will be handled as a deferred interaction with a followup message.

Usage:

import { discordBot } from "https://esm.town/v/dglazkov/discordBot"; const echo = async (data) => { await new Promise((r) => setTimeout(r, 5000)); return { type: 4, data: { content: data.data.options[0].value, }, }; }; export default discordBot({ ping: () => ({ type: 4, data: { content: `Pong! It is ${new Date()}`, }, }) echo, });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { verify_discord_signature } from "https://esm.town/v/mattx/verify_discord_signature?v=8";
import { fetch } from "https://esm.town/v/std/fetch";
export type Snowflake = string;
export type DiscordUserObject = {
id: Snowflake;
username: string;
discriminator: string;
global_name?: string;
avatar?: string;
bot?: boolean;
system?: boolean;
mfa_enabled?: boolean;
locale?: string;
verified?: boolean;
email?: string;
flags?: number;
premium_type?: number;
public_flags?: number;
};
export type DiscordCommandOption = {
name: string;
type: number;
value?: string;
options?: DiscordCommandOption[];
};
export type DiscordInteractionData = {
id: Snowflake;
application_id: Snowflake;
name: string;
type: number;
token: string;
options?: DiscordCommandOption[];
};
export type DiscordInteractionObject = {
id: Snowflake;
application_id: Snowflake;
type: number;
data?: DiscordInteractionData;
guild_id?: Snowflake;
channel_id?: Snowflake;
user?: DiscordUserObject;
member?: { nick?: string; user?: DiscordUserObject };
};
export type DiscordBotResponse = {
type: number;
data?: {
content?: string;
};
user?: DiscordUserObject;
member?: { nick?: string; user?: DiscordUserObject };
};
export type DiscordBotCommandHandler = (
interaction: DiscordInteractionObject,
) => Promise<DiscordBotResponse> | DiscordBotResponse;
export type DiscordBotSpec = Record<string, DiscordBotCommandHandler>;
export const discordBot = (spec: DiscordBotSpec) => {
return async (req: Request) => {
if (req.method === "GET")
return new Response("GET Method Not Allowed", { status: 405 });
const body = await req.json();
const verified = await verify_discord_signature(
Deno.env.get("discordPublicKey"),
JSON.stringify(body),
req.headers.get("X-Signature-Ed25519"),
req.headers.get("X-Signature-Timestamp"),
);
if (!verified)
return new Response("signature invalid", {
status: 401,
statusText: "signature invalid",
});
// PING
if (body.type === 1)
return Response.json({ type: 1 }); // PONG
// APPLICATION_COMMAND interactions
if (body.type === 2) {
const handler = spec[body.data.name];
if (!handler) {
return new Response("Bad request", {
status: 400,
statusText: "Bad request",
});
}
const response = handler(body);
if (response instanceof Promise) {
(async () => {
const data = await response;
const update = await fetch(
`https://discord.com/api/v10/webhooks/${body.application_id}/${body.token}/messages/@original`,
{
method: "PATCH",
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
Comments
Nobody has commented on this val yet: be the first!
July 23, 2024