From 80121e2b414a9fe01d7f2190b9bc292f8e2bcab9 Mon Sep 17 00:00:00 2001 From: Wayne Zheng Date: Sun, 2 Aug 2026 19:00:52 +0800 Subject: [PATCH] feat: add SAMR vehicle recall source --- server/glob.d.ts | 1 + server/sources/samr.ts | 34 ++++++++++++++++++++++++++++++++++ shared/pinyin.json | 1 + shared/pre-sources.ts | 9 +++++++++ shared/sources.json | 9 +++++++++ shared/updated-sources.ts | 3 ++- test/samr.test.ts | 29 +++++++++++++++++++++++++++++ 7 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 server/sources/samr.ts create mode 100644 test/samr.test.ts diff --git a/server/glob.d.ts b/server/glob.d.ts index ff95848bb..8cd152627 100644 --- a/server/glob.d.ts +++ b/server/glob.d.ts @@ -32,6 +32,7 @@ declare module 'glob:./sources/{*.ts,**/index.ts}' { export const pcbeta: typeof import('./sources/pcbeta') export const producthunt: typeof import('./sources/producthunt') export const qqvideo: typeof import('./sources/qqvideo') + export const samr: typeof import('./sources/samr') export const smzdm: typeof import('./sources/smzdm') export const solidot: typeof import('./sources/solidot') export const sputniknewscn: typeof import('./sources/sputniknewscn') diff --git a/server/sources/samr.ts b/server/sources/samr.ts new file mode 100644 index 000000000..0785cff3c --- /dev/null +++ b/server/sources/samr.ts @@ -0,0 +1,34 @@ +import * as cheerio from "cheerio" +import type { NewsItem } from "@shared/types" + +const sourceURL = "https://www.samrdprc.org.cn/qczh/gnzhqc/" + +export function parseSamrRecalls(html: string): NewsItem[] { + const $ = cheerio.load(html) + const news: NewsItem[] = [] + + $(".boxl_ul li").each((_, element) => { + const anchor = $(element).find("a").first() + const href = anchor.attr("href") + const title = anchor.attr("title")?.trim() || anchor.text().trim() + const date = $(element).text().match(/\d{4}-\d{2}-\d{2}/)?.[0] + if (!href || !title) return + + const url = new URL(href, sourceURL).toString() + news.push({ + id: url, + title, + url, + pubDate: date ? `${date}T00:00:00+08:00` : undefined, + }) + }) + + return news +} + +export default defineSource(async () => { + const html = await myFetch(sourceURL, { + responseType: "text" as any, + }) + return parseSamrRecalls(html) +}) diff --git a/shared/pinyin.json b/shared/pinyin.json index f51596692..e6d2117b9 100644 --- a/shared/pinyin.json +++ b/shared/pinyin.json @@ -18,6 +18,7 @@ "toutiao": "jinritoutiao", "ithome": "ITzhijia", "thepaper": "pengpaixinwen-rebang", + "samr": "guojiashichangjianguanzongju-guoneiqichezhaohui", "sputniknewscn": "weixingtongxunshe", "cankaoxiaoxi": "cankaoxiaoxi", "pcbeta-windows11": "yuanjingluntan-Win11", diff --git a/shared/pre-sources.ts b/shared/pre-sources.ts index 673285957..44af6892c 100644 --- a/shared/pre-sources.ts +++ b/shared/pre-sources.ts @@ -170,6 +170,15 @@ export const originSources = { color: "gray", home: "https://www.thepaper.cn", }, + "samr": { + name: "国家市场监管总局", + title: "国内汽车召回", + interval: Time.Common, + type: "realtime", + column: "china", + color: "red", + home: "https://www.samrdprc.org.cn/qczh/gnzhqc/", + }, "sputniknewscn": { name: "卫星通讯社", color: "orange", diff --git a/shared/sources.json b/shared/sources.json index 7ce3ed623..171d08107 100644 --- a/shared/sources.json +++ b/shared/sources.json @@ -203,6 +203,15 @@ "color": "gray", "interval": 1800000 }, + "samr": { + "title": "国内汽车召回", + "name": "国家市场监管总局", + "type": "realtime", + "column": "china", + "home": "https://www.samrdprc.org.cn/qczh/gnzhqc/", + "color": "red", + "interval": 1800000 + }, "sputniknewscn": { "name": "卫星通讯社", "column": "world", diff --git a/shared/updated-sources.ts b/shared/updated-sources.ts index 6903f2edf..19d4f9e46 100644 --- a/shared/updated-sources.ts +++ b/shared/updated-sources.ts @@ -1,4 +1,5 @@ export const updatedSourceIds = [ + "samr", "dongqiudi", "producthunt", "aihot", @@ -20,5 +21,5 @@ export const updatedSourceIds = [ "bilibili-hot-video", "bilibili-ranking", "kuaishou", - "toutiao" + "toutiao", ] as const diff --git a/test/samr.test.ts b/test/samr.test.ts new file mode 100644 index 000000000..b318acca0 --- /dev/null +++ b/test/samr.test.ts @@ -0,0 +1,29 @@ +import { describe, expect, it } from "vitest" +import { parseSamrRecalls } from "../server/sources/samr" + +describe("parseSamrRecalls", () => { + it("parses official recall entries", () => { + const html = ` + + ` + + expect(parseSamrRecalls(html)).toEqual([ + { + id: "https://www.samrdprc.org.cn/qczh/gnzhqc/202607/t20260730_115807.html", + title: "广汽丰田汽车有限公司召回部分铂智7汽车", + url: "https://www.samrdprc.org.cn/qczh/gnzhqc/202607/t20260730_115807.html", + pubDate: "2026-07-31T00:00:00+08:00", + }, + ]) + }) +})