Hello World
A bot that answers /hello. Two versions: one without any library, to show the protocol, and one with the SDK.
Before you start
Create an application, install it in a test server and set three environment variables (see Getting started):
export ZRZUR_BOT_TOKEN=YOUR_BOT_TOKEN
export ZRZUR_API_URL=https://api.zrzur.com
export ZRZUR_WS_URL=wss://events.zrzur.comWithout a library
Node 22 or newer, nothing to install.
// Hello World without any library: Node 22+, fetch and WebSocket only.
//
// ZRZUR_BOT_TOKEN=YOUR_BOT_TOKEN node hello-raw.mjs
const token = process.env.ZRZUR_BOT_TOKEN;
const api = process.env.ZRZUR_API_URL ?? "https://api.zrzur.com";
const ws = process.env.ZRZUR_WS_URL ?? "wss://events.zrzur.com";
if (!token) {
console.error("Set ZRZUR_BOT_TOKEN first.");
process.exit(2);
}
/** An interaction's token is in its path: it must never reach a log */
const masked = (path) =>
path.replace(/(\/interactions\/[^/]+\/)[^/]+/, "$1***");
async function rest(method, path, body) {
const response = await fetch(api + path, {
method,
headers: {
"x-bot-token": token,
...(body ? { "content-type": "application/json" } : {}),
},
body: body ? JSON.stringify(body) : undefined,
});
if (!response.ok)
throw new Error(
`${method} ${masked(path)}: ${response.status} ${await response.text()}`,
);
return response.status === 204 ? null : response.json();
}
// 1. Who am I? A bot's application id is its user id.
const me = await rest("GET", "/users/@me");
// 2. Register the global /hello command (PUT replaces the whole list).
await rest("PUT", `/applications/${me._id}/commands`, [
{ name: "hello", description: "Say hello" },
]);
// 3. Connect to the gateway and answer interactions.
const socket = new WebSocket(`${ws}/?version=1&format=json`);
// intents 33 = SERVERS (1) + INTERACTIONS (32): commands, nothing else
socket.addEventListener("open", () =>
socket.send(JSON.stringify({ type: "Authenticate", token, intents: 33 })),
);
async function handle(event) {
if (event.type === "Ready") console.log(`ready as ${me.username}`);
if (
event.type === "InteractionCreate" &&
event.interaction.command?.name === "hello"
) {
const { _id, token: interactionToken } = event.interaction;
// answer within 3 seconds
await rest("POST", `/interactions/${_id}/${interactionToken}/callback`, {
type: "Message",
data: { content: "Hello from Zrzur!" },
});
}
}
socket.addEventListener("message", ({ data }) => {
const event = JSON.parse(data);
// several events can arrive together in one Bulk packet
for (const item of event.type === "Bulk" ? event.v : [event])
handle(item).catch(console.error);
});
// keep the connection alive
setInterval(
() => socket.send(JSON.stringify({ type: "Ping", data: Date.now() })),
20_000,
);
Run it with node hello-raw.mjs.
With the SDK
You cannot download @zrzur/sdk yet, see SDKs. When you have it, this is the same bot:
// Hello World with @zrzur/sdk/bot.
//
// ZRZUR_BOT_TOKEN=... ZRZUR_API_URL=https://api.zrzur.com \
// ZRZUR_WS_URL=wss://events.zrzur.com node hello.mjs
import { Client } from "@zrzur/sdk/bot";
const bot = new Client({
token: process.env.ZRZUR_BOT_TOKEN,
apiUrl: process.env.ZRZUR_API_URL,
wsUrl: process.env.ZRZUR_WS_URL,
});
bot.command("hello", (i) =>
i.reply(`Hello, ${i.user.display_name ?? i.user.username}!`),
);
await bot.login();
await bot.syncCommands([{ name: "hello", description: "Say hello" }]);
console.log(`ready as ${bot.user.username}`);
Run it with node hello.mjs.
Try it
Type /hello in a channel of your test server.
What it shows
GET /users/@metells the bot its own id; for bots, the application id is the same.PUT /applications/{application_id}/commandsregisters the command list.- The gateway delivers
InteractionCreate; the bot answers through the interaction callback within 3 seconds. Bulkpackets are unpacked, and aPingkeeps the connection open.