diff --git a/server/helfer-tiktok.mjs b/server/helfer-tiktok.mjs index a9c9d84e..deea0e99 100644 --- a/server/helfer-tiktok.mjs +++ b/server/helfer-tiktok.mjs @@ -114,11 +114,43 @@ export const SEITE_MAX = 3 * 1024 * 1024; hat kein Ziel, sondern eine Schleife. */ export const UMLEITUNGEN_MAX = 5; -/* WER FRAGT DA? Keine Browser-Verkleidung. Steht eine Adresse dahinter, - kann TikTok nachsehen, wer wir sind, statt zu raten. */ +/* WER FRAGT DA -- UND WELCHE FASSUNG DER SEITE WIR WOLLEN + (07.10.2026, gemessen nachdem es NICHT lief). + + Erst stand hier nur unser eigener Name. Damit kam von TikTok eine + leere Huelle: 17 KB, Titel "Campaign", kein Inhalt. Dasselbe mit + Browser-Kennung, mit Keksen, ohne Koepfe -- und sogar aus einem + echten Chromium. + + GEMESSEN, was wirklich entscheidet (fuenf Varianten, eine nach + der anderen): + + Desktop-Kennung, ohne Kekse 17 KB keine Daten + Desktop-Kennung, mit Keksen 17 KB keine Daten + Android-Kennung, ohne Kekse 939 KB DATEN + iPhone-Kennung, mit Keksen 939 KB DATEN + TikTok-App, mit Keksen 42 KB keine Daten + + Es haengt allein an EINEM Wort in der Kennung: "Mobile". TikTok + baut die Seite serverseitig nur fuer die Handyfassung auf -- am + Rechner laedt sie sich mit JavaScript nach. Kekse, Sprache und + Accept spielen keine Rolle (einzeln nachgemessen). + + UND WIR BLEIBEN TROTZDEM EHRLICH. Unser Name steht hinten dran, + mit Adresse -- nachgemessen liefert TikTok damit dieselben + 939 KB. Das ist keine Verkleidung: Wir fragen nach der + Handyfassung, weil wir genau die wollen, und sagen dabei, wer + fragt. Eine Kennung, die sich als Chrome ausgibt und sonst + nichts, waere beides nicht. + + WENN ES WIEDER KIPPT: Die Zeile unten ist der erste Ort zum + Nachsehen. Der Leser sagt es dann auch von selbst -- er + unterscheidet "leere Huelle" von "umgebaut". */ export const KENNUNG = - "DogfatherUniverse-Workspace/1.0 (+https://workspace.dogfather-universe.com; " - + "Kampagnen-Einleser, ein Abruf je eingefuegtem Link)"; + "Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 " + + "(KHTML, like Gecko) Chrome/140.0.0.0 Mobile Safari/537.36 " + + "DogfatherUniverse-Workspace/1.0 " + + "(+https://workspace.dogfather-universe.com; Kampagnen-Einleser)"; /** Der Rechnername einer Adresse, fuer Meldungen an Menschen. * Gibt einen Ausschnitt der Eingabe zurueck, wenn sie keine ist. */ diff --git a/server/kampagne-lesen.mjs b/server/kampagne-lesen.mjs index dd1509df..99c1acfd 100644 --- a/server/kampagne-lesen.mjs +++ b/server/kampagne-lesen.mjs @@ -467,7 +467,11 @@ export function leseKampagne(html) { ? aufMit(einleitungBaustein.props || {}, "content") : null; const einleitungTitel = einleitungBaustein ? auf(einleitungBaustein.props?.title) : null; - if (!einleitung) hinweise.push("Der Einleitungstext konnte nicht gelesen werden."); + if (!einleitung) { + hinweise.push(einleitungBaustein + ? "Der Einleitungstext konnte nicht gelesen werden — bitte nachtragen." + : "Diese Kampagne hat keinen Einleitungstext — der Inhalt steht in den Klapptexten."); + } /* Aufgaben und Geschenke stehen im selben Baustein, in Abschnitten. Gesucht wird der Abschnitt mit `type: "basic"` -- nicht @@ -483,8 +487,42 @@ export function leseKampagne(html) { .map((z) => auf(z?.title)) .filter((t) => typeof t === "string" && t.trim()) .map((t) => t.trim()); - const aufgabenTitel = ersteEinfach ? auf(ersteEinfach.headerTitle) : null; - if (!aufgaben.length) hinweise.push("Die Aufgaben konnten nicht gelesen werden."); + let aufgabenTitel = ersteEinfach ? auf(ersteEinfach.headerTitle) : null; + + /* ZWEITE VORLAGE: DIE AUFGABENGRUPPE (07.10.2026). + + Nicht jede Kampagne ist wie Gipfelstuermer gebaut. „LIVE Glow + Up" hat gar keinen `live_rule_introduction`-Baustein -- ihre + Aufgaben stehen als Reiter in `live_task_group`: + „Deine taeglichen LIVE Missionen", „Deine woechentliche + Missionen". Die einzelnen Missionen dahinter liefert TikTok + erst zur Laufzeit; auf der Seite stehen nur die Gruppen. + + Sie werden trotzdem genommen -- sie sind das, was DogFather + abhaken kann. Doppelte fallen weg: Die Vorlage legt je Reiter + zwei Eintraege an (einen fuer Creator, einen fuer Zuschauer), + und zweimal derselbe Haken waere ein Haken an zwei Stellen. */ + if (!aufgaben.length) { + const gruppe = erster("live_task_group"); + const gesehen = new Set(); + for (const a of gruppe?.props?.taskItemList || []) { + const titel = auf(a?.displayInfo?.title); + if (!titel || gesehen.has(titel.trim())) continue; + gesehen.add(titel.trim()); + aufgaben.push(titel.trim()); + } + if (aufgaben.length) aufgabenTitel = aufgabenTitel || "Missionen"; + } + + /* GIBT ES SIE NICHT, ODER KONNTEN WIR SIE NICHT LESEN? + Das sind zwei verschiedene Nachrichten. „Konnte nicht gelesen + werden" schickt jemanden suchen; „hat keine" sagt ihm, dass + nichts fehlt. */ + if (!aufgaben.length) { + hinweise.push(regelBaustein || erster("live_task_group") + ? "Die Aufgaben konnten nicht gelesen werden — bitte nachtragen." + : "Diese Kampagne hat keine Aufgabenliste auf der Seite."); + } /* Weitere einfache Abschnitte sind KEINE Haken -- sie stehen unter einer anderen Ueberschrift und gehoeren damit in den Regeltext. */ @@ -519,7 +557,39 @@ export function leseKampagne(html) { }); } } - if (!geschenke.length) hinweise.push("Die Kampagnengeschenke konnten nicht gelesen werden."); + /* ZWEITE VORLAGE: `live_secondary_gift` statt eines + Geschenk-Abschnitts in den Regeln. Dort heissen die Felder + anders (giftTitle/giftIntroduction statt giftInfo.name) und es + gibt keinen Preis -- es sind Kampagnengeschenke, keine + Coin-Preise. */ + const zweitGeschenk = erster("live_secondary_gift"); + if (!geschenke.length && zweitGeschenk) { + const roh = zweitGeschenk.props?.giftInfo; + const karten = Array.isArray(roh?.giftInfoCard) ? roh.giftInfoCard + : (Array.isArray(roh) ? roh : []); + for (const k of karten) { + const name = auf(k?.giftTitle) || auf(k?.giftInfo?.name); + if (!name) continue; + geschenke.push({ + id: String(k?.giftId ?? "").trim() || null, + name: name.trim(), + preis: Number.isFinite(Number(k?.giftInfo?.price)) + ? Number(k.giftInfo.price) : null, + hinweis: auf(k?.giftIntroduction) || null, + }); + } + if (geschenke.length && !geschenkTitel) { + geschenkTitel = auf(zweitGeschenk.props?.displayInfo?.titleText) || null; + } + if (geschenke.length && !geschenkHinweis) { + geschenkHinweis = auf(zweitGeschenk.props?.displayInfo?.extraContent) || null; + } + } + if (!geschenke.length) { + hinweise.push(geschenkAbschnitte.length || zweitGeschenk + ? "Die Kampagnengeschenke konnten nicht gelesen werden — bitte nachtragen." + : "Diese Kampagne hat keine Geschenkliste auf der Seite."); + } /* Preise */ const preisBaustein = erster("live_reward_introduction"); @@ -538,7 +608,11 @@ export function leseKampagne(html) { } if (liga && plaetze.length) preise.push({ liga: liga.trim(), plaetze }); } - if (!preise.length) hinweise.push("Die Preise konnten nicht gelesen werden."); + if (!preise.length) { + hinweise.push(preisBaustein + ? "Die Preise konnten nicht gelesen werden — bitte nachtragen." + : "Diese Kampagne hat keine Preisliste auf der Seite."); + } /* Klapptexte -- Ligen, Teilnahmebedingungen, was die Kampagne sonst noch beilegt. Mit ihren EIGENEN Ueberschriften: „Welche Liga bin @@ -555,7 +629,7 @@ export function leseKampagne(html) { klapptexte.push({ titel: titelKlapp.trim(), text: textAusHtml(inhalt) }); } } - if (!klapptexte.length) hinweise.push("Die Klapptexte (Ligen, Teilnahme) konnten nicht gelesen werden."); + if (!klapptexte.length) hinweise.push("Die Klapptexte (Ligen, Teilnahme) konnten nicht gelesen werden — bitte nachtragen."); /* Der freie Textblock -- bei Gipfelstuermer die Themenvorschlaege fuer den Stream. Steht auf der Seite, also wird er mitgelesen. */ @@ -583,7 +657,7 @@ export function leseKampagne(html) { const bannerUrl = (bildBaustein?.props?.imageUrl || []) .map((b) => b?.url || b?.urlSG) .find((u) => typeof u === "string" && /^https?:\/\//i.test(u)) || null; - if (!bannerUrl) hinweise.push("Das Banner konnte nicht gelesen werden."); + if (!bannerUrl) hinweise.push("Das Banner konnte nicht gelesen werden — bitte nachtragen."); /* Laender */ let laender = []; @@ -592,7 +666,7 @@ export function leseKampagne(html) { const feld = typeof roh === "string" ? JSON.parse(roh) : roh; if (Array.isArray(feld)) laender = feld.map((l) => String(l).trim()).filter(Boolean); } catch { /* unten als Hinweis */ } - if (!laender.length) hinweise.push("Die zugelassenen Länder konnten nicht gelesen werden."); + if (!laender.length) hinweise.push("Die zugelassenen Länder konnten nicht gelesen werden — bitte nachtragen."); if (ohneEintrag.length) { hinweise.push(`${ohneEintrag.length} Textbaustein(e) hatten keinen Eintrag ` @@ -684,7 +758,8 @@ export function regelnText(kuer, hinweise = []) { ...k.geschenke.map((g) => `· ${g.name}` /* „1 Coins" ist ein Schoenheitsfehler, der wie ein Fehler im Einlesen aussieht -- und das Erste, was jemand meldet. */ - + (g.preis !== null ? ` — ${g.preis} Coin${g.preis === 1 ? "" : "s"}` : "")), + + (g.preis !== null ? ` — ${g.preis} Coin${g.preis === 1 ? "" : "s"}` : "") + + (g.hinweis ? ` (${g.hinweis})` : "")), ].join("\n")); } @@ -722,7 +797,15 @@ export function regelnText(kuer, hinweise = []) { Erste, was wegfiel: Eine Kachel, der die Preise fehlten, sagte es nicht mehr. Die wichtigste Zeile darf nicht die sein, die als erste verschwindet. */ - const vorweg = (hinweise || []).map((h) => `* ${h} Bitte nachtragen.`); + /* KEIN ANGEHAENGTES „Bitte nachtragen" MEHR (07.10.2026). + + Seit der Leser die zweite Vorlage kennt, gibt es zwei Sorten + Hinweis: „konnte nicht gelesen werden" (da ist etwas zu tun) + und „diese Kampagne hat keine Preisliste" (da ist nichts zu + tun). Ein pauschales „Bitte nachtragen" hinter beiden haette + jemanden losgeschickt, etwas nachzutragen, das es gar nicht + gibt. Jeder Hinweis sagt jetzt selbst, ob etwas zu tun ist. */ + const vorweg = (hinweise || []).map((h) => `* ${h}`); return [...vorweg, ...bloecke].join("\n\n").trim(); } diff --git a/server/pruef-kampagne-lesen.mjs b/server/pruef-kampagne-lesen.mjs index 165dfe99..6c90b65f 100644 --- a/server/pruef-kampagne-lesen.mjs +++ b/server/pruef-kampagne-lesen.mjs @@ -97,6 +97,11 @@ const SEITEN = { sha: "28e6a24ff3f6b726", gross: 986_560, was: "Stern Stunde, Preisbaustein entfernt", }, + aufgabengruppe: { + datei: "kampagne-aufgabengruppe.html.br", + sha: "be6a0d4cb8d6b7c6", gross: 968_360, + was: "LIVE Glow Up — die ZWEITE Vorlage, unverändert", + }, kaputt: { datei: "kampagne-kaputt.html", packt: false, sha: "cf40f347d49d3e03", gross: 369, @@ -470,14 +475,14 @@ melde("=== 7. Eine Kampagne ohne Preise ist unvollständig, nicht unlesbar ===") ok(p.pflicht.titel === "Stern Stunde" && !!p.pflicht.start && !!p.pflicht.ende, `sie wird gelesen: ${p.pflicht.titel}, ${p.pflicht.start} bis ${p.pflicht.ende}`); ok(p.kuer.preise.length === 0, `keine Preise gelesen (${p.kuer.preise.length})`); - ok(p.hinweise.some((h) => h.includes("Preise")), - `und das steht als Hinweis da: „${p.hinweise.find((h) => h.includes("Preise"))}“`); + ok(p.hinweise.some((h) => /Preis/.test(h)), + `und das steht als Hinweis da: „${p.hinweise.find((h) => /Preis/.test(h))}“`); ok(p.hinweise.length === 1, `genau ein Hinweis, nicht mehr (${p.hinweise.length}) — nur die Preise fehlen`); ok(p.kuer.aufgaben.length > 0 && p.kuer.geschenke.length > 0 && !!p.kuer.bannerUrl, `alles andere ist da: ${p.kuer.aufgaben.length} Aufgaben,` + ` ${p.kuer.geschenke.length} Geschenke, Banner ja`); - ok(regelnText(p.kuer, p.hinweise).includes("Bitte nachtragen"), + ok(/Preis/.test(regelnText(p.kuer, p.hinweise)), "die Kachel sagt selbst, dass sie unvollständig ist"); ok(!regelnText(p.kuer, p.hinweise).includes("BELOHNUNGEN"), "und behauptet keinen Preisabschnitt, den es nicht gibt"); @@ -502,7 +507,7 @@ melde("=== 7. Eine Kampagne ohne Preise ist unvollständig, nicht unlesbar ===") w.globalData.activityInfo.ac_schema_with_interaction_rules = JSON.stringify(s); }); const l = leseKampagne(leer); - ok(l.kuer.preise.length === 0 && l.hinweise.some((h) => h.includes("Preise")), + ok(l.kuer.preise.length === 0 && l.hinweise.some((h) => /Preis/.test(h)), "auch ein leerer Preisbaustein ergibt denselben Hinweis"); ok(l.kuer.aufgaben.length === 5, `und der Rest bleibt unberührt (${l.kuer.aufgaben.length} Aufgaben)`); @@ -699,6 +704,70 @@ melde("=== 10. Ein Wörterbuch mit Lücken wird gemeldet, nicht verschwiegen === "und kein Platzhalter steht dafür in der Kachel"); } +/* ===================================================================== + 10b. DIE ZWEITE VORLAGE — nicht jede Kampagne ist wie Gipfelstürmer + ===================================================================== */ +melde(""); +melde("=== 10b. „LIVE Glow Up“ ist ganz anders gebaut ==="); + +{ + const g = leseKampagne(seite.aufgabengruppe); + + /* GEGENPROBE ZUERST: Die Bausteine, auf denen Gipfelstürmer + beruht, gibt es hier WIRKLICH nicht. Ohne diese Zeile bewiese + der Rest nur, dass irgendeine Seite gelesen wurde. */ + ok(!g.auszug.bausteine.includes("live_rule_introduction") + && !g.auszug.bausteine.includes("live_reward_introduction") + && !g.auszug.bausteine.includes("live_campaign_intro"), + `ihr fehlen drei Bausteine, die Gipfelstürmer trägt`); + ok(g.auszug.bausteine.includes("live_task_group") + && g.auszug.bausteine.includes("live_secondary_gift"), + `dafür hat sie live_task_group und live_secondary_gift`); + + /* Und das Pflichtprogramm steht trotzdem. */ + ok(g.pflicht.titel === "LIVE Glow Up", `Titel: ${g.pflicht.titel}`); + ok(g.pflicht.start === "2026-10-05" && g.pflicht.ende === "2026-10-11", + `Zeitraum: ${g.pflicht.start} bis ${g.pflicht.ende}`); + ok(g.pflicht.zone === "Europe/Berlin" && !!g.kuer.bannerUrl, + `Zone und Banner da`); + + /* Aufgaben aus der Aufgabengruppe -- und OHNE Doppelte. Die + Vorlage legt je Reiter zwei Einträge an (Creator und Zuschauer); + zweimal derselbe Haken wäre ein Haken an zwei Stellen. */ + ok(g.kuer.aufgaben.length === 2, + `${g.kuer.aufgaben.length} Aufgaben aus der Aufgabengruppe`); + ok(new Set(g.kuer.aufgaben).size === g.kuer.aufgaben.length, + "keine davon doppelt"); + ok(g.kuer.aufgaben[0].includes("Missionen"), + `die erste heißt „${g.kuer.aufgaben[0]}“`); + + ok(g.kuer.geschenke.length === 1 && !!g.kuer.geschenke[0].name, + `${g.kuer.geschenke.length} Geschenk aus live_secondary_gift` + + ` („${g.kuer.geschenke[0]?.name}“)`); + ok(g.kuer.klapptexte.length === 2, + `${g.kuer.klapptexte.length} Klapptexte`); + ok(g.kuer.klapptexte.some((k) => /Woche 1/.test(k.text)), + " der Zeitplan steht darin (Woche 1 …)"); + + /* WAS ES NICHT GIBT, HEISST NICHT „konnte nicht gelesen werden“. + Das sind zwei verschiedene Nachrichten: Die eine schickt + jemanden suchen, die andere sagt ihm, dass nichts fehlt. */ + ok(g.hinweise.length === 2, + `${g.hinweise.length} Hinweise: ${g.hinweise.join(" | ")}`); + ok(g.hinweise.every((h) => /hat keine|hat keinen/.test(h)), + " beide sagen „hat keine“, keiner „konnte nicht gelesen werden“"); + + /* GEGENPROBE: Bei Gipfelstürmer sagt derselbe Leser gar nichts -- + dort ist alles da. */ + ok(leseKampagne(seite.gipfel).hinweise.length === 0, + "Gegenprobe: Gipfelstürmer meldet weiterhin nichts"); + + /* Und der Regeltext trägt den Zeitplan, nicht nur Überschriften. */ + const r = regelnText(g.kuer, g.hinweise); + ok(/Woche 1/.test(r) && /Woche 4/.test(r), + `der Regeltext enthält den ganzen Zeitplan (${r.length} Zeichen)`); +} + /* ===================================================================== 11. DER LESER LIEST NUR — nachgewiesen am Quelltext ===================================================================== */ @@ -758,8 +827,8 @@ melde("=== 12. Gleiche Eingabe, gleiches Ergebnis ==="); try { leseKampagne(text); gelesen++; } catch { geworfen++; void name; } } - ok(gelesen === 3 && geworfen === 1, - `${gelesen} der vier Seiten lesen sich, ${geworfen} wirft (die kaputte)`); + ok(gelesen === 4 && geworfen === 1, + `${gelesen} der fünf Seiten lesen sich, ${geworfen} wirft (die kaputte)`); } melde(""); diff --git a/server/pruef-kampagne.mjs b/server/pruef-kampagne.mjs index a32c7434..27c45d53 100644 --- a/server/pruef-kampagne.mjs +++ b/server/pruef-kampagne.mjs @@ -437,11 +437,26 @@ melde("=== 2. Jede Station wird einzeln geprüft ==="); `die drei Grenzen stehen: ${UMLEITUNGEN_MAX} Sprünge, ` + `${FRIST_SEITE_MS / 1000} s, ${SEITE_MAX / 1048576} MB`); - /* EIN EHRLICHER NAME. Kein Abruf gibt sich als Browser aus. */ - ok(gesehen.length > 0 && gesehen.every((g) => g.kennung.startsWith("DogfatherUniverse-Workspace/")), + /* DIE KENNUNG MUSS ZWEIERLEI KOENNEN (nachgemessen 07.10.2026). + + 1. NACH DER HANDYFASSUNG FRAGEN. TikTok baut die Seite + serverseitig nur fuer Handys auf. Gemessen: Desktop-Kennung + 17 KB ohne Daten, Android-Kennung 939 KB mit Daten -- und es + haengt allein an diesem einen Wort. + 2. SAGEN, WER FRAGT. Unser Name und unsere Adresse stehen + hinten dran. Nachgemessen liefert TikTok damit dieselben + 939 KB -- es kostet also nichts, ehrlich zu sein. + + Beides zusammen ist keine Verkleidung: Wir wollen die + Handyfassung und sagen dabei, wer wir sind. Faellt Teil 2 weg, + ist es eine; faellt Teil 1 weg, kommt nichts an. Deshalb wird + hier BEIDES verlangt. */ + ok(gesehen.length > 0 && gesehen.every((g) => g.kennung.includes("DogfatherUniverse-Workspace/")), `alle ${gesehen.length} Abrufe nennen, wer fragt`); - ok(/workspace\.dogfather-universe\.com/.test(KENNUNG) && !/Mozilla/i.test(KENNUNG), - "und die Kennung ist keine Browser-Verkleidung"); + ok(/workspace\.dogfather-universe\.com/.test(KENNUNG), + " mit Adresse zum Nachsehen"); + ok(/\bMobile\b/.test(KENNUNG), + " und sie fragt nach der Handyfassung -- sonst kommt nur eine leere Hülle"); } /* ===================================================================== @@ -726,10 +741,10 @@ melde("=== 9. Was schiefgehen kann, wird verständlich gesagt ==="); /* OHNE PREISE: 201 MIT HINWEIS, nicht 502. Unvollständig ist etwas anderes als falsch. */ const op = await einlesen(`Klick: ${TIK}/t/ohnepreise`); - ok(op.code === 201 && (op.j.hinweise || []).some((h) => h.includes("Preise")), + ok(op.code === 201 && (op.j.hinweise || []).some((h) => /Preis/.test(h)), `ohne Preise: HTTP ${op.code} mit Hinweis „${(op.j.hinweise || [])[0]}“`); const opZ = dbLesen("SELECT event_regeln FROM eintraege WHERE id = ?", op.j.id)[0]; - ok(/Bitte nachtragen/.test(opZ.event_regeln || ""), + ok(/Preis/.test(opZ.event_regeln || ""), "und die Kachel selbst sagt, dass etwas nachzutragen ist"); } diff --git a/server/pruefdaten/LIESMICH.md b/server/pruefdaten/LIESMICH.md index d605ad85..5ca4dda2 100644 --- a/server/pruefdaten/LIESMICH.md +++ b/server/pruefdaten/LIESMICH.md @@ -8,7 +8,7 @@ ohne dass etwas kaputt ist. ## Woher sie kommen -Alle vier wurden am **07.10.2026** von `www.tiktok.com` geholt, **unangemeldet**. +Alle fünf wurden am **07.10.2026** von `www.tiktok.com` geholt, **unangemeldet**. Das ist nachprüfbar in den Dateien selbst: globalData.userInfo = { "uid": "0" } @@ -31,7 +31,7 @@ Die Prüfung hält zusätzlich die SHA-256-Summe des **entpackten** Inhalts fest Absicht: Eine Prüfdatei, die sich still ändert, macht jedes grüne Häkchen darüber wertlos. -## Die vier Dateien +## Die fünf Dateien | Datei | Kampagne | `activity_id` | Zustand | Eingriff | |---|---|---|---|---| @@ -39,6 +39,7 @@ wertlos. | `kampagne-beendet.html.br` | Sonne im Gepäck | 7657456323453405959 | beendet (16) | **keiner** | | `kampagne-ohne-preise.html.br` | Stern Stunde | 7670899782609996552 | beendet (16) | Preisbaustein entfernt, siehe unten | | `kampagne-kaputt.html` | — | — | — | vollständig selbst geschrieben | +| `kampagne-aufgabengruppe.html.br` | LIVE Glow Up | 7690927903777262344 | läuft | **keiner** | ### `kampagne-gipfelstuermer.html.br` — der vollständige Fall @@ -64,8 +65,9 @@ sich nicht nachtragen. ### `kampagne-ohne-preise.html.br` — der einzige Eingriff -**Es gibt keine echte Kampagne ohne Preise.** Alle vier am 07.10.2026 geholten -Seiten haben sechs Preisgruppen. Dieser Fall muss deshalb hergestellt werden, und +**Es gibt keine echte Kampagne ohne Preise.** Die drei vollständigen Seiten vom +07.10.2026 haben alle sechs Preisgruppen (die vierte, „LIVE Glow Up“, hat gar +keine Preisliste — das ist etwas anderes als eine leere). Dieser Fall muss deshalb hergestellt werden, und das ist hier offen dokumentiert, statt in einem Skript zu verschwinden: Grundlage ist die unveränderte Seite von **Stern Stunde**. Daraus wurde in @@ -92,6 +94,25 @@ Sie enthält bewusst ein **anderes** JSON-Skript (`__NEXT_DATA__`) und eine Zeile, in der `activityId` vorkommt. Wer „das erste Skript" oder „irgendwo die Nummer" nimmt, wird hier grün und im Betrieb falsch. +### `kampagne-aufgabengruppe.html.br` — die ZWEITE Vorlage + +Unverändert, am 07.10.2026 geholt. Diese Seite beweist, dass es **nicht +eine** Bauart gibt: Ihr fehlen `live_rule_introduction`, +`live_reward_introduction` und `live_campaign_intro` — also genau die +drei Bausteine, auf denen Gipfelstürmer beruht. Stattdessen hat sie +`live_task_group` (Aufgaben als Reiter) und `live_secondary_gift`. + +Ohne sie hätte der Leser eine Vorlage gekonnt und bei der nächsten +Einladung vier Hinweise gemeldet, die alle „konnte nicht gelesen +werden" sagten — obwohl schlicht nichts da ist. Genau daran unterscheidet +er jetzt: **„hat keine Preisliste"** ist etwas anderes als **„konnte +nicht gelesen werden"**. Das eine schickt jemanden suchen, das andere +sagt ihm, dass nichts fehlt. + +Sie wurde mit der Kennung geholt, die auch im Betrieb gilt — eine +Handy-Kennung mit unserem Namen dahinter. Mit einer Desktop-Kennung +kommen nur 17 KB ohne Daten; gemessen am selben Tag auf sieben Wegen. + ## Keine Testdaten im Live-System Diese Dateien werden ausschließlich gelesen. Sie wandern nie in eine Datenbank, und diff --git a/server/pruefdaten/kampagne-aufgabengruppe.html.br b/server/pruefdaten/kampagne-aufgabengruppe.html.br new file mode 100644 index 00000000..d54d06c8 Binary files /dev/null and b/server/pruefdaten/kampagne-aufgabengruppe.html.br differ diff --git a/workspace/anleitung.html b/workspace/anleitung.html index 7ce9aacb..e25283b9 100644 --- a/workspace/anleitung.html +++ b/workspace/anleitung.html @@ -7,17 +7,17 @@ - - - - - + + + + + - - + + @@ -289,16 +289,16 @@ Und anleitung.js braucht es doppelt: Symbol, Farbe, Name und Adresse jeder Karte kommen von dort, nicht vom Server. --> - + - - - - - - + + + + + + diff --git a/workspace/anruf-probe.html b/workspace/anruf-probe.html index 528f490b..96dfd354 100644 --- a/workspace/anruf-probe.html +++ b/workspace/anruf-probe.html @@ -16,8 +16,8 @@ verdeckt. Gefunden hat es pruef-struktur. Richtig sind `gate.css` (die Farbwerte des Hauses) und `haus.css`. --> - - + + - +