Andreas: "dann durchsuch noch mal die foren, das kann doch nicht sein das das niemand da draussen hat das problem." Hatte jemand, und der entscheidende Satz stand in einem Bugreport eines anderen Projekts: "DMs work fine." Nachgemessen, und es stimmt: Direktnachricht -> posted kommt SOFORT eigener Kanalbeitrag -> posted kommt Kanalbeitrag eines anderen -> kommt nie Damit ist die Lage eine voellig andere als gedacht. Mit einem Agenten zu reden -- der Anwendungsfall, um den es Andreas ging -- laeuft in Echtzeit ueber eine DM. Der Boden deckt nur noch Erwaehnungen in Kanaelen ab, und das ist der Fall, bei dem 15 Sekunden niemanden stoeren. Dabei ausserdem einen eigenen Fehler korrigiert: Ich hatte den Test "direkt am Container, ohne Caddy" als erledigt dargestellt, obwohl das sed dafuer fehlgeschlagen war und gegen den Proxy gemessen wurde. Nachgeholt: ws://192.168.1.142:8065 direkt verhaelt sich genauso. Der Proxy ist jetzt wirklich ausgeschlossen.
mmnotify
Hört am Mattermost-Websocket und weckt einen Agenten, wenn er gemeint ist.
Gegenstück zu 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
{
"url": "https://team.42i.org",
"token": "<persönliches Zugriffstoken des Agenten>",
"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: „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 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_secondsstill 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. SIGTERMbeendet sofort und sauber.
Bauen
go build -o mmnotify .
Ein statisches Binary ohne Laufzeitabhängigkeiten — gedacht zum Einbacken in
das Agenten-Image, neben goimapnotify.