# mmnotify Hört am Mattermost-Websocket und **weckt einen Agenten**, wenn er gemeint ist. Gegenstück zu [`goimapnotify`](https://gitlab.com/shackra/goimapnotify) für Mail: ein kleiner Prozess, der lauscht und bei einem Ereignis einen Befehl ausführt. Der Agent liest und antwortet danach mit seinen eigenen Werkzeugen — `mmnotify` schreibt nie in einen Kanal, es klopft nur an. ``` mmnotify -conf /etc/mmnotify.json ``` ## Konfiguration ```json { "url": "https://team.42i.org", "token": "", "on_mention": "/root/run-agent.sh team", "on_direct": "", "debounce_seconds": 5, "floor_seconds": 60, "ignore_users": [] } ``` Die Datei enthält ein Token und gehört mit `0600` dem Dienstbenutzer. | Feld | Bedeutung | |---|---| | `on_mention` | Befehl bei Erwähnung (`@name`) im Kanal | | `on_direct` | Befehl bei Direktnachricht; leer = `on_mention` | | `debounce_seconds` | Mindestabstand zwischen zwei Läufen (Standard 5) | | `floor_seconds` | Nachsehen auch ohne Ereignis (Standard 15, `0` schaltet ab) | | `ignore_users` | Absender, die nie wecken | | `away_after_seconds` | Wann der grüne Punkt auf abwesend zurückfällt (Standard 120) | Der Befehl bekommt die Ereignisdaten über die Umgebung: `MM_TRIGGER` (`event` oder `floor`), `MM_CHANNEL`, `MM_CHANNEL_NAME`, `MM_CHANNEL_TYPE`, `MM_SENDER`, `MM_POST_ID`, `MM_ROOT_ID`, `MM_MESSAGE`. ## Zwei Wege, und der Boden ist der tragende Der Websocket ist der Beschleuniger; darunter läuft ein Boden, der ohnehin nachsieht. Die Aufteilung stammt aus hive (`hive_runtime/channels/team.py`) und ist dort begründet: *„A stream that dies quietly must make us LATE, never blind."* **Hier ist der Boden nicht nur Vorsicht.** Gemessen am 2026-09-01 gegen Mattermost 11.7.10: | | | |---|---| | **Direktnachricht** | `posted` kommt **sofort** an | | eigener Kanalbeitrag | `posted` kommt an | | Kanalbeitrag eines anderen Kontos | kommt **nie** an | **Das Gespräch mit einem Agenten läuft damit in Echtzeit.** Eine DM weckt ihn ohne Verzögerung; nur Erwähnungen in Kanälen gehen über den Boden. Dieselbe Trennung ist [für andere Mattermost-Integrationen dokumentiert](https://github.com/openclaw/openclaw/issues/11797): *„DMs work fine"* — Kanal-Ereignisse fehlen, DMs kommen durch. Die Verbindung ist also voll funktionsfähig und hat die Kanal-Abos — es fehlen ausschließlich die Ereignisse fremder Konten. Ausgeschlossen wurden: `authentication_challenge`, Token im Authorization-Header beim Handshake, ein echter Bot-Account statt eines Nutzers mit Token, und das Sitzungs-Cookie `MMAUTHTOKEN` wie im Browser. Alle vier verhalten sich gleich. Das Verhalten ist [im Mattermost-Forum beschrieben](https://forum.mattermost.com/t/websocket-does-not-provide-posted-events-from-other-accounts/14876) und tritt dort auch mit einem System-Admin-Token auf; eine Lösung steht nicht dabei. Deshalb ist `floor_seconds` der Weg und nicht der Notnagel, und der Standard ist kurz: **15 Sekunden**. Ein Durchgang kostet einen API-Aufruf — billig genug, um es oft zu tun. Wer den Stream repariert bekommt, kann ihn hochsetzen. Daraus folgt die zweite Entscheidung: **mmnotify liest den Beitrag nicht aus dem Ereignis.** Es klopft an; was zu tun ist, entscheidet der Agent, wenn er nachsieht. Zwei Leser wären zwei Antworten. ## Anwesenheit Solange mmnotify verbunden ist, zeigt der Punkt neben dem Namen, woran man ist: - **abwesend**, sobald die Verbindung steht — da, aber untätig - **grün**, während der Befehl wegen einer Erwähnung läuft - **abwesend** wieder, wenn es `away_after_seconds` still war - **offline** beim Beenden, statt einen grünen Punkt zurückzulassen, hinter dem niemand mehr sitzt Das routinemäßige Nachsehen des Bodens macht ausdrücklich **nicht** grün. Läuft der Boden häufiger als die Away-Frist, leuchtete der Agent sonst rund um die Uhr — und ein Punkt, der immer grün ist, sagt genauso wenig wie gar keiner. (Beim Testen genau so passiert: Boden alle 20 s, Frist 25 s, nie abwesend.) Ein Status ist eine Höflichkeit gegenüber dem, der in die Seitenleiste schaut. Weigert sich der Server, wird das protokolliert und weitergearbeitet: Ein Agent, der aufhört zu antworten, weil er einen Punkt nicht einfärben konnte, wäre absurd. ## Robustheit - Wiederaufbau mit wachsendem Abstand (1 s bis 60 s). Ein Server, der neu startet, wird nicht von einem Client bestürmt, der im Sekundentakt anklopft. - Herzschlag alle 30 s, Lesefrist 90 s. Eine halboffene Verbindung liest sich sonst für immer wie Stille. - Höchstens ein Lauf gleichzeitig; wer währenddessen schreibt, wird vom laufenden Durchgang mitgenommen. Ohne das lösen fünf Beiträge in einer Minute fünf Agentenläufe aus, die sich gegenseitig überholen. - Eigene Beiträge und `system_*`-Ereignisse wecken nie — sonst antwortet ein Agent auf sich selbst. - `SIGTERM` beendet sofort und sauber. ## Bauen ```bash go build -o mmnotify . ``` Ein statisches Binary ohne Laufzeitabhängigkeiten — gedacht zum Einbacken in das Agenten-Image, neben `goimapnotify`.