# Chat-Bot bauen

Mit deinem Tool schreiben: Deine Nachricht kommt per Webhook, die Antwort über den Senden-Aufruf.

In einem Kanal mit Antworten schreibst du mit deinem Tool, zum Beispiel mit deinem eigenen KI-Agenten. Neues musst du dafür kaum lernen: Deine Nachricht kommt als Webhook-Ereignis, die Antwort ist eine ganz normale Nachricht.

**Du brauchst:** [Nachricht senden](https://packlets.app/docs/senden.md) und einen [Webhook](https://packlets.app/docs/webhook.md).

## So läuft ein Gespräch

1. Du schreibst in der App. Deine Nachricht steht sofort rechts im Verlauf.
2. Samy schickt sie als Ereignis `message` an deinen Webhook.
3. Dein Tool antwortet mit `POST /v1/push`. Die Antwort erscheint links im Verlauf und als Push.

## 1. Antworten einschalten

Leg in der App ein neues Packlet an, wähle **Mir Bescheid geben** und schalte **Antworten erlauben** ein. Trag gleich die Webhook-URL deines Tools ein. Ohne sie kommen deine Nachrichten bei deinem Tool nicht an.

Einen bestehenden Kanal machst du genauso zum Chat: In den Einstellungen des Packlets **Antworten erlauben** einschalten. Schaltest du es aus, verschwindet das Eingabefeld wieder; der Verlauf bleibt.

## 2. Nachrichten empfangen

Schreibst du im Kanal, bekommt dein Webhook dieses Ereignis:

```json
{
  "type": "message",
  "packlet_id": "9d2f7c10-3a5b-4c8e-9f21-6b7d8e9f0a12",
  "message_id": "c41e8a77-0d1f-4e6a-b2c3-91a4d5e6f708",
  "text": "Läuft der Shop wieder?",
  "sent_at": "2026-11-10T08:15:00.000Z"
}
```

In `text` steht, was du geschrieben hast, bis zu 4.000 Zeichen.

## 3. Antworten

Die Antwort ist derselbe Aufruf wie bei jeder Nachricht. Der Schlüssel sorgt dafür, dass sie im richtigen Kanal landet:

```bash
curl https://api.packlets.app/v1/push \
  -H "Authorization: Bearer $SAMY_KEY" \
  -H "Content-Type: application/json" \
  -d '{"text": "Ja. Seit 9:52 Uhr antworten alle Server."}'
```

Alles, was eine Nachricht kann, geht auch hier: [Formatierung](https://packlets.app/docs/senden.md#formatierung), [Knöpfe](https://packlets.app/docs/knoepfe.md) und [Elemente](https://packlets.app/docs/nachrichten-elemente.md). Einen Titel lässt du bei Antworten meist weg.

## Ein vollständiger Bot

Dieser Bot prüft die Signatur und antwortet auf jede Nachricht:

```python
import hashlib, hmac, os, requests
from flask import Flask, request

app = Flask(__name__)
SAMY_KEY = os.environ["SAMY_KEY"]
SECRET = hashlib.sha256(SAMY_KEY.encode()).hexdigest().encode()

def reply(text):
    requests.post("https://api.packlets.app/v1/push", timeout=10,
                  headers={"Authorization": f"Bearer {SAMY_KEY}"},
                  json={"text": text}).raise_for_status()

@app.post("/samy")
def samy_webhook():
    expected = "sha256=" + hmac.new(SECRET, request.get_data(), hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, request.headers.get("X-Samy-Signature", "")):
        return "", 401
    event = request.get_json()
    if event["type"] == "message":
        reply(f"Du hast geschrieben: {event['text']}")
    return "", 204

app.run(port=8080)
```

```typescript
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
import { createServer } from "node:http";

const SAMY_KEY = process.env.SAMY_KEY ?? "";
const secret = createHash("sha256").update(SAMY_KEY).digest("hex");

async function reply(text: string) {
  const res = await fetch("https://api.packlets.app/v1/push", {
    method: "POST",
    headers: { Authorization: `Bearer ${SAMY_KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({ text }),
  });
  if (!res.ok) throw new Error(`Samy ${res.status}: ${await res.text()}`);
}

createServer(async (req, res) => {
  let body = "";
  for await (const chunk of req) body += chunk;
  const expected = Buffer.from("sha256=" + createHmac("sha256", secret).update(body, "utf8").digest("hex"));
  const given = Buffer.from(String(req.headers["x-samy-signature"] ?? ""));
  if (expected.length !== given.length || !timingSafeEqual(expected, given)) {
    res.writeHead(401).end();
    return;
  }
  const event = JSON.parse(body) as { type: string; text?: string };
  res.writeHead(204).end(); // erst antworten, dann arbeiten
  if (event.type === "message") await reply(`Du hast geschrieben: ${event.text}`);
}).listen(8080);
```

## Wenn die Antwort länger dauert

Samy wartet nur 10 Sekunden auf die Antwort des Webhooks. Eine KI braucht oft länger. Trenn deshalb beides:

1. Bestätige den Webhook sofort mit einem Status `2xx`.
2. Lass die Antwort im Hintergrund entstehen.
3. Schick sie mit `POST /v1/push`, sobald sie fertig ist.

Bestätigst du zu spät, schickt Samy dasselbe Ereignis noch einmal, und dein Bot antwortet doppelt. Merk dir deshalb die `message_id` der Nachrichten, die du schon bearbeitest. Mehr dazu unter [Doppelte Ereignisse](https://packlets.app/docs/webhook.md#doppelte-ereignisse).

## Gut zu wissen

- **Von sich aus schreiben** darf dein Tool jederzeit, zum Beispiel eine Erinnerung. Es muss nicht auf eine Nachricht warten.
- **Den Verlauf** bekommt dein Tool nicht mitgeschickt. Jedes Ereignis enthält nur die eine neue Nachricht. Braucht dein Bot ein Gedächtnis, speichert er die Nachrichten selbst.
- **Deine Texte sind Eingaben von außen.** Führ sie nicht ungeprüft als Befehl aus, siehe [Schlüssel und Sicherheit](https://packlets.app/docs/sicherheit.md).
