← back to Crazy News Channel Shadowman
daily-cartoons/extract_stories.mjs
59 lines
#!/usr/bin/env node
// daily-cartoons/extract_stories.mjs — TK-12237.
//
// Safely parses ../real-news-data.js (window.P24_REAL_STORIES) and
// ../stories-data.js (window.P24_EXTRA_STORIES) using the same sandboxed
// vm-context pattern already used elsewhere in this repo for these plain
// <script src> globals (scripts/verify-tags.mjs,
// scripts/cartoon-shorts/make-cartoon-shorts.mjs) — never eval'd in the
// current process, never a require() of the file. Prints ONE flat JSON
// array of candidate articles to stdout. Read-only, $0, no network.
//
// Each article: { id, source: 'real-news'|'p24', headline, summary, tags,
// sourceUrl (real-news only, else null), sourceName, publishedAt (ISO or
// null if publishedLabel didn't parse) }.
import fs from 'node:fs';
import path from 'node:path';
import vm from 'node:vm';
import { fileURLToPath } from 'node:url';
const HERE = path.dirname(fileURLToPath(import.meta.url));
const SITE = path.resolve(HERE, '..');
const ctx = { window: {}, console };
vm.createContext(ctx);
for (const f of ['real-news-data.js', 'stories-data.js']) {
const p = path.join(SITE, f);
if (fs.existsSync(p)) vm.runInContext(fs.readFileSync(p, 'utf8'), ctx, { filename: f });
}
const REAL = Array.isArray(ctx.window.P24_REAL_STORIES) ? ctx.window.P24_REAL_STORIES : [];
const EXTRA = Array.isArray(ctx.window.P24_EXTRA_STORIES) ? ctx.window.P24_EXTRA_STORIES : [];
function publishedAt(story) {
const t = Date.parse(story.publishedLabel || '');
return Number.isFinite(t) ? new Date(t).toISOString() : null;
}
function toArticle(story, source) {
const stages = Array.isArray(story.stages) ? story.stages : [];
const stage = stages[story.stageIndex || 0] || stages[0] || {};
const headline = stage.headline || story.article?.dek || story.id;
const summary = stage.detail || story.article?.dek || (story.article?.paragraphs || [])[0] || '';
return {
id: story.id,
source,
headline: String(headline || '').trim(),
summary: String(summary || '').trim(),
tags: Array.isArray(story.tags) ? story.tags : [],
sourceUrl: source === 'real-news' ? (story.sourceUrl || null) : null,
sourceName: story.sourceName || null,
publishedAt: publishedAt(story),
};
}
const out = [
...REAL.filter((s) => s && s.id).map((s) => toArticle(s, 'real-news')),
...EXTRA.filter((s) => s && s.id).map((s) => toArticle(s, 'p24')),
];
process.stdout.write(JSON.stringify(out));