Es laeuft: TikTok baut die Seite nur fuer Handys -- und zwei Vorlagen statt einer

Filipes Kachel ging nicht. Der Grund war nicht TikTok, sondern EIN WORT
in unserer Kennung.

DAS WORT HEISST "Mobile"
  Gestern hiess es hier noch "17 KB, keine Daten, von jedem Weg aus".
  Nachgemessen mit einem Keksglas und fuenf Kennungen:

    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

  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 EHRLICH: Unser Name und unsere Adresse stehen hinten
  an der Kennung. Nachgemessen liefert TikTok damit dieselben 939 KB.
  Es kostet also nichts. Die Pruefung verlangt jetzt BEIDES: "Mobile"
  (sonst kommt nichts an) und unseren Namen (sonst ist es eine
  Verkleidung).

ZWEI VORLAGEN, NICHT EINE
  Filipes Einladung fuehrt zu "LIVE Glow Up" -- und die ist ganz
  anders gebaut als "Gipfelstuermer". Ihr fehlen gleich DREI
  Bausteine, auf denen der Bauplan beruht: live_rule_introduction,
  live_reward_introduction, live_campaign_intro. Stattdessen hat sie
  live_task_group (Aufgaben als Reiter) und live_secondary_gift.

  Der Leser kann jetzt beide. Gemessen:

    Gipfelstuermer  5 Aufgaben, 5 Geschenke, 6 Preisgruppen, 0 Hinweise
    LIVE Glow Up    2 Aufgaben, 1 Geschenk,  0 Preise,       2 Hinweise

  Die doppelten Reiter fallen weg: Die Vorlage legt je Reiter zwei
  Eintraege an (Creator und Zuschauer), und zweimal derselbe Haken
  waere ein Haken an zwei Stellen.

"HAT KEINE" IST ETWAS ANDERES ALS "KONNTE NICHT GELESEN WERDEN"
  Ohne diese Unterscheidung haette LIVE Glow Up vier Hinweise gemeldet,
  die alle nach einem Fehler klingen -- obwohl schlicht nichts da ist.
  Das eine schickt jemanden suchen, das andere sagt ihm, dass nichts
  fehlt. Entsprechend haengt der Regeltext auch kein pauschales "Bitte
  nachtragen" mehr an: Jeder Hinweis sagt selbst, ob etwas zu tun ist.

FUENFTE PRUEFDATEI
  kampagne-aufgabengruppe.html.br -- die echte Seite von LIVE Glow Up,
  unveraendert, unangemeldet geholt (uid "0", anchor_id leer),
  147 KB gepackt, sofort zurueckgelesen und Byte fuer Byte verglichen.
  Ohne sie koennte der Leser eine Vorlage und faellt bei der naechsten
  Einladung um.

GEPRUEFT
  pruef-kampagne-lesen 112 -> 128 (eigener Abschnitt fuer die zweite
  Vorlage, mit Gegenprobe: Gipfelstuermer meldet weiterhin nichts) ·
  pruef-kampagne 102 -> 103 · pruef-struktur, pruef-zeichen, pruef-video

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-10-07 18:15:50 +02:00
co-authored by Claude Opus 5
parent 015ac7738e
commit b49a03d811
53 changed files with 940 additions and 720 deletions
+93 -10
View File
@@ -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();
}