Artikel

Moderationsbefehle und die Frage, wer eigentlich was darf

Kick, Bann, Timeout — technisch in einer Stunde gebaut. Die eigentliche Arbeit steckt in der Frage, wer sie benutzen darf, und in dem, was man hinterher noch nachvollziehen kann.

BlackZackBlackzack

1063 Wörter · 5 Min. Lesezeit

  • yurna
  • discord
  • rechte

Als der Server größer wurde, kam der Wunsch nach Moderationsbefehlen. Jemanden aus einem Sprachkanal holen, verwarnen, im Notfall ausschließen. Der Code dafür ist überschaubar — die Bibliothek nimmt einem alles ab:

await mitglied.kick("Regelverstoß: Spam");
await mitglied.ban({ days: 1, reason: "Werbung in DMs" });

Zwei Zeilen. Und trotzdem habe ich an diesem Thema wochenlang gearbeitet, weil die Zeilen der einfache Teil sind.

Die vier Fragen, die vor jedem Moderationsbefehl stehen

Darf der Aufrufende das? Klingt offensichtlich. In der Praxis reicht „ist Admin" nicht, weil es Abstufungen gibt: Jemand darf verwarnen, aber nicht bannen. Jemand darf in einem Kanal moderieren, aber nicht überall.

Darf der Bot das? Der Bot hat eigene Rechte, und die sind nicht dieselben. Ein Bot kann niemanden ausschließen, dessen höchste Rolle über seiner eigenen liegt. Das ist keine Einstellung, sondern eine harte Regel von Discord — und der Fehler, der dabei zurückkommt, ist unspezifisch genug, um einen eine Stunde zu kosten.

Darf der Aufrufende das bei dieser Person? Ein Moderator sollte keinen anderen Moderator bannen können, und schon gar nicht den Serverbesitzer. Das prüft Discord nicht für dich, das musst du selbst tun:

if (ziel.roles.highest.position >= aufrufer.roles.highest.position) {
  return "Diese Person steht in der Rollenhierarchie nicht unter dir.";
}
if (ziel.id === nachricht.guild.ownerId) {
  return "Den Serverbesitzer kann niemand moderieren.";
}

Was passiert, wenn es schiefgeht? Eine Moderationshandlung, die zur Hälfte durchgeht, ist schlimmer als eine, die scheitert. Wenn der Bann klappt, aber der Protokolleintrag nicht geschrieben wird, weiß hinterher niemand, warum jemand weg ist.

Die Reihenfolge, die ich mir angewöhnt habe

Nach ein paar peinlichen Fehlern bin ich bei einem festen Ablauf gelandet, den ich bis heute in jedem sicherheitsrelevanten Befehl benutze:

  1. Eingabe prüfen — gibt es die Person überhaupt, ist der Grund lang genug, ist eine Dauer plausibel?
  2. Berechtigung prüfen, in der Reihenfolge Aufrufer, Bot, Verhältnis der beiden zueinander.
  3. Vorher benachrichtigen. Wer gebannt wird, kann danach keine Nachricht mehr bekommen. Also erst die Direktnachricht schicken, dann die Handlung ausführen.
  4. Handeln.
  5. Protokollieren — in die Datenbank und in einen Moderationskanal.
  6. Rückmelden, kurz und ohne Drama.

Punkt 3 ist ein hübsches Beispiel dafür, dass Reihenfolge fachlich sein kann und nicht nur technisch. Ich habe das erst gemerkt, als sich jemand beschwerte, keine Begründung bekommen zu haben — der Code war völlig korrekt, nur eben in der falschen Reihenfolge.

Warnungen sind kein Bann light

Am längsten habe ich über Verwarnungen nachgedacht, weil sie das einzige Instrument sind, das eine Geschichte hat.

Meine erste Fassung war eine Zahl pro Nutzer. Drei Verwarnungen, dann automatischer Ausschluss. Das ist einfach zu bauen und in der Praxis unbrauchbar, weil ein Zähler nichts erzählt. Warum wurde jemand verwarnt? Von wem? Wann? War es dreimal dieselbe Sache oder dreimal etwas anderes, verteilt über ein Jahr?

Die zweite Fassung war eine Tabelle:

CREATE TABLE verwarnungen (
  id         INTEGER PRIMARY KEY AUTOINCREMENT,
  gilde      VARCHAR(20) NOT NULL,
  nutzer     VARCHAR(20) NOT NULL,
  moderator  VARCHAR(20) NOT NULL,
  grund      TEXT        NOT NULL,
  erstellt   DATETIME    NOT NULL,
  verfaellt  DATETIME    NULL
);

Damit wurden auf einen Schlag Dinge möglich, die vorher nicht gingen: die Historie einer Person ansehen, eine einzelne Verwarnung zurücknehmen (weil Moderatoren sich auch irren), und automatisches Verfallen nach einer bestimmten Zeit.

