Zrzur Entwickler

Ohne Bibliothek

Alles, was ein Bot tut, ist einfaches HTTPS und WebSocket. Diese Seite zeigt die Aufrufe, damit du einen Bot in jeder Sprache schreiben kannst.

Das brauchst du

Einen HTTP-Client und einen WebSocket-Client. curl reicht für die ersten Schritte. YOUR_BOT_TOKEN steht für dein Token.

Eine Anfrage stellen

curl "https://api.zrzur.com/users/@me" -H "x-bot-token: YOUR_BOT_TOKEN"

_id in der Antwort ist die ID deines Bots und auch seine Anwendungs-ID.

Anfragetexte sind JSON, mit content-type: application/json. Antworten sind JSON oder leer mit 204.

Eine Nachricht senden

curl -X POST "https://api.zrzur.com/channels/CHANNEL_ID/messages" \
  -H "x-bot-token: YOUR_BOT_TOKEN" \
  -H "content-type: application/json" \
  -d '{ "content": "Hello!" }'

Eine Datei hochladen

curl -X POST "https://cdn.zrzur.com/attachments" \
  -H "x-bot-token: YOUR_BOT_TOKEN" \
  -F "[email protected];type=text/csv"

Die Antwort ist { "id": "FILE_ID" }. Sende sie in attachments einer Nachricht.

Die ganze Schleife

  1. PUT /applications/{application_id}/commands mit deinen Befehlen.
  2. Öffne wss://events.zrzur.com/?version=1&format=json.
  3. Sende { "type": "Authenticate", "token": "YOUR_BOT_TOKEN", "intents": 33 }.
  4. Sende alle 20 Sekunden { "type": "Ping", "data": 1 }.
  5. Für jedes Paket: Ist type gleich Bulk, behandle jeden Eintrag von v.
  6. Bei InteractionCreate, innerhalb von 3 Sekunden, POST /interactions/{interaction_id}/{token}/callback.
  7. Wenn der Socket schließt, warte und beginne wieder bei Schritt 2.

Node

Node 22 hat fetch und WebSocket eingebaut. Das ist ein vollständiger Bot in einer Datei:

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

Der vollständige Bot ergänzt Ratenlimits, Wiederholungen, Wiederverbindung und gespeicherte Daten.

Python

Python braucht das Paket websockets (pip install websockets). Für HTTP reicht die Standardbibliothek.

import asyncio, json, os, urllib.request
import websockets

TOKEN = os.environ["ZRZUR_BOT_TOKEN"]
API = "https://api.zrzur.com"

def rest(method, path, body=None):
    request = urllib.request.Request(
        API + path,
        method=method,
        data=json.dumps(body).encode() if body is not None else None,
        headers={"x-bot-token": TOKEN, "content-type": "application/json"},
    )
    with urllib.request.urlopen(request) as response:
        data = response.read()
        return json.loads(data) if data else None

async def main():
    me = rest("GET", "/users/@me")
    rest("PUT", f"/applications/{me['_id']}/commands",
         [{"name": "hello", "description": "Say hello"}])

    async with websockets.connect("wss://events.zrzur.com/?version=1&format=json") as socket:
        await socket.send(json.dumps({"type": "Authenticate", "token": TOKEN, "intents": 33}))

        async def ping():
            while True:
                await asyncio.sleep(20)
                await socket.send(json.dumps({"type": "Ping", "data": 1}))
        asyncio.create_task(ping())

        async for raw in socket:
            packet = json.loads(raw)
            for event in packet["v"] if packet["type"] == "Bulk" else [packet]:
                if event["type"] == "Ready":
                    print("ready")
                if event["type"] == "InteractionCreate":
                    i = event["interaction"]
                    rest("POST", f"/interactions/{i['_id']}/{i['token']}/callback",
                         {"type": "Message", "data": {"content": "Hello from Python!"}})

asyncio.run(main())

Diese kurze Fassung verbindet nicht neu. Ergänze die Schleife aus Gateway.

Maschinenlesbare Beschreibungen

AdresseWas
GET /openapi.jsonJede REST-Route als OpenAPI 3
GET /gateway/schemaJedes Gateway-Ereignis als JSON Schema
GET /Adressen und Limits dieses Servers

Erzeuge daraus einen Client in deiner Sprache.

Weiter