diff --git a/server/bild-kampagne.mjs b/server/bild-kampagne.mjs index 277b9c3e..b9329767 100644 --- a/server/bild-kampagne.mjs +++ b/server/bild-kampagne.mjs @@ -254,6 +254,89 @@ await messen(412, 915, "Auf dem Handy, haeufigste Android-Breite"); `keine Bewegung bei prefers-reduced-motion (${ruht.animation}, ${ruht.dauer})`); } +/* ---- SAGT DER KASTEN, WARUM ES NICHT GING? ------------------------- + + Am 07.10.2026 stand bei Filipe im Kasten "Ging nicht." -- der + Ersatztext. Im Protokoll des Servers stand dagegen der genaue + Grund. Zwischen beiden liegt die ganze Frage, ob jemand weiss, was + zu tun ist. + + GEMESSEN WIRD HIER DER ECHTE WEG: Auf einer Pruefadresse hat die + Sitzung kein Haus, die Route lehnt deshalb mit 403 und einem + ganzen deutschen Satz ab. Genau dieser Satz muss im Kasten + stehen -- nicht "Ging nicht.". + + Das ist kein Umweg: Jede Fehlermeldung dieser Route geht + denselben Weg (res.status(...).json({ fehler })). Wer hier + ankommt, kommt ueberall an. */ +{ + const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } }); + const seite = await ctx.newPage(); + await anmelden(seite, "admin", "CODE-DOGI-0001"); + await seite.goto(BASIS + "/workspace/bereich.html?b=agentur", { waitUntil: "networkidle" }); + await seite.waitForTimeout(1400); + + await seite.fill("#kampagne-text", + "Dein Creator Network lädt dich ein: https://www.tiktok.com/t/ZSbqPsFa8/"); + await seite.$eval("#kampagne-holen", (b) => b.click()); + /* Auf eine ANTWORT warten, nicht auf eine feste Zeit: Ein fester + Wert waere hier dieselbe Rechnung von gestern wie ueberall. */ + await seite.waitForFunction( + () => document.getElementById("kampagne-sagt").textContent.trim() !== "Wird geholt …", + { timeout: 25000 }).catch(() => {}); + + const gezeigt = await seite.evaluate(() => ({ + text: document.getElementById("kampagne-sagt").textContent.trim(), + knopfFrei: !document.getElementById("kampagne-holen").disabled, + })); + console.log("\n=== Was der Kasten nach einem Fehlschlag sagt ==="); + console.log(" gezeigt: " + JSON.stringify(gezeigt.text)); + + ok(gezeigt.text !== "Ging nicht.", + "der Kasten zeigt NICHT den Ersatztext"); + ok(gezeigt.text.length > 25 && /[.!]$/.test(gezeigt.text), + `sondern einen ganzen Satz (${gezeigt.text.length} Zeichen)`); + ok(/Agentur|workspace\.dogfather/i.test(gezeigt.text), + "der den Grund nennt und sagt, was zu tun ist"); + ok(gezeigt.knopfFrei, "und der Knopf ist wieder frei"); + + /* UND DER FALL, DER FILIPE GETROFFEN HAT: HTTP 502. + + Der 403 oben beweist, dass der Weg grundsaetzlich traegt. Die + Meldung, die bei ihm verschwand, war aber ein 502 -- und ein + 502 kommt in diesem Haus durch einen anderen Flaschenhals + (Vorbau, Dienst). Deshalb wird er hier EINZELN nachgestellt: + window.holen gibt eine fertige 502-Antwort zurueck, genau so, + wie der Server sie schickt. Zeigt der Kasten dann den Satz, + liegt es nicht am Browsercode. + + Nachgestellt wird die ANTWORT, nicht der Fehler -- damit misst + diese Zeile genau eine Sache und nicht nebenbei TikTok. */ + await seite.evaluate(() => { + const echt = window.holen; + window.holen = async (pfad, o) => { + if (!String(pfad).includes("/kampagne/einlesen")) return echt(pfad, o); + return new Response(JSON.stringify({ + fehler: "Die Seite konnte nicht gelesen werden: " + + "__MODERN_ROUTER_DATA__ fehlt in der Seite. " + + "Vermutlich hat TikTok die Seite umgebaut.", + }), { status: 502, headers: { "Content-Type": "application/json" } }); + }; + }); + await seite.fill("#kampagne-text", "https://www.tiktok.com/t/ZSbqPsFa8/"); + await seite.$eval("#kampagne-holen", (b) => b.click()); + await seite.waitForFunction( + () => document.getElementById("kampagne-sagt").textContent.trim() !== "Wird geholt …", + { timeout: 20000 }).catch(() => {}); + const beiFuenfNullZwei = (await seite.evaluate( + () => document.getElementById("kampagne-sagt").textContent.trim())); + console.log(" bei HTTP 502: " + JSON.stringify(beiFuenfNullZwei)); + ok(beiFuenfNullZwei !== "Ging nicht." + && /MODERN_ROUTER_DATA/.test(beiFuenfNullZwei), + "auch ein 502 zeigt den Satz des Servers, nicht den Ersatztext"); + await ctx.close(); +} + /* ---- Und ein Creator sieht den Kasten gar nicht --------------------- */ { const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } }); diff --git a/server/kampagne-lesen.mjs b/server/kampagne-lesen.mjs index 4e1b2db8..dd1509df 100644 --- a/server/kampagne-lesen.mjs +++ b/server/kampagne-lesen.mjs @@ -166,8 +166,33 @@ export function datenAusSeite(html) { const text = String(html || ""); const treffer = DATENBLOCK.exec(text); if (!treffer) { + /* ZWEI SEHR VERSCHIEDENE FAELLE, EINE FEHLENDE MARKE + (nachgetragen 07.10.2026, nachdem genau das passiert ist). + + (a) TikTok hat die Seite umgebaut -- dann kommt eine volle + Seite an, nur anders gebaut. Das muss jemand ansehen. + (b) TikTok schickt nur eine HUELLE, die sich erst im Browser + aufbaut. Gemessen am 07.10.2026: 17 KB statt rund 1 MB, + Titel "Campaign", leerer Koerper, zwei Skripte + (tiktok-environment, gfdatav1). Von jedem Abrufweg aus + dasselbe -- mit ehrlicher Kennung, mit Browser-Kennung, + mit Keksen, sogar aus einem echten Chromium. + + Fuer den Menschen davor ist der Unterschied alles: Bei (a) + ist etwas zu reparieren, bei (b) kann er gar nichts machen + und soll das auch hoeren. Eine Meldung, die beides + zusammenwirft, schickt ihn auf die falsche Suche. */ + const huelle = text.length < 200_000 + && (text.includes("gfdatav1") || text.includes("tiktok-environment")); throw new KampagneUnlesbar( - "__MODERN_ROUTER_DATA__ fehlt in der Seite", "aufbau"); + huelle + ? `TikTok hat nur eine leere Hülle geschickt ` + + `(${text.length < 1024 ? text.length + " Bytes" + : Math.round(text.length / 1024) + " KB"} statt rund 1 MB). ` + + `Die Seite baut sich erst im Browser auf -- daran kann ` + + `von hier aus niemand etwas ändern` + : "__MODERN_ROUTER_DATA__ fehlt in der Seite", + "aufbau"); } const anfang = treffer.index + treffer[0].length; const ende = text.indexOf("", anfang); diff --git a/server/pruef-kampagne-lesen.mjs b/server/pruef-kampagne-lesen.mjs index 45eab624..165dfe99 100644 --- a/server/pruef-kampagne-lesen.mjs +++ b/server/pruef-kampagne-lesen.mjs @@ -542,6 +542,40 @@ function faengt(tun) { ok(faengt(() => leseKampagne(seite.gipfel)) === null, "Gegenprobe: die vollständige Seite wirft nicht"); + /* DIE LEERE HUELLE IST ETWAS ANDERES ALS EIN UMBAU + (nachgetragen 07.10.2026, nachdem genau das passiert ist). + + Gemessen an diesem Tag: TikTok schickt auf einen gewoehnlichen + Abruf nur noch 17 KB -- Titel "Campaign", leerer Koerper, zwei + Skripte. Von jedem Weg aus dasselbe: ehrliche Kennung, + Browser-Kennung, mit Keksen, sogar aus einem echten Chromium. + Die Seite baut sich erst im Browser auf. + + Fuer den Menschen davor ist der Unterschied alles: Bei einem + Umbau ist etwas zu reparieren, bei einer Huelle kann er nichts + machen -- und soll das hoeren statt zu suchen. */ + { + const huelle = [ + "
", + "