Skip to content

App Lifecycle

Marc Staebler edited this page Mar 25, 2026 · 6 revisions

App-Lifecycle und Hooks

Jede UserApp durchläuft einen definierten Lebenszyklus. Über Hooks reagierst du auf Ereignisse wie App-Start, Nutzer-Aktionen oder Nachrichten.


Das App-Objekt

Jede UserApp muss ein globales App-Objekt definieren. Darin werden alle Hooks und Chat-Befehle registriert:

var App = {

    // --- Hooks ---
    onAppStart: function() {
        // App wurde gestartet
    },

    // --- Chat-Befehle ---
    chatCommands: {
        hilfe: function(user, params, command) {
            user.sendPrivateMessage('Verfügbare Befehle: /hilfe');
        }
    }
};

// --- Hilfsfunktionen ---
function meineHilfsfunktion() {
    // ...
}

Wichtig: Hooks und chatCommands werden als Eigenschaften des App-Objekts definiert. Hilfsfunktionen stehen als normale Funktionen außerhalb des Objekts.


Lebenszyklus einer App

App installiert
      │
      ▼
 onAppStart()       ← App wird initialisiert (einmalig)
      │
      ▼
 App läuft          ← Events werden verarbeitet
      │
      ▼
 onShutdown()       ← App wird gestoppt

Die App läuft, solange der Channel existiert und die App installiert ist. Bei einem Server-Neustart oder App-Update wird die App heruntergefahren und neu gestartet.


Alle verfügbaren Hooks

In den folgenden Beispielen wird nur der jeweilige Hook gezeigt. Alle Hooks gehören als Eigenschaften in das var App = { ... };-Objekt.

App-Lifecycle-Hooks

// Wird einmalig beim Start der App aufgerufen
onAppStart: function() {
    KnuddelsServer.getDefaultLogger().info('App gestartet!');
},

// Wird beim Herunterfahren der App aufgerufen
onShutdown: function() {
    KnuddelsServer.getDefaultLogger().info('App wird gestoppt...');
},

Benutzer-Hooks

// Nutzer betritt den Channel
onUserJoined: function(user) {
    KnuddelsServer.getDefaultBotUser().sendPublicMessage(
        'Willkommen, ' + user.getProfileLink() + '!'
    );
},

// Nutzer verlässt den Channel
onUserLeft: function(user) {
    KnuddelsServer.getDefaultLogger().info(user.getNick() + ' hat den Channel verlassen.');
},

Nachrichten-Hooks

// Öffentliche Nachricht im Channel
onPublicMessage: function(publicMessage) {
    var text = publicMessage.getText();
    var author = publicMessage.getAuthor();
    // Nachricht verarbeiten...
},

// Private Nachricht an den Bot
onPrivateMessage: function(privateMessage) {
    var text = privateMessage.getText();
    var author = privateMessage.getAuthor();
    author.sendPrivateMessage('Du hast geschrieben: ' + text);
},

// Öffentliche Action-Nachricht (z.B. "/me tanzt")
onPublicActionMessage: function(publicActionMessage) {
    // ...
},

// Öffentliche Event-Nachricht
onPublicEventMessage: function(publicEventMessage) {
    // ...
},

Knuddel-Hooks

// Bevor ein Nutzer Knuddel an die App sendet
onBeforeKnuddelReceived: function(knuddelTransfer) {
    // Transfer akzeptieren oder ablehnen
    knuddelTransfer.accept();
    // Oder: knuddelTransfer.reject('Grund für Ablehnung');
},

// Nachdem Knuddel empfangen wurden
onKnuddelReceived: function(sender, receiver, knuddelAmount, transferReason) {
    sender.sendPrivateMessage('Danke für ' + knuddelAmount.asNumber() + ' Knuddel!');
},

// Wenn sich der Knuddel-Betrag eines Nutzers ändert
onKnuddelAmountChanged: function(user, knuddelAmountOld, knuddelAmountNew) {
    // ...
},

Event-Hooks (Client → Server)

// Events vom Frontend empfangen
onEventReceived: function(user, type, data, appContentSession) {
    if (type === "buttonClicked") {
        // data enthält die vom Client gesendeten Daten
        appContentSession.sendEvent('response', { status: 'ok' });
    }
},

Nutzerlöschung (DSGVO)

Wenn ein Nutzer seinen Account löscht, wird dieser Hook aufgerufen. Hier solltest du alle gespeicherten Nutzerdaten bereinigen:

onUserDeleted: function(userId, userPersistence) {
    // Nutzerdaten aus der UserPersistence entfernen
    userPersistence.deleteAllNumbers();
    userPersistence.deleteAllStrings();
    userPersistence.deleteAllObjects();
    KnuddelsServer.getDefaultLogger().info('Daten für User ' + userId + ' gelöscht.');
},

Wichtig: Wenn deine App onUserDeleted implementiert, werden Persistence-Daten und der Zugriff auf gespeicherte Nutzerdaten gelöschter Nutzer automatisch bereinigt. Ohne diesen Hook wird die Bereinigung gedrosselt und erst beim nächsten App-Start nachgeholt.

Inter-App-Hooks

// Event von einer anderen App empfangen
onAppEventReceived: function(appInstance, type, data) {
    KnuddelsServer.getDefaultLogger().info(
        'Event von ' + appInstance.getAppInfo().getAppName() + ': ' + type
    );
},

Chat-Befehle definieren

Chat-Befehle werden als Objekt auf chatCommands definiert:

