Sync Parliament Atom fallback
This commit is contained in:
parent
f5384f8437
commit
2459d72420
120
src/sources.ts
120
src/sources.ts
@ -155,8 +155,97 @@ function parseParliamentDate(text: string): string | null {
|
||||
return Number.isNaN(value.getTime()) ? null : value.toISOString();
|
||||
}
|
||||
|
||||
async function fetchParliamentAtom(source: SourceDefinition): Promise<NormalizedItem[]> {
|
||||
const feedUrl = "https://www3.parliament.nz/en/channellisting/rss/9343/e37f168d-4521-4437-a6fd-6bb7ff54e800";
|
||||
const response = await fetch(feedUrl, {
|
||||
headers: {
|
||||
accept: "application/atom+xml,application/xml,text/xml;q=0.9,*/*;q=0.5",
|
||||
"user-agent": "Mozilla/5.0 (compatible; ArrowstackMediaIntelligence/0.1; +https://media.arrowstack.dev)"
|
||||
},
|
||||
redirect: "follow",
|
||||
signal: AbortSignal.timeout(20000)
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`Parliament Atom feed HTTP ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
const xml = await response.text();
|
||||
if (/radware page/i.test(xml) || /<title>\s*radware page\s*<\/title>/i.test(xml)) {
|
||||
throw new Error(`Parliament Atom feed blocked by Radware (bytes=${xml.length})`);
|
||||
}
|
||||
|
||||
const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: "@_" });
|
||||
const parsed = parser.parse(xml);
|
||||
let list = asArray<any>(
|
||||
parsed?.feed?.entry ??
|
||||
parsed?.rss?.channel?.item ??
|
||||
parsed?.["atom:feed"]?.["atom:entry"]
|
||||
);
|
||||
if (!list.length) list = rawFeedItems(xml);
|
||||
|
||||
const items = list.slice(0, 30).map((entry: any, index: number) => {
|
||||
const title = cleanXmlText(
|
||||
entry.title?.["#text"] ??
|
||||
entry["atom:title"]?.["#text"] ??
|
||||
entry.title ??
|
||||
entry["atom:title"] ??
|
||||
"Parliament item"
|
||||
);
|
||||
const summary = cleanXmlText(
|
||||
entry.summary?.["#text"] ??
|
||||
entry["atom:summary"]?.["#text"] ??
|
||||
entry.summary ??
|
||||
entry["atom:summary"] ??
|
||||
entry.description ??
|
||||
entry.content ??
|
||||
""
|
||||
);
|
||||
const linkValue = entry.link ?? entry["atom:link"];
|
||||
const url = firstLink(linkValue) || feedUrl;
|
||||
const publishedAt =
|
||||
cleanXmlText(
|
||||
entry.updated ??
|
||||
entry["atom:updated"] ??
|
||||
entry.published ??
|
||||
entry["atom:published"] ??
|
||||
entry.pubDate ??
|
||||
""
|
||||
) || null;
|
||||
|
||||
return {
|
||||
sourceId: "parliament" as const,
|
||||
externalId: String(
|
||||
entry.id?.["#text"] ??
|
||||
entry["atom:id"]?.["#text"] ??
|
||||
entry.id ??
|
||||
entry["atom:id"] ??
|
||||
entry.guid ??
|
||||
url ??
|
||||
index
|
||||
),
|
||||
title,
|
||||
summary,
|
||||
url,
|
||||
publishedAt,
|
||||
topics: ["parliament", "select-committee", "evidence"],
|
||||
raw: entry
|
||||
};
|
||||
}).filter((item: NormalizedItem) => item.title && item.url);
|
||||
|
||||
if (!items.length) {
|
||||
throw new Error(
|
||||
`Parliament Atom feed contained no entries (bytes=${xml.length}, prefix=${xml.slice(0, 120).replace(/\s+/g, " ")})`
|
||||
);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
async function fetchParliament(): Promise<NormalizedItem[]> {
|
||||
const source = SOURCES.find((s) => s.id === "parliament")!;
|
||||
let htmlFailure = "";
|
||||
|
||||
try {
|
||||
const response = await fetch(source.url, {
|
||||
headers: {
|
||||
accept: "text/html,application/xhtml+xml",
|
||||
@ -167,10 +256,14 @@ async function fetchParliament(): Promise<NormalizedItem[]> {
|
||||
signal: AbortSignal.timeout(20000)
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`${response.status} ${response.statusText} for ${source.url}`);
|
||||
throw new Error(`${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
const html = await response.text();
|
||||
if (/radware page/i.test(html) || /<title>\s*radware page\s*<\/title>/i.test(html)) {
|
||||
throw new Error(`blocked by Radware (bytes=${html.length})`);
|
||||
}
|
||||
|
||||
const $ = load(html);
|
||||
const seen = new Set<string>();
|
||||
const items: NormalizedItem[] = [];
|
||||
@ -228,12 +321,7 @@ async function fetchParliament(): Promise<NormalizedItem[]> {
|
||||
const container = $(element).closest("tr").length
|
||||
? $(element).closest("tr")
|
||||
: $(element).closest("li, article, .list-item, div");
|
||||
addItem(
|
||||
href,
|
||||
$(element).text(),
|
||||
container.text(),
|
||||
index
|
||||
);
|
||||
addItem(href, $(element).text(), container.text(), index);
|
||||
});
|
||||
|
||||
if (!items.length) {
|
||||
@ -246,14 +334,20 @@ async function fetchParliament(): Promise<NormalizedItem[]> {
|
||||
}
|
||||
}
|
||||
|
||||
if (!items.length) {
|
||||
const pageTitle = $("title").text().replace(/\s+/g, " ").trim();
|
||||
throw new Error(
|
||||
`Parliament evidence listing returned no document links (bytes=${html.length}, title=${pageTitle || "unknown"})`
|
||||
);
|
||||
if (items.length) return items;
|
||||
htmlFailure = `HTML listing contained no document links (bytes=${html.length})`;
|
||||
} catch (error) {
|
||||
htmlFailure = error instanceof Error ? error.message : String(error);
|
||||
}
|
||||
|
||||
return items;
|
||||
try {
|
||||
return await fetchParliamentAtom(source);
|
||||
} catch (error) {
|
||||
const atomFailure = error instanceof Error ? error.message : String(error);
|
||||
throw new Error(
|
||||
`Parliament unavailable from Arrowstack. HTML: ${htmlFailure || "unknown failure"}; Atom: ${atomFailure}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
interface SitemapEntry {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user