feat: add Game8 parser with Genshin and NTE adapters
Parsers are keyed by site template, not game: one Game8 parser serves every Game8 page, and adapters bind a URL and game to a parser. Adding a source for a known site is one SOURCES entry. Game8 uses at least three templates. This handles label/value detail tables (Genshin, 9 events) and column tables (NTE, 13 events). Its image-grid schedule has no year and no end date, so Arknights: Endfield is not supportable from that source and gets no adapter. canParse is a structural tripwire: a site redesign fails the run loudly instead of publishing an empty calendar that reads as "no events". Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
caefe6b0d7
commit
489ce92ef5
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Run an adapter against a checked-in fixture and print the events it yields.
|
||||
*
|
||||
* Offline and free — this never touches the network. Use it to eyeball an
|
||||
* adapter's output before writing the .expected.json, and to regenerate that
|
||||
* file after an intentional change.
|
||||
*
|
||||
* bun run parse <adapter-id> <fixture-path> [--now ISO] [--json]
|
||||
*/
|
||||
import { adapterById, ADAPTERS } from "../src/ingest/adapters/index.ts";
|
||||
import { GachaEvent } from "../src/shared/schema.ts";
|
||||
|
||||
const [adapterId, fixturePath] = Bun.argv.slice(2);
|
||||
const nowFlag = Bun.argv.indexOf("--now");
|
||||
const now =
|
||||
nowFlag !== -1 ? (Bun.argv[nowFlag + 1] ?? "") : "2026-08-14T00:00:00.000Z";
|
||||
const asJson = Bun.argv.includes("--json");
|
||||
|
||||
if (!adapterId || !fixturePath) {
|
||||
console.error("usage: bun run parse <adapter-id> <fixture-path> [--now ISO] [--json]");
|
||||
console.error(`adapters: ${ADAPTERS.map((a) => a.id).join(", ")}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const adapter = adapterById(adapterId);
|
||||
if (!adapter) {
|
||||
console.error(`unknown adapter '${adapterId}'`);
|
||||
console.error(`adapters: ${ADAPTERS.map((a) => a.id).join(", ")}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const html = await Bun.file(fixturePath).text();
|
||||
const events = adapter.parse(html, {
|
||||
now,
|
||||
sourceUrl: adapter.url,
|
||||
sourceId: adapter.id,
|
||||
game: adapter.game,
|
||||
});
|
||||
|
||||
// Validate here too — the script should fail loudly on a schema violation
|
||||
// rather than print something the pipeline would later reject.
|
||||
for (const e of events) GachaEvent.parse(e);
|
||||
|
||||
if (asJson) {
|
||||
console.log(JSON.stringify(events, null, 2));
|
||||
} else {
|
||||
console.log(`${adapter.id}: ${events.length} events\n`);
|
||||
for (const e of events) {
|
||||
const end = e.endsAt ? e.endsAt.slice(0, 10) : " (unknown)";
|
||||
console.log(
|
||||
` ${e.startsAt.slice(0, 10)} → ${end} [${e.type.padEnd(11)}] ${e.title}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user