Benachrichtigungen: nicht "Verbindung offen", sondern "sieht jemand hin"
Diene im Support (vor 5 Tagen): „Die Benachrichtigungen werden nicht
angezeigt, wenn neue Nachrichten reinkommen. Erst, wenn man die App
öffnet."
ERST GEMESSEN, WAS NICHT DAS PROBLEM IST. Am echten Bestand
nachgesehen: Diene HAT ein angemeldetes Geraet (Android, Chrome, seit
dem 29.09.), und alle zehn Geraete im Haus melden `fehler = 0`. An
der Zustellung liegt es nicht.
DANN NACHGESTELLT (pruef-abzeichen, Abschnitt 8):
Verbindung offen -> KEINE Benachrichtigung
Verbindung zu -> sie kommt
Genau sein Befund.
DER GEDANKE WAR RICHTIG, DIE FRAGE FALSCH. Im Quelltext stand:
if ((zuschauer.get(personId) || new Set()).size) continue;
und daneben die Begruendung -- „wer die Seite offen hat, sieht die
Nachricht ohnehin; ihm auch noch eine Meldung aufs Handy zu schicken
ist der schnellste Weg, dass er Benachrichtigungen abschaltet." Das
stimmt. Nur beantwortet `zuschauer` eine ANDERE Frage: ob eine
VERBINDUNG offen ist. Ein Handy mit der App im Hintergrund haelt sie
weiter -- und der Server hielt Diene fuer anwesend, waehrend sein
Bildschirm schwarz war.
DIE SEITE WEISS ES, DER SERVER NICHT. `document.visibilityState` ist
die einzige Stelle, die den Unterschied kennt. Also sagt sie es --
ueber einen winzigen Weg (`/api/chat/sicht`), beim Aufbau, bei jedem
Wechsel und mit `keepalive` beim Weggehen.
MIT VERFALL, und das ist der wichtige Teil: Ein Geraet, das
abstuerzt, im Funkloch steht oder eingefroren wird, sagt gar nichts
mehr. Ohne Verfall bliebe es fuer immer „sichtbar" und fuer immer
still. Wer nicht widerspricht, gilt nach zweieinhalb Minuten als weg
-- eine Meldung zu viel ist laestig, eine zu wenig ist genau der
Fehler, den Diene gemeldet hat. Dazu alle Minute ein Lebenszeichen,
solange die App vorn liegt.
EINE STELLE FUER DIE FRAGE. Sie wurde an zwei Orten gestellt:
`siehtZu()` und eine Abschrift mitten in `chatEreignis`. Die
Abschrift war die kaputte. Jetzt fragen beide dieselbe Funktion.
GEPRUEFT -- vier Lagen, und die zweite ist die wichtigere
Gegenprobe:
App liegt hinten -> Meldung kommt (war: nichts)
sieht wirklich hin -> KEINE Meldung (Absicht bleibt)
App weggelegt -> Meldung kommt wieder
gar keine Verbindung -> Meldung kommt
Ohne die zweite Zeile hiesse die Reparatur nur „jetzt kommt immer
eine", und das waere der schnellste Weg, dass jemand
Benachrichtigungen abschaltet.
UND EINE PRUEFUNG HAT DEN FEHLER MITGETRAGEN. pruef-anruf-klingelt
hielt den WORTLAUT der kaputten Zeile fest -- genau das, wovor ihr
eigener Kommentar drei Zeilen darueber warnt („Die Pruefung hat den
alten Wortlaut bestaetigt statt sein Verhalten"). Sie prueft jetzt
beides: dass gefragt wird, und dass die Frage die richtige ist.
pruef-abzeichen 26/0, pruef-chat, pruef-anruf 132/0,
pruef-chat-kanaele 81/0, pruef-anruf-klingelt 25/0, pruef-push-ziel
38/0, pruef-arten 28/0, pruef-reaktion 421/0, pruef-struktur,
pruef-zwischenspeicher 34/0.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -532,6 +532,132 @@ console.log("=== 7. Jede Adresse zaehlt nur ihr eigenes Haus ===");
|
||||
}
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
8. DIENES MELDUNG: DIE APP IM HINTERGRUND (30.09.2026)
|
||||
|
||||
„Die Benachrichtigungen werden nicht angezeigt, wenn neue
|
||||
Nachrichten reinkommen. Erst, wenn man die App öffnet."
|
||||
|
||||
Am echten Bestand nachgesehen: Diene HAT ein angemeldetes Geraet
|
||||
(Android, Chrome, seit dem 29.09.), und alle zehn Geraete im Haus
|
||||
melden `fehler = 0`. An der Zustellung liegt es nicht.
|
||||
|
||||
DER VERDACHT steht in workspace-chat.js:
|
||||
|
||||
if ((zuschauer.get(personId) || new Set()).size) continue;
|
||||
|
||||
Wer eine offene Verbindung hat, bekommt KEINE Benachrichtigung.
|
||||
Der Gedanke daneben ist richtig -- „wer die Seite offen hat, sieht
|
||||
die Nachricht ohnehin, und ihm auch noch eine Meldung aufs Handy zu
|
||||
schicken ist der schnellste Weg, dass er Benachrichtigungen
|
||||
abschaltet."
|
||||
|
||||
NUR HEISST EINE OFFENE VERBINDUNG NICHT, DASS JEMAND HINSIEHT.
|
||||
Ein Handy mit der App im Hintergrund haelt sie weiter; der Server
|
||||
haelt ihn dann fuer anwesend und schweigt.
|
||||
|
||||
HERGESTELLT UND NICHT ANGENOMMEN: Der Strom wird geoeffnet und
|
||||
offen gelassen -- genau das, was ein Telefon im Hintergrund tut.
|
||||
======================================================================= */
|
||||
console.log("");
|
||||
console.log("=== 8. Wenn die App im Hintergrund liegt ===");
|
||||
{
|
||||
const { request: httpRoh } = await import("node:http");
|
||||
/* EINE ROHE VERBINDUNG, DIE OFFEN BLEIBT. `fetch` wartet auf das
|
||||
Ende; ein Ereignisstrom endet nie. Genau daran ist im September
|
||||
schon einmal eine Pruefung haengengeblieben. */
|
||||
const stromAuf = (keks) => new Promise((fertig) => {
|
||||
const a = httpRoh({
|
||||
host: "127.0.0.1", port: PORT, path: "/workspace/api/chat/strom",
|
||||
method: "GET",
|
||||
headers: { Cookie: keks, Host: "crew.dogfather-universe.com", Accept: "text/event-stream" },
|
||||
}, (antwort) => {
|
||||
antwort.on("data", () => { }); /* mitlesen, nichts tun */
|
||||
fertig({ anfrage: a, code: antwort.statusCode });
|
||||
});
|
||||
a.on("error", () => fertig({ anfrage: a, code: 0 }));
|
||||
a.end();
|
||||
});
|
||||
|
||||
const strom = await stromAuf(ayla.keks);
|
||||
ok(strom.code === 200, `Aylas Handy haelt die Verbindung (HTTP ${strom.code})`);
|
||||
/* Kurz warten, damit der Server sie wirklich eingetragen hat. */
|
||||
await new Promise((r) => setTimeout(r, 400));
|
||||
|
||||
const vorher = pakete.length;
|
||||
const g = await rufe(dogi, `/workspace/api/chat/raeume/${raumId}/nachrichten`,
|
||||
{ text: "Kommt das an, wenn die App hinten liegt?" }, "POST");
|
||||
ok(g.ok, `DogFather schreibt ihr (HTTP ${g.status})`);
|
||||
/* Auf das Ergebnis warten, nicht auf die Uhr -- aber hier ist das
|
||||
AUSBLEIBEN das Ergebnis, also braucht es eine Frist. Drei
|
||||
Sekunden sind grosszuegig: Der Weg zum Push-Dienst dauert
|
||||
oertlich Millisekunden. */
|
||||
const bis = Date.now() + 3000;
|
||||
while (pakete.length === vorher && Date.now() < bis) {
|
||||
await new Promise((r) => setTimeout(r, 60));
|
||||
}
|
||||
const kam = pakete.length > vorher;
|
||||
console.log(` Benachrichtigung verschickt: ${kam ? "ja" : "NEIN"}`);
|
||||
ok(kam,
|
||||
kam
|
||||
? "sie bekommt die Meldung, obwohl eine Verbindung offen ist "
|
||||
+ "(die App liegt hinten)"
|
||||
/* BACKTICKS, WEIL EINE DEUTSCHE ANFUEHRUNG DARIN STEHT. In
|
||||
einem "..."-String beendet das ASCII-Zeichen nach dem Wort
|
||||
den String -- mir heute zum fuenften Mal passiert. Wo eine
|
||||
deutsche Anfuehrung hinein soll, gehoeren Backticks darum. */
|
||||
: `KEINE MELDUNG — der Server haelt eine offene Verbindung fuer `
|
||||
+ `„sie sieht ja hin". Genau Dienes Befund.`);
|
||||
|
||||
/* ==== DIE GEGENPROBE, OHNE DIE DIE ZEILE OBEN NICHTS BEWEIST ====
|
||||
|
||||
Wer WIRKLICH hinsieht, soll KEINE Meldung bekommen -- das war
|
||||
der richtige Gedanke hinter der alten Zeile, und er bleibt.
|
||||
Ohne diesen Teil hiesse die Reparatur nur „jetzt kommt immer
|
||||
eine", und das waere der schnellste Weg, dass jemand
|
||||
Benachrichtigungen abschaltet.
|
||||
|
||||
GEMELDET WIRD, WAS DIE SEITE SAGEN WUERDE: `sichtbar: true` --
|
||||
genau das schickt die Kopfleiste, wenn die App vorn liegt. */
|
||||
const sicht = await rufe(ayla, "/workspace/api/chat/sicht",
|
||||
{ sichtbar: true }, "POST");
|
||||
ok(sicht.ok, `Ayla meldet: ich sehe hin (HTTP ${sicht.status})`);
|
||||
const vorherS = pakete.length;
|
||||
await rufe(dogi, `/workspace/api/chat/raeume/${raumId}/nachrichten`,
|
||||
{ text: "Und wenn sie wirklich hinsieht?" }, "POST");
|
||||
await new Promise((r) => setTimeout(r, 2500));
|
||||
ok(pakete.length === vorherS,
|
||||
`wer hinsieht, bekommt KEINE Meldung aufs Handy `
|
||||
+ `(${pakete.length - vorherS} verschickt)`);
|
||||
|
||||
/* Und wenn sie die App weglegt, kommt sie wieder. */
|
||||
await rufe(ayla, "/workspace/api/chat/sicht", { sichtbar: false }, "POST");
|
||||
const vorherW = pakete.length;
|
||||
await rufe(dogi, `/workspace/api/chat/raeume/${raumId}/nachrichten`,
|
||||
{ text: "App weggelegt." }, "POST");
|
||||
const bisW = Date.now() + 5000;
|
||||
while (pakete.length === vorherW && Date.now() < bisW) {
|
||||
await new Promise((r) => setTimeout(r, 60));
|
||||
}
|
||||
ok(pakete.length > vorherW,
|
||||
`legt sie die App weg, kommt die Meldung wieder `
|
||||
+ `(${pakete.length - vorherW})`);
|
||||
|
||||
/* GEGENPROBE ZUM SCHLUSS: Ohne offene Verbindung erst recht. */
|
||||
strom.anfrage.destroy();
|
||||
await new Promise((r) => setTimeout(r, 500));
|
||||
const vorher2 = pakete.length;
|
||||
await rufe(dogi, `/workspace/api/chat/raeume/${raumId}/nachrichten`,
|
||||
{ text: "Und jetzt, mit geschlossener App?" }, "POST");
|
||||
const bis2 = Date.now() + 5000;
|
||||
while (pakete.length === vorher2 && Date.now() < bis2) {
|
||||
await new Promise((r) => setTimeout(r, 60));
|
||||
}
|
||||
ok(pakete.length > vorher2,
|
||||
`Gegenprobe: ohne offene Verbindung kommt sie sehr wohl `
|
||||
+ `(${pakete.length - vorher2})`);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------- */
|
||||
await schliessen();
|
||||
d.close();
|
||||
|
||||
Reference in New Issue
Block a user