Der Server ist kein Tool aus der Neunerliste, aber Broadcaster, Recap und Clipper posten dorthin. Deshalb steht die Struktur als Code im Repo und nicht nur in einer Oberflaeche. packages/discord: - permissions.ts Rechtebits als BigInt, das Feld ist 64 Bit breit - api.ts REST-Client: Warteschlange, Rate Limits, lesbare Fehler - plan.ts Rollen, Kanaele, Rechte, AutoMod, Willkommenstext - apply.ts traegt den Bauplan auf einen bestehenden Server auf Neuer Befehl `abdulive discord:setup [--server ID] [--vorschau]`. Zugeschnitten auf 5 bis 30 gleichzeitige Zuschauer: fuenf sichtbare Kanaele statt zwanzig, und eine selbst vergebene Rolle Live-Ping statt eines Rundrufs an alle bei jedem Stream. Wiederholbar statt einmalig: gefunden wird ueber Namen und frueheren Namen, danach wird angeglichen. Der zweite Lauf aendert nichts. Geloescht wird nie, und von Hand gesetzte Rechte-Ausnahmen bleiben stehen. Drei Dinge, die am echten Server nachgemessen und nicht angenommen wurden: - Discord schreibt Textkanaele klein und macht aus Leerzeichen Bindestriche; Emoji, Umlaute und U+2502 bleiben. normalizeName() bildet das ab, sonst geht bei jedem Lauf dieselbe Umbenennung erneut raus. - Kanalnamen lassen sich nur zweimal je zehn Minuten aendern, danach verlangt Discord bis zu neun Minuten Pause. Der Lauf wartet hoechstens eine Minute und meldet den Rest. - Eine Rolle kann nur Rollen unterhalb der eigenen verschieben, auch mit Administrator. rolePositions() rechnet den Platz unter der Bot-Rolle aus, statt blind 3, 2, 1 zu verlangen. Zugangsdaten laufen ueber den verschluesselten Speicher (ADR-0005): der Bot-Token wird verdeckt abgefragt, die drei Webhook-URLs werden nie ausgegeben und nie geloggt. Im Quelltext bleibt es bei ae/oe/ue, alles was ein Mitglied auf Discord liest steht in richtigem Deutsch. Ein Test haelt das fest. Begruendung, Fallstricke und die Schritte von Hand: docs/projects/discord-community.md Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
246 lines
11 KiB
Markdown
246 lines
11 KiB
Markdown
# Discord-Community
|
|
|
|
**Wirkrichtung:** Bindung. Twitch ist der Ort, an dem Leute zuschauen. Discord
|
|
ist der Ort, an dem sie zwischen den Streams noch da sind. Ohne den zweiten Ort
|
|
faengt jeder Stream bei null an.
|
|
|
|
Der Server ist kein Tool aus der Neunerliste, aber drei davon posten dorthin:
|
|
[Go-Live-Broadcaster](go-live-broadcaster.md), [Stream-Recap](stream-recap.md)
|
|
und [Clip-to-Shorts](clip-shorts.md). Deshalb steht die Struktur hier im Repo
|
|
und nicht nur in einer Oberflaeche.
|
|
|
|
## Leitgedanke: klein anfangen
|
|
|
|
Der Kanal hat 5 bis 30 gleichzeitige Zuschauer. Daraus werden realistisch 20
|
|
bis 100 Discord-Mitglieder, von denen eine Handvoll schreibt.
|
|
|
|
Ein Server mit zwanzig Kanaelen sieht bei dieser Groesse nicht nach einer
|
|
grossen Community aus, sondern nach einer verlassenen. Zwanzig leere Kanaele
|
|
sagen jedem Neuankoemmling: hier ist nichts los. Fuenf Kanaele, in denen etwas
|
|
steht, sagen das Gegenteil.
|
|
|
|
Also: **fuenf sichtbare Kanaele.** Wachstum kommt spaeter, und nur dann, wenn
|
|
ein bestehender Kanal wirklich zu voll wird. Wann das so weit ist, steht weiter
|
|
unten unter [Wann ein Kanal dazukommt](#wann-ein-kanal-dazukommt).
|
|
|
|
## Die Struktur
|
|
|
|
Der Bauplan liegt in [`packages/discord/src/plan.ts`](../../packages/discord/src/plan.ts)
|
|
und ist die massgebliche Fassung. Was hier steht, ist die Begruendung dazu.
|
|
|
|
### Kanaele
|
|
|
|
| Kategorie | Kanal | Wer schreibt | Wofuer |
|
|
|---|---|---|---|
|
|
| 📌 Info | `👋│willkommen` | niemand | Die drei Regeln, einmal gepostet |
|
|
| 📌 Info | `📢│ankündigungen` | nur Webhooks | Stream-Start und Recap; Diskussion in Threads |
|
|
| 🎮 Community | `💬│allgemein` | alle | Der Hauptkanal. Im Zweifel hier |
|
|
| 🎮 Community | `🎬│clips` | alle + Clip-Knopf | Gute Momente |
|
|
| 🎮 Community | `🔊 Zusammen zocken` | — | Sprachkanal |
|
|
| 🔒 Intern | `🔨│mod-chat` | Mods | Fuer alle anderen unsichtbar |
|
|
| 🔒 Intern | `🤖│technik-log` | Toolkit | Probelaeufe, Fehler, AutoMod-Treffer |
|
|
|
|
Zu den Namen: Discord schreibt Textkanaele klein und macht aus Leerzeichen
|
|
Bindestriche. Emoji, Umlaute und das Trennzeichen `│` (U+2502) bleiben stehen -
|
|
am echten Server nachgemessen, nicht angenommen. `normalizeName()` bildet das
|
|
ab, damit nicht bei jedem Lauf dieselbe Umbenennung erneut rausgeht.
|
|
|
|
Und zur Schreibweise: im Quelltext bleibt es bei `ae/oe/ue` wie im uebrigen
|
|
Repo, aber alles, was ein Mitglied auf Discord liest, steht in richtigem
|
|
Deutsch. „Sei anstaendig" liest sich wie ein Tippfehler, und der Text im
|
|
Server ist der erste Eindruck, den jemand davon bekommt.
|
|
|
|
`📢│ankündigungen` ist schreibgeschuetzt, erlaubt aber Reaktionen und Threads.
|
|
So bleibt die Kanalansicht eine saubere Liste von Streams, und die Diskussion
|
|
ueber das Recap haengt trotzdem direkt daran.
|
|
|
|
Der Sprachkanal ist bewusst dabei, obwohl er anfangs leer sein wird. Ein
|
|
Sprachkanal, den zwei Leute nutzen, ist der Anfang; einer, den es nicht gibt,
|
|
wird nie einer.
|
|
|
|
### Rollen
|
|
|
|
Die Reihenfolge ist zugleich die Rangfolge auf dem Server.
|
|
|
|
| Rolle | Sichtbar | Wofuer |
|
|
|---|---|---|
|
|
| `Abdu` | ja | Der Kanal selbst. Administrator |
|
|
| `Mod` | ja | Loeschen, stummschalten, rauswerfen |
|
|
| `Stammgast` | ja | Auszeichnung fuer Leute, die regelmaessig da sind |
|
|
| `Live-Ping` | nein | Wird bei Stream-Start angepingt, selbst vergeben |
|
|
|
|
**`Abdu`** traegt `Administrator`. Das Recht schliesst alle anderen ein und
|
|
setzt jede Kanal-Ausnahme ausser Kraft, auch kuenftige. Es gehoert deshalb an
|
|
genau ein Konto und an kein zweites. Ein Test im Repo haelt fest, dass keine
|
|
andere Rolle es bekommt.
|
|
|
|
**`Live-Ping` ist die wichtigste der vier.** Ein Rundruf an alle bei jedem
|
|
Stream ist der schnellste Weg, dass die Leute den Server stummschalten - und
|
|
ein stummgeschalteter Server ist ein toter Server. Angepingt wird nur, wer sich
|
|
die Rolle selbst geholt hat. Das sind weniger Leute, aber es sind die, die
|
|
kommen.
|
|
|
|
**`Stammgast`** hat fast keine zusaetzlichen Rechte, und das ist Absicht. Der
|
|
Wert liegt darin, dass der Name farbig ist und in der Mitgliederliste oben
|
|
steht. Bei dieser Kanalgroesse ist sichtbare Zugehoerigkeit der wirksamste
|
|
Hebel, den es gibt, und er kostet nichts.
|
|
|
|
**`Mod`** bekommt kein `Server verwalten`, kein `Rollen verwalten` und kein
|
|
`Kanaele verwalten`. Moderation heisst aufraeumen, nicht umbauen. Wer umbauen
|
|
soll, bekommt das Recht einzeln.
|
|
|
|
### Was @everyone darf
|
|
|
|
Gesetzt wird eine vollstaendige Liste statt Discords Vorgabe - die aendert sich
|
|
mit der Zeit, und dann steht irgendwann ein Recht drin, das niemand vergeben
|
|
hat. Ausdruecklich **nicht** enthalten: `@everyone` erwaehnen, TTS-Nachrichten,
|
|
private Threads, saemtliche Verwaltungsrechte.
|
|
|
|
Ausdruecklich **enthalten**: Einladungen erstellen. Ein Server, in den
|
|
Mitglieder niemanden einladen koennen, waechst nur so schnell, wie Abdu selbst
|
|
Links verteilt.
|
|
|
|
### Serverweit
|
|
|
|
- **Sicherheitsstufe „mittel"** - das Konto muss aelter als fuenf Minuten sein.
|
|
Kostet echte Leute nichts und haelt Wegwerf-Konten draussen. Ein oeffentlich
|
|
von Twitch aus verlinkter Server bekommt die.
|
|
- **Benachrichtigungen nur bei Erwaehnung.** Sonst klingelt jede Nachricht bei
|
|
jedem. Siehe oben, stummgeschalteter Server.
|
|
- **Zwei AutoMod-Regeln**: Erwaehnungs-Spam (mehr als fuenf auf einmal) und
|
|
Discords eigene Spam-Erkennung. Beide blocken die Nachricht und melden sie in
|
|
`🤖│technik-log`. Kein Wortfilter - der kostet bei dieser Groesse mehr Aerger
|
|
durch Fehlalarme, als er an Spam verhindert.
|
|
|
|
## Einrichten
|
|
|
|
### 1. Von Hand: Server und Bot
|
|
|
|
Beides geht nur ueber die Oberflaeche, ein Skript darf das nicht.
|
|
|
|
1. **Server anlegen.** In Discord links unten auf `+`, „Eigenen erstellen",
|
|
Name und Bild setzen.
|
|
2. **Entwicklermodus einschalten** (Einstellungen > Erweitert), dann Rechtsklick
|
|
auf den Server > „Server-ID kopieren".
|
|
3. **App anlegen** unter <https://discord.com/developers/applications>:
|
|
„New Application", dann links „Bot". Dort „Reset Token", Token kopieren.
|
|
Er wird genau einmal angezeigt.
|
|
4. **Bot einladen.** Client-ID aus „General Information" einsetzen:
|
|
|
|
```
|
|
https://discord.com/oauth2/authorize?client_id=DEINE_CLIENT_ID&permissions=805375024&scope=bot
|
|
```
|
|
|
|
Die Zahl sind genau die sieben Rechte, die das Skript braucht: Kanaele,
|
|
Rollen, Webhooks und Server verwalten, lesen, schreiben, Verlauf lesen.
|
|
Kein Administrator.
|
|
|
|
### 2. Der Befehl
|
|
|
|
```powershell
|
|
abdulive discord:setup --server <SERVER-ID> --vorschau
|
|
```
|
|
|
|
Die Vorschau liest den Server aus und zeigt Zeile fuer Zeile, was passieren
|
|
wuerde. Sie schreibt nichts. Sieht das gut aus, dasselbe ohne `--vorschau`:
|
|
|
|
```powershell
|
|
abdulive discord:setup --server <SERVER-ID>
|
|
```
|
|
|
|
Der Bot-Token wird beim ersten Lauf abgefragt (die Eingabe wird nicht
|
|
angezeigt) und danach zusammen mit der Server-ID verschluesselt abgelegt -
|
|
siehe [ADR-0005](../decisions/ADR-0005-zugangsdaten.md). Ab dem zweiten Lauf
|
|
genuegt `abdulive discord:setup`.
|
|
|
|
Die drei Webhook-URLs landet das Skript direkt im selben Speicher, unter
|
|
`goLiveWebhook`, `recapWebhook` und `clipsWebhook`. Sie werden nie ausgegeben
|
|
und nie geloggt - Broadcaster, Recap und Clipper lesen sie von dort.
|
|
|
|
### 3. Von Hand: der Rest
|
|
|
|
Vier Dinge kann die API nicht oder nicht sinnvoll:
|
|
|
|
1. **Dir selbst `Abdu` geben** und die Bot-Rolle in den Servereinstellungen
|
|
ueber `Abdu` ziehen. Eine Rolle kann nur Rollen unterhalb der eigenen
|
|
verschieben - auch mit Administrator. Discord haengt die Bot-Rolle beim
|
|
Einladen ganz unten ein, dort ist also kein Platz fuer vier Rollen darunter.
|
|
Bis das erledigt ist, meldet das Skript die Rangfolge als uebersprungen und
|
|
nennt den Grund.
|
|
2. **Onboarding einrichten** (Servereinstellungen > Onboarding) und `Live-Ping`
|
|
als waehlbare Rolle anbieten. Das ist der Weg, auf dem sich die Leute die
|
|
Rolle selbst holen - ohne ihn bringt sie nichts.
|
|
3. **Einladungslink** erzeugen, ohne Ablaufdatum, und ins Twitch-Panel, in die
|
|
Kanalinfo und in einen Chat-Befehl eintragen. Ein Server, den im Stream
|
|
niemand erwaehnt, bleibt leer.
|
|
4. **Twitch-Konto verbinden** (Servereinstellungen > Integrationen). Danach
|
|
bekommen Subs automatisch eine Rolle.
|
|
|
|
## Was das Skript nicht tut
|
|
|
|
**Es loescht nichts.** Keine Kanaele, keine Rollen, keine Nachrichten. Ein
|
|
Einrichtungsskript, das aufraeumt, raeumt irgendwann das Falsche weg. Was von
|
|
Hand dazugekommen ist, bleibt stehen - auch eigene Rechte-Ausnahmen in einem
|
|
Kanal, den das Skript anfasst.
|
|
|
|
**Es ueberschreibt den Willkommenstext nicht.** Gepostet wird nur, wenn
|
|
`#willkommen` leer ist. Danach gehoert der Text der Hand, die ihn zuletzt
|
|
angefasst hat.
|
|
|
|
**Es laesst von Discord verwaltete Rollen in Ruhe.** Rollen, die zu einer App
|
|
oder zur Twitch-Anbindung gehoeren, meldet es als uebersprungen.
|
|
|
|
Wiederholtes Ausfuehren ist der Normalfall: gefunden wird ueber den Namen,
|
|
danach wird angeglichen statt neu angelegt. Der zweite Lauf aendert nichts.
|
|
|
|
## Wann ein Kanal dazukommt
|
|
|
|
Nicht auf Verdacht. Jeweils erst, wenn die Bedingung wirklich eintritt:
|
|
|
|
| Neuer Kanal | Erst wenn |
|
|
|---|---|
|
|
| `#vorschlaege` | In `#allgemein` gehen Vorschlaege zwischen anderem unter |
|
|
| `#fotos` oder `#offtopic` | `#allgemein` besteht ueberwiegend aus einem Thema, das nicht der Stream ist |
|
|
| Spiel-eigener Kanal | Zu einem Spiel gibt es ueber Wochen taeglich Gespraech |
|
|
| Zweiter Sprachkanal | Der erste ist regelmaessig belegt, wenn jemand dazu will |
|
|
| Forum statt `#clips` | Mehr als etwa zehn Clips pro Woche |
|
|
|
|
Umgekehrt gilt genauso: ein Kanal, in dem vier Wochen nichts steht, kommt weg.
|
|
|
|
## Fallstricke
|
|
|
|
- **Die Bot-Rolle steht anfangs unten.** Discord haengt sie beim Einladen
|
|
unterhalb bestehender Rollen ein. Dann schlaegt das Setzen der Rangfolge
|
|
fehl. Das Skript bricht deswegen nicht ab, sondern meldet es - nachziehen und
|
|
noch einmal laufen lassen.
|
|
- **Von Hand angelegte Webhooks geben ihren Token nicht heraus.** Wer einen
|
|
Webhook in der Oberflaeche neu anlegt, bekommt eine URL, die das Skript nicht
|
|
auslesen kann. Es meldet ihn als uebersprungen; loeschen und noch einmal
|
|
laufen lassen loest es.
|
|
- **Kanalnamen lassen sich nur zweimal in zehn Minuten aendern.** Discord
|
|
verlangt danach bis zu neun Minuten Pause. Der Lauf wartet hoechstens eine
|
|
Minute, meldet den Kanal als uebersprungen und macht weiter - spaeter noch
|
|
einmal ausfuehren holt es nach.
|
|
- **Umbenennen statt verdoppeln.** Aendert sich ein Name im Bauplan, gehoert
|
|
der alte in `aliases`. Sonst findet das Skript den Kanal nicht wieder und
|
|
legt einen zweiten daneben an. Ein Test besteht darauf, dass jeder Eintrag
|
|
einen frueheren Namen hinterlegt hat.
|
|
- **Ankuendigungskanaele im Discord-Sinn** (die, denen andere Server folgen
|
|
koennen) brauchen einen Community-Server, und der braucht Regelkanal,
|
|
Update-Kanal und einige Einstellungen. Fuer diese Groesse bringt das nichts,
|
|
deshalb ist `#ankuendigungen` ein normaler Textkanal mit Schreibschutz.
|
|
- **Ein Ping ist keine Reichweite.** Wer `Live-Ping` nicht anbietet und
|
|
stattdessen `@everyone` benutzt, hat beim dritten Mal einen stummen Server.
|
|
|
|
## Abnahmekriterien
|
|
|
|
- `abdulive discord:setup --vorschau` laeuft auf einem frischen Server durch und
|
|
meldet keinen uebersprungenen Punkt.
|
|
- Nach dem echten Lauf existieren drei Kategorien, sieben Kanaele, vier Rollen
|
|
und drei Webhooks; `👋│willkommen` enthaelt genau eine Nachricht.
|
|
- Ein zweiter Lauf meldet ausschliesslich „steht" und legt nichts doppelt an.
|
|
- Ein Konto ohne Rolle sieht `Intern` nicht und kann in `#ankuendigungen` nicht
|
|
schreiben, aber einen Thread eroeffnen.
|
|
- Die Webhook-URLs stehen im verschluesselten Speicher und tauchen in keiner
|
|
Ausgabe und keinem Log auf.
|