Infobox

Das Verfallsdatum war die Entscheidung, über die ich am längsten nachgedacht habe. Sie ist keine technische, sondern eine Haltung: Soll ein Fehler von vor einem Jahr heute noch zählen? Ich habe mich für nein entschieden, mit einer Ausnahme für schwere Fälle, die ausdrücklich als dauerhaft markiert werden. Diese Entscheidung steht bis heute so im System.

Was ich beim Protokollieren gelernt habe

Der Moderationskanal war anfangs eine Textzeile: „Max wurde von Anna gebannt." Das reicht nicht. Was man später wirklich braucht, sind fünf Angaben — wer, wen, was, warum, wann — und eine Kennung, über die man den Vorgang in der Datenbank wiederfindet.

Und noch etwas, das mir erst nach einem Streitfall aufgefallen ist: Die Kennung sollte auch in der Nachricht an die betroffene Person stehen. Wenn jemand sich beschwert, kann man dann nach genau diesem Vorgang suchen, statt in einem Kanal zu blättern.

Der Punkt dahinter ist allgemeiner. Ein Protokoll ist nicht dafür da, dass man es liest, wenn alles gut läuft. Es ist dafür da, dass man in einer unangenehmen Situation eine belastbare Antwort hat. Wer es unter diesem Gesichtspunkt schreibt, protokolliert andere Dinge als jemand, der es nachträglich einbaut.

Die Fälle, an die ich zuerst nicht gedacht habe

Moderationsbefehle haben eine unangenehme Eigenschaft: Fast jeder Sonderfall ist einer, der in einer angespannten Situation auftritt.

Die Person ist gar nicht mehr auf dem Server. Man kann jemanden ausschließen, der schon weg ist — das ist sinnvoll, damit er nicht zurückkommt. Der Code muss also mit einer Kennung arbeiten können, nicht nur mit einem anwesenden Mitglied.

Die Person ist ein Bot. Ein anderer Bot lässt sich moderieren wie ein Nutzer, und in seltenen Fällen ist das nötig.

Der Aufrufende moderiert sich selbst. Klingt absurd, passiert beim Testen sofort. Ohne Prüfung schließt sich ein Moderator selbst aus und kommt nicht zurück.

Der Grund enthält Formatierung. Ein Grund mit Zeilenumbrüchen oder Erwähnungen sieht in einem Protokoll anders aus als gedacht — und wenn er in eine Nachricht an die betroffene Person eingebaut wird, kann er dort Unsinn anrichten.

Die Handlung schlägt zur Hälfte fehl. Der Ausschluss klappt, die Direktnachricht nicht, weil die Person keine Nachrichten von Servern annimmt. Das ist kein Fehler, sondern eine Einstellung, und der Befehl muss trotzdem sauber zu Ende laufen.

Warum ich Moderationsfeatures heute anders baue

Der wichtigste Unterschied zu damals: Jede Moderationshandlung ist bei mir heute ein Datensatz, keine Aktion.

Vorher hat der Befehl den Ausschluss ausgeführt und danach eine Zeile geschrieben. Wenn das Schreiben scheiterte, war die Handlung passiert und nirgends vermerkt.

Heute entsteht zuerst der Datensatz mit Status „geplant", dann wird gehandelt, dann wird der Status aktualisiert. Wenn dazwischen etwas schiefgeht, sehe ich einen Vorgang, der nicht abgeschlossen ist — und das ist die einzige Zustandsform, aus der man einen Fehler überhaupt erkennen kann.

Diese Reihenfolge ist derselbe Gedanke, der später bei Bestellungen, Lizenzen und Warteschlangen wieder auftaucht: Erst festhalten, was passieren soll, dann handeln. Alles andere führt zu Zuständen, die man hinterher nicht mehr rekonstruieren kann.

Was ich heute anders sehe

Zwei Sachen.

Automatische Bestrafungen sind fast immer eine schlechte Idee. Ich hatte eine Zeit lang eine Automatik, die bei bestimmten Wörtern sofort verwarnt. Das hat mehr Diskussionen ausgelöst als gelöst, weil Sprache Kontext hat und ein Filter keinen kennt. Was funktioniert: automatisch melden, damit ein Mensch entscheidet.

Moderationswerkzeuge sind Vertrauenssache, auch technisch. Ein Bot mit Bannrechten auf einem Server mit tausend Mitgliedern ist ein mächtiges Werkzeug. Wenn dessen Token in falsche Hände gerät, ist der Schaden groß. Das ist einer der Gründe, warum mich das Thema Zugangsdaten seitdem so beschäftigt — nicht abstrakt, sondern weil ich weiß, was mein eigener Bot alles darf.