chatCommands: {
    // Befehl: /hilfe
    hilfe: function(user, params, command) {
        user.sendPrivateMessage('Verfügbare Befehle: /hilfe, /start, /stats');
    },

    // Befehl: /start mit Parametern
    start: function(user, params, command) {
        if (params.length === 0) {
            user.sendPrivateMessage('Bitte gib einen Spielmodus an: /start schnell oder /start normal');
            return;
        }
        user.sendPrivateMessage('Spiel startet im Modus: ' + params);
    }
}

Hinweis: Der Befehlsname wird automatisch kleingeschrieben. /Hilfe, /hilfe und /HILFE rufen alle dieselbe Funktion auf.


Vollständiges Beispiel

So sieht eine vollständige main.js mit mehreren Hooks und Chat-Befehlen aus:

var App = {

    onAppStart: function() {
        KnuddelsServer.getDefaultLogger().info('App gestartet!');
    },

    onShutdown: function() {
        KnuddelsServer.getDefaultLogger().info('App wird gestoppt...');
    },

    onUserJoined: function(user) {
        KnuddelsServer.getDefaultBotUser().sendPublicMessage(
            'Willkommen, ' + user.getProfileLink() + '!'
        );
    },

    onEventReceived: function(user, type, data, appContentSession) {
        if (type === 'ping') {
            appContentSession.sendEvent('pong', { success: true });
        }
    },

    chatCommands: {
        hilfe: function(user, params, command) {
            user.sendPrivateMessage('Verfügbare Befehle: /hilfe');
        }
    }
};

Code in mehrere Dateien aufteilen

Ein großer Vorteil der Objekt-Schreibweise: Wenn du Code in mehrere Dateien aufteilst, kannst du aus jeder Datei direkt auf das App-Objekt zugreifen. Mit KnuddelsServer.require() lädst du zusätzliche Dateien:

// main.js
var App = {
    onAppStart: function() {
        KnuddelsServer.getDefaultLogger().info('App gestartet!');
    },

    chatCommands: {}
};

// Weitere Dateien laden – diese können auf App zugreifen
KnuddelsServer.require('commands.js');
KnuddelsServer.require('game.js');
// commands.js – Chat-Befehle in eigener Datei
App.chatCommands.hilfe = function(user, params, command) {
    user.sendPrivateMessage('Verfügbare Befehle: /hilfe, /start');
};

App.chatCommands.start = function(user, params, command) {
    user.sendPrivateMessage('Spiel wird gestartet...');
};
// game.js – Spiellogik in eigener Datei
App.onUserJoined = function(user) {
    user.sendPrivateMessage('Willkommen! Gib /hilfe ein für Befehle.');
};

Da App ein normales globales Objekt ist, können alle per require geladenen Dateien dessen Eigenschaften lesen und erweitern.


Alternative: IIFE-Pattern

Neben der Objekt-Schreibweise gibt es auch das IIFE-Pattern (Immediately Invoked Function Expression). Es bietet echte Kapselung – interne Variablen und Funktionen sind von außen nicht erreichbar:

var App = (new function() {

    // Private Variable – von außen nicht zugreifbar
    var internerZaehler = 0;

    this.onAppStart = function() {
        internerZaehler++;
        KnuddelsServer.getDefaultLogger().info('App gestartet! (Start #' + internerZaehler + ')');
    };

    this.chatCommands = {
        hilfe: function(user, params, command) {
            user.sendPrivateMessage('Verfügbare Befehle: /hilfe');
        }
    };

    // Private Hilfsfunktion – von außen nicht aufrufbar
    function interneBerechnung() {
        return internerZaehler * 2;
    }

}());

Vergleich der beiden Schreibweisen

Objekt-Schreibweise IIFE-Pattern
Einfachheit Einfach zu lesen und zu schreiben Etwas komplexer durch den Wrapper
Mehrere Dateien Sehr einfach – App ist direkt zugreifbar Aufwändiger – nur this.-Eigenschaften sind von außen sichtbar
Private Variablen Nicht möglich – alles liegt offen am Objekt Ja – Variablen und Funktionen ohne this. sind gekapselt
Sicherheit Interner Zustand kann von außen manipuliert werden Interner Zustand ist geschützt
Empfehlung Für die meisten Apps ausreichend und empfohlen Sinnvoll bei sicherheitskritischem Code oder komplexen internen Zuständen

Empfehlung: Für die meisten UserApps ist die Objekt-Schreibweise der bessere Einstieg. Sie ist leichter verständlich und macht das Aufteilen in mehrere Dateien einfacher. Greife zum IIFE-Pattern, wenn du internen Zustand bewusst kapseln und vor Manipulation schützen möchtest.


Reihenfolge der Hook-Aufrufe

Wenn ein Nutzer den Channel betritt und eine Nachricht schreibt, werden die Hooks in dieser Reihenfolge aufgerufen:

  1. onUserJoined(user) – Nutzer ist dem Channel beigetreten
  2. onPublicMessage(message) – Wenn er eine öffentliche Nachricht schreibt
  3. onEventReceived(user, type, data, session) – Wenn er Events vom Frontend sendet
  4. onUserLeft(user) – Nutzer verlässt den Channel

API-Referenz

Die vollständige Dokumentation aller Hooks und Methoden findest du in der App-Klasse der API-Dokumentation.


← Zurück zur Übersicht

Navigation

Einstieg

Kernkonzepte

Features

Tutorials

Referenz & Hilfe

Clone this wiki locally