'use strict'; const fs = require('fs'); const path = require('path'); function stripHtml(value) { return String(value || '') .replace(//gi, '\n') .replace(/<[^>]+>/g, ' ') .replace(/ /g, ' ') .replace(/&/g, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"') .replace(/\s+/g, ' ') .trim(); } function extractFaqFromLdJson(html, max = 50) { const re = /]*type=["']application\/ld\+json["'][^>]*>([\s\S]*?)<\/script>/gi; let m; while ((m = re.exec(String(html)))) { try { const data = JSON.parse(m[1].trim()); const candidates = Array.isArray(data) ? data : [data]; for (const item of candidates) { const main = item && (item.mainEntity || (item['@type'] === 'FAQPage' && item.mainEntity)); if (!Array.isArray(main)) continue; const out = main .filter((q) => q && q.name && q.acceptedAnswer && q.acceptedAnswer.text) .map((q) => ({ q: stripHtml(q.name), a: stripHtml(q.acceptedAnswer.text) })) .filter((q) => q.q && q.a) .slice(0, max); if (out.length) return out; } } catch { // 非 JSON-LD 块忽略 } } return []; } function loadFaqConfig(file) { try { return JSON.parse(fs.readFileSync(file, 'utf8')); } catch { return { enabled: false, pages: [] }; } } function createFaqFetcher(config = {}) { const pages = config.pages || []; const timeoutMs = config.timeoutMs || 15000; const max = config.maxFaqsPerPage || 50; async function fetchPage(url) { const res = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36', Accept: 'text/html,application/xhtml+xml' }, signal: AbortSignal.timeout(timeoutMs), }); if (!res.ok) throw new Error(`fetch ${url} failed: HTTP ${res.status}`); return extractFaqFromLdJson(await res.text(), max); } async function fetchAll() { const out = []; for (const page of pages) { try { const questions = await fetchPage(page.url); out.push({ ...page, questions }); } catch (err) { out.push({ ...page, questions: [], error: err.message }); } } return out; } return { fetchPage, fetchAll, pages }; } module.exports = { createFaqFetcher, loadFaqConfig, extractFaqFromLdJson, stripHtml };