Das erste Ticket-System — und warum ich es zweimal gebaut habe
Ein Knopf, ein Kanal, ein Gespräch. Die erste Fassung hat sechs Wochen gehalten, dann kamen die Fragen, die ich nicht bedacht hatte: Verlauf, Kategorien, Rechte, Löschen.
954 Wörter · 5 Min. Lesezeit
- yurna
- discord
- architektur
Ticket-Systeme sind auf Discord das, was Kontaktformulare im Web sind: Jeder größere Server hat eins, und alle funktionieren ungefähr gleich. Jemand klickt einen Knopf, es entsteht ein privater Kanal, in dem er mit dem Team spricht, und am Ende wird der Kanal geschlossen.
Die erste Fassung habe ich an einem Wochenende gebaut. Die zweite hat zwei Monate gedauert.
Fassung eins
Der Kern ist wenig Code. Ein Knopf löst eine Interaktion aus, daraufhin wird ein Kanal mit passenden Rechten erzeugt:
const { ViewChannel: SEHEN, SendMessages: SCHREIBEN } = PermissionsBitField.Flags;
const kanal = await gilde.channels.create({
name: `ticket-${nutzer.username}`,
type: ChannelType.GuildText,
parent: kategorieId,
permissionOverwrites: [
{ id: gilde.roles.everyone.id, deny: [PermissionsBitField.Flags.ViewChannel] },
{ id: nutzer.id, allow: [SEHEN, SCHREIBEN] },
{ id: teamRolleId, allow: [SEHEN, SCHREIBEN] },
],
});Dazu ein Schließen-Knopf, der den Kanal löscht. Fertig, funktioniert, alle zufrieden.
Sechs Wochen lang.
Die Fragen, die dann kamen
„Wo ist das Gespräch von letzter Woche?" Gelöscht. Mit dem Kanal. Für immer. Es gab keinen Verlauf — und bei einem Ticket-System ist der Verlauf oft das Wichtigste, weil dieselbe Frage in drei Monaten wiederkommt oder weil man belegen können muss, was besprochen wurde.
„Warum kann jeder ein Ticket aufmachen, so oft er will?" Weil ich keine Begrenzung eingebaut hatte. Ein einzelner Nutzer hatte an einem Abend elf Kanäle erzeugt.
„Kann man vorher auswählen, worum es geht?" Berechtigte Frage. Ein Support-Ticket, eine Bewerbung und eine Beschwerde über ein Teammitglied gehören nicht in dieselbe Kategorie und schon gar nicht vor dieselben Augen.
„Der Kanal ist weg, aber die Person wartet noch auf Antwort." Jemand hat versehentlich geschlossen. Es gab keine Rückfrage, keine Bestätigung, kein Rückgängig.
Fassung zwei
Die zweite Fassung hat drei Dinge grundlegend anders gemacht.
Ein Ticket ist ein Datensatz, kein Kanal. Der Kanal ist nur seine sichtbare Form. In der Datenbank steht, wer es geöffnet hat, wann, in welcher Kategorie, welchen Status es hat, wer zuständig ist.
CREATE TABLE tickets (
id INTEGER PRIMARY KEY,
gilde VARCHAR(20) NOT NULL,
nummer INT NOT NULL,
ersteller VARCHAR(20) NOT NULL,
kategorie VARCHAR(40) NOT NULL,
kanal VARCHAR(20) NULL,
status VARCHAR(20) NOT NULL DEFAULT 'offen',
bearbeiter VARCHAR(20) NULL,
erstellt_am DATETIME NOT NULL,
beendet_am DATETIME NULL
);Allein dadurch wurden Dinge möglich, die vorher gar nicht denkbar waren: eine laufende Nummer, eine Übersicht offener Tickets, Statistiken darüber, wie lange Antworten dauern.
Schließen heißt archivieren, nicht löschen. Beim Schließen wird der Verlauf ausgelesen und als Abschrift gespeichert. Danach wird der Kanal entfernt. Wer die Abschrift sehen darf, ist eine Rechtefrage — und eine, die man sich vorher stellen sollte, weil in Tickets persönliche Dinge stehen.
Kategorien sind Konfiguration. Jede Kategorie hat einen Namen, eine Beschreibung, eine Zielkategorie für den Kanal, eine zuständige Rolle und optional Fragen, die vorab gestellt werden. Das war die Änderung, die das System von „mein Ticket-System" zu „ein Ticket-System, das andere Server benutzen können" gemacht hat.
Warnung
Beim Auslesen des Verlaufs sollte man sich vorher überlegen, was genau gespeichert wird. Anhänge liegen auf Discords Servern und verschwinden mit dem Kanal — ein Verweis darauf ist nach dem Löschen wertlos. Wer Anhänge braucht, muss sie kopieren, und dann stellt sich sofort die Frage nach Speicherplatz, Zugriffsrechten und Aufbewahrungsfristen. Ich habe das damals bewusst zurückgestellt und später gebaut.
Was ich beim zweiten Bauen über Zustände gelernt habe
Mein Ticket hatte in Fassung eins zwei Zustände: Es existiert (der Kanal ist da) oder nicht (Kanal weg). In Fassung zwei sind es fünf: offen, in Bearbeitung, wartet auf Antwort, geschlossen, archiviert.
Der Punkt dabei ist nicht die Anzahl, sondern dass sie ausdrücklich sind. Vorher war „wartet auf Antwort" ein Zustand, den ein Teammitglied im Kopf hatte. Danach war er sichtbar, filterbar, auswertbar — und man konnte automatisch nach drei Tagen ohne Reaktion nachfassen.
Diese Umstellung — von impliziten Zuständen in Köpfen zu ausdrücklichen Zuständen im System — ist eine der Sachen, die ich seitdem bei fast jedem Projekt irgendwann mache. Sie fühlt sich nach Bürokratie an, solange alles klein ist, und wird genau in dem Moment wertvoll, in dem mehr als eine Person beteiligt ist.
Die Rechte, die man dabei versehentlich vergibt
Ein Ticket-System erzeugt Kanäle mit Rechteüberschreibungen. Das klingt harmlos und ist der Teil, an dem man am ehesten etwas falsch macht.
Wer den Kanal sehen darf, sieht ihn dauerhaft. Wenn jemand einem Ticket hinzugefügt wird — etwa ein zweites Teammitglied —, bleibt diese Berechtigung, bis sie jemand entfernt. Bei einem archivierten Ticket ist das egal, weil der Kanal weg ist. Bei einem, das reaktiviert wird, nicht.
Rollen ändern sich. Ein Ticket, dessen Zugriff an einer Rolle hängt, ist automatisch für jeden zugänglich, der diese Rolle später bekommt. Das ist meistens gewollt und in einem Beschwerdeticket über ein Teammitglied möglicherweise nicht.
Die Ersteller-Berechtigung überlebt einen Austritt. Wer den Server verlässt und wieder beitritt, hat wieder Zugriff auf sein altes Ticket, weil die Überschreibung an der Kennung hängt.
Keiner dieser Punkte ist dramatisch. Aber sie sind der Grund, warum ich beim zweiten Bauen die Rechte nicht mehr als Nebeneffekt des Kanals betrachtet habe, sondern als eigenen Zustand: Wer darf dieses Ticket sehen, und warum?
Der Punkt, an dem ich Abschriften ernst genommen habe
Es gab einen konkreten Anlass, der aus einer netten Funktion eine notwendige gemacht hat: eine Auseinandersetzung, bei der zwei Beteiligte den Verlauf unterschiedlich erinnerten — und der Kanal war längst gelöscht.
Ohne Abschrift steht Aussage gegen Aussage, und derjenige, der das System betreibt, muss entscheiden. Das ist eine Position, die man nicht haben will.
Seitdem gilt für mich: Wo Entscheidungen über Menschen getroffen werden, muss nachvollziehbar sein, worauf sie beruhen. Das ist kein Misstrauen gegenüber dem Team, sondern Schutz für alle Beteiligten — und für die, die entscheiden, mehr als für die anderen.
Was ich falsch gemacht habe und heute anders anfangen würde
Ich habe Fassung eins gebaut, ohne eine einzige Frage nach dem Danach zu stellen. Kein böser Fehler — es war ein Wochenendprojekt. Aber die zwei Monate für Fassung zwei waren zu einem guten Teil Umbau, nicht Neubau.
Was mich zehn Minuten gekostet hätte: einmal aufschreiben, was ein Ticket im Betrieb durchläuft. Wer öffnet es, wer sieht es, wer schließt es, was passiert danach, wer will es später noch einmal lesen. Diese fünf Fragen hätten mich sofort zur Datenbanktabelle geführt — und der Rest wäre derselbe Code gewesen, nur an der richtigen Stelle.