rsshub
Version:
Make RSS Great Again!
169 lines (168 loc) • 6.92 kB
JavaScript
import { t as parseDate } from "./parse-date-Cr0wQjV_.mjs";
import { t as cache_default } from "./cache-BkqOokyU.mjs";
import { t as got_default } from "./got-BTowW50G.mjs";
import { t as timezone } from "./timezone-BBvDwS_3.mjs";
import { constants, publicEncrypt } from "node:crypto";
import { load } from "cheerio";
//#region lib/routes/gov/zhengce/govall.ts
const searchCode = "17da70961a7";
const dataTypeId = "107";
const searchPageUrl = "https://sousuo.www.gov.cn/sousuo/search.shtml";
const searchApiUrl = "https://sousuoht.www.gov.cn/athena/forward/2B22E8E39E850E17F95A016A74FCB6B673336FA8B6FEC0E2955907EF9AEE06BE";
const athenaAppCredential = "a46884b2013e4d189f2a8e2d49a23525";
const athenaPublicKey = `-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCSMhMJQ+XLI7oW0k9Bwufur4Ag40tcsrzT7WZf6Ao0O/hyY1gZtCSYFxkxIZUXjW46j27XSW8IDX1rTJoHaMxHCWsOpTi2W5stybGYZytsY5on8gd8AIaS1d52h9eaS2TFydtJJtE50xHmT0WmoyoinWCuVCOkdCLhh9b9jSdeSQIDAQAB
-----END PUBLIC KEY-----`;
const splitWords = (value) => value?.split(/\s+/).filter(Boolean) ?? [];
const getMonthBoundary = (yearValue, monthValue, end) => {
const year = Number(yearValue);
const month = monthValue ? Number(monthValue) : end ? 12 : 1;
if (!year || !month || month < 1 || month > 12) return "";
return end ? timezone(new Date(year, month, 1), 8).getTime() - 1 : timezone(new Date(year, month - 1, 1), 8).getTime();
};
const buildSearchRequest = (advance) => {
const params = new URLSearchParams(advance);
const searchField = params.get("search_field") ?? params.get("searchfield");
const beginDateTime = getMonthBoundary(params.get("pubmintimeYear"), params.get("pubmintimeMonth"), false);
const endDateTime = getMonthBoundary(params.get("pubmaxtimeYear"), params.get("pubmaxtimeMonth"), true);
return {
code: searchCode,
historySearchWords: [],
dataTypeId,
orderBy: "time",
searchBy: searchField === "title" ? "title" : "all",
appendixType: "",
granularity: beginDateTime || endDateTime ? "CUSTOM" : "ALL",
trackTotalHits: true,
beginDateTime,
endDateTime,
isSearchForced: 0,
filters: [],
pageNo: 1,
pageSize: 20,
...Boolean(advance) ? {
isDefaultAdvanced: 1,
advancedFilters: [
{
fieldId: "",
fieldName: "containsAll",
searchWord: [...splitWords(params.get("allpro")), ...params.get("inpro") ? [params.get("inpro")] : []]
},
{
fieldId: "",
fieldName: "containsOne",
searchWord: splitWords(params.get("orpro"))
},
{
fieldId: "",
fieldName: "none",
searchWord: splitWords(params.get("notpro"))
}
],
customFilter: {
operator: "and",
properties: []
},
isAdvancedSearch: 1
} : {
allData: true,
customFilter: {
operator: "and",
properties: []
}
}
};
};
const buildSearchLink = (request) => {
const url = new URL(searchPageUrl);
for (const field of [
"code",
"dataTypeId",
"orderBy",
"searchBy",
"granularity",
"beginDateTime",
"endDateTime",
"allData",
"isDefaultAdvanced",
"advancedFilters"
]) {
const value = request[field];
if (value !== void 0 && value !== "") url.searchParams.set(field, typeof value === "string" ? value : JSON.stringify(value));
}
return url.href;
};
const normalizeSearchItem = (item) => {
return {
title: load((item.title_no_tag || item.title || "").replaceAll(/<br\s*\/?>/gi, " ")).text().replaceAll(/\s+/g, " "),
link: item.url,
description: item.summary || item.content,
...item.time && { pubDate: timezone(parseDate(item.time, "YYYY-MM-DD HH:mm:ss"), 8) }
};
};
const getAthenaAppKey = () => encodeURIComponent(publicEncrypt({
key: athenaPublicKey,
padding: constants.RSA_PKCS1_PADDING
}, Buffer.from(athenaAppCredential)).toString("base64"));
const route = {
path: "/govall/:advance?",
categories: ["government"],
example: "/gov/zhengce/govall/orpro=555¬pro=2&search_field=title",
parameters: { advance: "高级搜索选项,将作为请求参数直接添加到url后。目前已知的选项及其意义如下。" },
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false
},
radar: [{
source: ["www.gov.cn/"],
target: "/govall"
}],
name: "信息稿件",
maintainers: ["ciaranchen"],
handler,
url: "www.gov.cn/",
description: `| 选项 | 意义 | 备注 |
| :-----------------------------: | :----------------------------------------------: | :----------------------------------------: |
| orpro | 包含以下任意一个关键词。 | 用空格分隔。 |
| allpro | 包含以下全部关键词 | |
| notpro | 不包含以下关键词 | |
| inpro | 完整不拆分的关键词 | |
| searchfield | title: 搜索词在标题中;content: 搜索词在正文中。 | 上游已不支持仅正文,content 将搜索全部位置 |
| pubmintimeYear, pubmintimeMonth | 从某年某月 | 单独使用月份参数无法只筛选月份 |
| pubmaxtimeYear, pubmaxtimeMonth | 到某年某月 | 单独使用月份参数无法只筛选月份 |
| colid | 栏目 | 上游新接口已不再支持 |`
};
async function handler(ctx) {
const advance = ctx.req.param("advance");
const request = buildSearchRequest(advance);
const link = buildSearchLink(request);
const { data: response } = await got_default.post(searchApiUrl, {
headers: {
athenaAppKey: getAthenaAppKey(),
athenaAppName: encodeURIComponent("国网搜索")
},
json: request
});
if (response?.resultCode?.code !== 200 || !Array.isArray(response?.result?.data?.middle?.list)) throw new Error(`中国政府网搜索接口请求失败,错误代码:${response?.resultCode?.code ?? "未知"}`);
const list = response.result.data.middle.list.filter((item) => item.url).map((item) => normalizeSearchItem(item));
return {
title: "信息稿件 - 中国政府网",
link,
item: await Promise.all(list.map((item) => cache_default.tryGet(item.link, async () => {
let description = item.description;
try {
description = load((await got_default(item.link)).data)("#UCAP-CONTENT, div.TRS_UEDITOR").first().html() || description;
} catch {}
return {
...item,
description
};
})))
};
}
//#endregion
export { route };