Zrzur Developers

Start your bot

In about ten minutes you will have a bot that answers a slash command in your own test server. You need a Zrzur account, a server of your own and Node.js 22 or newer. There is nothing to install.

1. Create an application

In the Developer Portal, choose New application, give it a name and choose Create. The application comes with a bot account. Open My applications

2. Copy the token

The token appears once, right after you create the application. Copy it now. Whoever has the token controls the bot.

3. Install the bot in a test server

On the Overview tab of your application choose Add to a server, and pick a server where you have Manage Server. More about installing

Do this before the first run. A bot that is not in the server gets 403 MissingPermission when it writes to one of its channels, which looks like a missing permission. Is the bot in that server?

4. No library needed

Node.js 22 has everything built in: the API is plain HTTP and WebSocket. The official library, @zrzur/sdk, cannot be downloaded yet. About the SDKs

5. Write your first bot

Save this as hello-raw.mjs in an empty folder. It registers /hello and answers it.

// 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,
);

6. Run it

Set the token, then start the bot:

mkdir my-bot
cd my-bot
# save hello-raw.mjs here, then:
export ZRZUR_BOT_TOKEN=YOUR_BOT_TOKEN
node hello-raw.mjs

When the terminal shows ready, type /hello in a channel of your test server. The bot answers Hello from Zrzur!

7. Receive events

The bot keeps a WebSocket connection to the gateway. Commands, button clicks, new messages and member changes arrive there as events. Gateway

8. Build a real bot

This bot stops when the line drops and knows only one command. The complete bot has commands, buttons, a form, moderation, saved data, error handling and reconnects, in one file. Build a complete bot

Next steps