# Events (https://docs.pixl.lennox.zip/sdk/events)

> Auf alles reagieren, was in deinem Kanal passiert

Mit `pixl.on()` meldest du einen Handler für ein Event an. Sobald auf deinem Twitch-Kanal etwas passiert, ruft das SDK ihn mit den Daten des Events auf. Alle Funktionen rund um Events stehen in der [SDK-Referenz](/sdk/reference#events).

```js
pixl.on('twitch.follow', (data) => {
  showAlert(`${data.username} folgt jetzt!`);
});

pixl.on('twitch.cheer', (data) => {
  showAlert(`${data.username} hat ${data.bits} Bits geschickt`);
});
```

`on()` gibt die SDK-Instanz zurück, du kannst die Aufrufe also verketten. Mit `pixl.on('*', …)` bekommst du alle Events.

## Event-Referenz [#event-referenz]

Über `pixl.on()` kommt jedes Event aus der Tabelle an. Die mit ✅ in der Spalte **Eventlog** speichert pixl außerdem: Sie stehen im Dashboard unter **Events**, lassen sich dort wiederholen und kommen auch über `pixl.events()` und [`pixl.feed()`](/sdk/feed), jeweils mit `id` und Zeitpunkt `at`. Zwischenstände (`*_progress`), Chat und Moderation speichert pixl nicht, die gibt es nur live.

| Event | Daten | Wann | Eventlog |
|---|---|---|:-:|
| `twitch.follow` | `{ username }` | Neuer Follower | ✅ |
| `twitch.sub` | `{ username, login, tier, months?, streak?, durationMonths?, message? }` | Neuer Sub oder Resub. `tier`: `Prime`, `1000`, `2000` oder `3000`. Beim Resub: `months` = Monate insgesamt, `streak` = Monate am Stück (fehlt, wenn die Person sie nicht teilt), `message` = ihre Nachricht. `durationMonths` = im Voraus bezahlte Monate. Verschenkte Subs kommen nur als `twitch.sub_gift`, nie zusätzlich als `twitch.sub` | ✅ |
| `twitch.sub_gift` | `{ username, login?, tier, amount, community?, months?, recipient?, recipientLogin?, recipients?, totalGifted? }` | Verschenkte Subs, `amount` = Anzahl. Mit `community: true` gingen sie an zufällige Zuschauer (immer ein Monat, auch bei `amount: 1`). Die Empfänger trägt pixl danach in `recipients` (`{ username, login? }[]`) ein: Im Live-Event fehlen sie noch, nach ein paar Sekunden stehen sie vollständig in der Historie (`pixl.events()`). Sonst ein Geschenk an eine Person: `recipient` und `recipientLogin`, `months` = geschenkte Monate. `totalGifted` = alle bisher verschenkten Subs der Person, bei anonymen Geschenken ist `username` `Anonym` | ✅ |
| `twitch.cheer` | `{ username, bits }` | Bits geschickt | ✅ |
| `twitch.raid` | `{ from, viewerCount }` | Jemand raidet deinen Kanal | ✅ |
| `twitch.channel_point` | `{ username, reward, cost, input }` | Belohnung mit Kanalpunkten eingelöst, eigene oder von Twitch (z.B. Sub-Emote freischalten) | ✅ |
| `twitch.channel_point_resolved` | `{ username, reward, status }` | Einlösung erledigt oder erstattet. `status`: `fulfilled` oder `canceled` | |
| `twitch.power_up` | `{ username, title, bits, input }` | Eigenes Power-up eingelöst | ✅ |
| `twitch.poll_started` | `{ title, choices }` | Umfrage gestartet | ✅ |
| `twitch.poll_progress` | `{ title, results }` | Zwischenstand bei jeder Stimme, `results` = `{ title, votes }[]` | |
| `twitch.poll_ended` | `{ title, status, results }` | Umfrage beendet. `status`: `completed`, `archived` oder `terminated`, `results` = `{ title, votes }[]` | ✅ |
| `twitch.prediction_started` | `{ title, outcomes }` | Vorhersage gestartet | ✅ |
| `twitch.prediction_progress` | `{ title, results }` | Zwischenstand bei jedem Einsatz, `results` = `{ title, points }[]` | |
| `twitch.prediction_locked` | `{ title, results }` | Einsätze geschlossen, aber noch nicht aufgelöst (siehe `twitch.prediction_ended`). `results` = `{ title, points }[]`, eingesetzte Kanalpunkte je Ausgang | ✅ |
| `twitch.prediction_ended` | `{ title, status, winningOutcome?, results }` | Vorhersage aufgelöst oder abgebrochen. `status`: `resolved` oder `canceled`, `results` wie oben | ✅ |
| `twitch.hype_train_started` | `{ level, progress, goal, total, expiresAt, type, startedAt, shared, participants, topContributions }` | Hype Train beginnt | ✅ |
| `twitch.hype_train_progress` | wie `twitch.hype_train_started` | Hype Train kommt voran | |
| `twitch.hype_train_ended` | `{ level, total, progress?, goal?, type, startedAt, shared, participants, topContributions }` | Hype Train endet. `progress` und `goal` = letzter Stand im letzten Level (fehlen, wenn pixl während des Zugs neu gestartet ist) | ✅ |
| `twitch.goal_started` | `{ type, description, current, target }` | Ziel des Kanals beginnt | |
| `twitch.goal_progress` | `{ current, target }` | Ziel kommt voran | |
| `twitch.goal_ended` | `{ type, description, current, target, achieved }` | Ziel endet | |
| `twitch.charity_started` | `{ name, target, currency, decimalPlaces }` | Spendenaktion gestartet | ✅ |
| `twitch.charity_progress` | `{ current, target, currency, decimalPlaces }` | Spendenaktion kommt voran | |
| `twitch.charity_donated` | `{ username, name, amount, currency, decimalPlaces }` | Spende an die Spendenaktion | ✅ |
| `twitch.charity_stopped` | `{ name, current, target, currency, decimalPlaces }` | Spendenaktion beendet | ✅ |
| `twitch.ad_break` | `{ durationSec, isAutomatic, startedAt? }` | Werbepause beginnt. Sie endet bei `startedAt` + `durationSec` (`startedAt` fehlt in alten Einträgen) | ✅ |
| `streamelements.tip` | `{ username, amount, currency, message, tipId }` | Spende über [StreamElements](/streamelements). `amount` ist schon ein Dezimalbetrag (`4.2`), `username` der Name, den die Person angegeben hat (kein Twitch-Login), `tipId` die ID bei StreamElements. Testspenden aus dem StreamElements-Dashboard haben `data.test: true` und kommen mit `replay: true`, live wie aus der Historie, damit Zähler sie wie Wiederholungen überspringen | ✅ |
| `twitch.shoutout_sent` | `{ to, viewerCount }` | Du gibst einen Shoutout | |
| `twitch.title_changed` | `{ title }` | Titel ändert sich während des Streams. pixl meldet nur, was sich wirklich geändert hat, auch wenn Twitch Titel und Kategorie immer zusammen schickt | ✅ |
| `twitch.category_changed` | `{ category }` | Kategorie ändert sich während des Streams | ✅ |
| `twitch.ban` | `{ username, moderator, reason, isPermanent, endsAt }` | Jemand wird gebannt oder bekommt einen Timeout | |
| `twitch.unban` | `{ username, moderator }` | Bann aufgehoben | |
| `twitch.vip_added` / `twitch.vip_removed` | `{ username }` | VIP vergeben oder entzogen | |
| `twitch.moderator_added` / `twitch.moderator_removed` | `{ username }` | Mod-Status vergeben oder entzogen | |
| `twitch.watch_streak` | `{ username, streak, channelPoints }` | Jemand hat eine bestimmte Zahl von Streams am Stück geschaut | ✅ |
| `twitch.modiversary` | `{ username, months }` | Jemand ist seit `months` Monaten Mod in deinem Kanal | |
| `stream.online` | `{ streamId, title, category, url, thumbnailUrl }` | Du bist live gegangen | ✅ |
| `stream.offline` | `{ durationSec? }` | Stream beendet | ✅ |
| `subs.updated` | `{ total, points, at }` | Die Sub-Zahl laut Twitch hat sich geändert (siehe `pixl.subs()`). Braucht `subs:read` statt `events:read` | |
| `spotify.now_playing` | `{ playing, progressMs, at, track, addedBy }` | Anderer Song, Pause, Fortsetzen oder Sprung im Song (siehe `pixl.nowPlaying()`). Braucht `spotify:read` statt `events:read` | |
| `twitch.chat_message` | `{ username, login, userId, messageId, text, color, emotes, badges, paint, html }` | Chat-Nachricht mit Badges und Emotes von Twitch, 7TV, BTTV und FFZ. `html` baut das SDK, siehe [Chat](/sdk/chat) | |
| `twitch.chat_cleared` | `{ }` | Chat komplett geleert | |
| `twitch.chat_user_cleared` | `{ username }` | Alle Nachrichten einer Person gelöscht | |
| `twitch.chat_message_deleted` | `{ username, messageId }` | Einzelne Nachricht gelöscht | |
| `twitch.chat_settings_updated` | `{ emoteMode, followerMode, followerModeDuration, slowMode, slowModeWait, subscriberMode, uniqueChatMode }` | Chat-Einstellungen geändert | |
| `twitch.chat_message_held` | `{ username, text, messageId }` | AutoMod hält eine Nachricht zurück | |
| `twitch.chat_message_held_updated` | `{ username, status, messageId }` | Status einer zurückgehaltenen Nachricht ändert sich, z.B. freigegeben | |
| `seventv.emote_added` / `seventv.emote_removed` | `{ emote, actor }` | Emote im 7TV-Set des Kanals hinzugefügt oder entfernt | |
| `seventv.emote_renamed` | `{ emote, oldName, actor }` | Emote im 7TV-Set umbenannt | |
| `seventv.emote_set_changed` | `{ setId, name, actor }` | Kanal nutzt ein anderes 7TV-Set | |
| `twitch.shared_chat_started` / `twitch.shared_chat_updated` | `{ sessionId, participants }` | Geteilter Chat beginnt oder ändert sich | |
| `twitch.shared_chat_ended` | `{ sessionId }` | Geteilter Chat endet | |
| `twitch.guest_star_session_started` / `twitch.guest_star_session_ended` | `{ sessionId }` | Guest-Star-Sitzung beginnt oder endet | |
| `twitch.guest_star_guest_updated` | `{ sessionId, guest, state, slotId }` | Status eines Gastes ändert sich | |
| `twitch.guest_star_settings_updated` | `{ slotCount, groupLayout }` | Guest-Star-Einstellungen geändert | |

## Beispiel: Raid-Alert [#beispiel-raid-alert]

```js
const pixl = new Pixl({ key: 'pixl_DEIN_SCHLÜSSEL' });

pixl.on('twitch.raid', (data, event) => {
  if (event.replay) return; // im Dashboard wiederholte Raids ignorieren
  document.querySelector('#alert').textContent =
    `${data.from} raidet mit ${data.viewerCount} Leuten!`;
});
```

Für Widgets wie „Bits seit Stream-Start“ nimmst du [Historie + live](/sdk/feed).
