mirror of
https://github.com/imsyy/DailyHotApi.git
synced 2026-01-12 05:04:56 +08:00
🐞 fix: 部分类型错误
- 新增 米游社 综合接口
This commit is contained in:
@@ -1,5 +0,0 @@
|
|||||||
.vscode
|
|
||||||
docker-compose.yml
|
|
||||||
dist
|
|
||||||
logs
|
|
||||||
!/.github
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"env": {
|
|
||||||
"browser": true,
|
|
||||||
"es2021": true
|
|
||||||
},
|
|
||||||
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
|
|
||||||
"parser": "@typescript-eslint/parser",
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": "latest",
|
|
||||||
"sourceType": "module"
|
|
||||||
},
|
|
||||||
"plugins": ["@typescript-eslint"],
|
|
||||||
"rules": {}
|
|
||||||
}
|
|
||||||
14
eslint.config.js
Normal file
14
eslint.config.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import globals from "globals";
|
||||||
|
import pluginJs from "@eslint/js";
|
||||||
|
import tseslint from "typescript-eslint";
|
||||||
|
|
||||||
|
/** @type {import('eslint').Linter.Config[]} */
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ["**/node_modules", "**/dist", "**/.gitignore", "**/logs", "**/docker-compose.yml"],
|
||||||
|
},
|
||||||
|
{ files: ["**/*.{js,mjs,cjs,ts}"] },
|
||||||
|
{ languageOptions: { globals: globals.node } },
|
||||||
|
pluginJs.configs.recommended,
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
];
|
||||||
35
package.json
35
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "dailyhot-api",
|
"name": "dailyhot-api",
|
||||||
"version": "2.0.6",
|
"version": "2.0.7",
|
||||||
"description": "An Api on Today's Hot list",
|
"description": "An Api on Today's Hot list",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"API",
|
"API",
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts,.vue --fix",
|
"lint": "eslint .",
|
||||||
"dev": "cross-env NODE_ENV=development tsx watch --no-cache src/index.ts",
|
"dev": "cross-env NODE_ENV=development tsx watch --no-cache src/index.ts",
|
||||||
"dev:cache": "cross-env NODE_ENV=development tsx watch src/index.ts",
|
"dev:cache": "cross-env NODE_ENV=development tsx watch src/index.ts",
|
||||||
"build": "tsc --project tsconfig.json",
|
"build": "tsc --project tsconfig.json",
|
||||||
@@ -36,30 +36,35 @@
|
|||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hono/node-server": "^1.13.5",
|
"@hono/node-server": "^1.13.7",
|
||||||
"axios": "^1.7.7",
|
"axios": "^1.7.8",
|
||||||
"chalk": "^5.3.0",
|
"chalk": "^5.3.0",
|
||||||
"cheerio": "1.0.0-rc.12",
|
"cheerio": "^1.0.0",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.6",
|
||||||
"feed": "^4.2.2",
|
"feed": "^4.2.2",
|
||||||
"hono": "^4.6.9",
|
"hono": "^4.6.12",
|
||||||
"md5": "^2.3.0",
|
"md5": "^2.3.0",
|
||||||
"node-cache": "^5.1.2",
|
"node-cache": "^5.1.2",
|
||||||
|
"puppeteer-cluster": "^0.24.0",
|
||||||
"rss-parser": "^3.13.0",
|
"rss-parser": "^3.13.0",
|
||||||
"winston": "^3.16.0",
|
"winston": "^3.17.0",
|
||||||
"xml2js": "^0.6.2"
|
"xml2js": "^0.6.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.17.6",
|
"@eslint/js": "^9.16.0",
|
||||||
|
"@types/md5": "^2.3.5",
|
||||||
|
"@types/node": "^22.10.1",
|
||||||
"@types/xml2js": "^0.4.14",
|
"@types/xml2js": "^0.4.14",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
"@typescript-eslint/eslint-plugin": "^8.17.0",
|
||||||
"@typescript-eslint/parser": "^7.18.0",
|
"@typescript-eslint/parser": "^8.17.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^8.57.1",
|
"eslint": "^9.16.0",
|
||||||
"prettier": "^3.3.3",
|
"globals": "^15.13.0",
|
||||||
"tsx": "^3.14.0",
|
"prettier": "^3.4.1",
|
||||||
"typescript": "^5.6.3"
|
"tsx": "^4.19.2",
|
||||||
|
"typescript": "^5.7.2",
|
||||||
|
"typescript-eslint": "^8.17.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
|
|||||||
1696
pnpm-lock.yaml
generated
1696
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,7 @@ export type Config = {
|
|||||||
const getEnvVariable = (key: string): string | undefined => {
|
const getEnvVariable = (key: string): string | undefined => {
|
||||||
const value = process.env[key];
|
const value = process.env[key];
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { serve } from "@hono/node-server";
|
import { serve } from "@hono/node-server";
|
||||||
import { config } from "./config.js";
|
import { config } from "./config.js";
|
||||||
|
import packageJson from "../package.json";
|
||||||
import logger from "./utils/logger.js";
|
import logger from "./utils/logger.js";
|
||||||
import app from "./app.js";
|
import app from "./app.js";
|
||||||
|
|
||||||
@@ -10,6 +11,7 @@ const serveHotApi: (port?: number) => void = (port: number = config.PORT) => {
|
|||||||
fetch: app.fetch,
|
fetch: app.fetch,
|
||||||
port,
|
port,
|
||||||
});
|
});
|
||||||
|
logger.info(`📦 Version: ${packageJson.version}`);
|
||||||
logger.info(`🔥 DailyHot API 成功在端口 ${port} 上运行`);
|
logger.info(`🔥 DailyHot API 成功在端口 ${port} 上运行`);
|
||||||
logger.info(`🔗 Local: 👉 http://localhost:${port}`);
|
logger.info(`🔗 Local: 👉 http://localhost:${port}`);
|
||||||
return apiServer;
|
return apiServer;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ let allRoutePath: Array<string> = [];
|
|||||||
const routersDirName: string = "routes";
|
const routersDirName: string = "routes";
|
||||||
|
|
||||||
// 排除路由
|
// 排除路由
|
||||||
const excludeRoutes: Array<string> = ["52pojie", "hostloc"];
|
const excludeRoutes: Array<string> = [];
|
||||||
|
|
||||||
// 建立完整目录路径
|
// 建立完整目录路径
|
||||||
const routersDirPath = path.join(__dirname, routersDirName);
|
const routersDirPath = path.join(__dirname, routersDirName);
|
||||||
|
|||||||
12
src/router.types.d.ts
vendored
12
src/router.types.d.ts
vendored
@@ -310,9 +310,19 @@ export type RouterType = {
|
|||||||
id: number;
|
id: number;
|
||||||
ttitle: string;
|
ttitle: string;
|
||||||
shareUrl: string;
|
shareUrl: string;
|
||||||
username:string;
|
username: string;
|
||||||
tpic: string;
|
tpic: string;
|
||||||
message: string;
|
message: string;
|
||||||
replynum: number;
|
replynum: number;
|
||||||
};
|
};
|
||||||
|
nodeseek: {
|
||||||
|
guid: {
|
||||||
|
_: string;
|
||||||
|
}[];
|
||||||
|
title: string;
|
||||||
|
description: string | string[];
|
||||||
|
"dc:creator": string;
|
||||||
|
pubDate: string[];
|
||||||
|
link: string[];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,24 +1,26 @@
|
|||||||
import type { RouterData, ListContext, Options } from "../types.js";
|
import type { RouterData, ListContext, Options, RouterResType } from "../types.js";
|
||||||
import type { RouterType } from "../router.types.js";
|
import type { RouterType } from "../router.types.js";
|
||||||
import { post } from "../utils/getData.js";
|
import { post } from "../utils/getData.js";
|
||||||
import { getTime } from "../utils/getTime.js";
|
import { getTime } from "../utils/getTime.js";
|
||||||
|
|
||||||
|
const typeMap: Record<string, string> = {
|
||||||
|
hot: "人气榜",
|
||||||
|
video: "视频榜",
|
||||||
|
comment: "热议榜",
|
||||||
|
collect: "收藏榜",
|
||||||
|
};
|
||||||
|
|
||||||
export const handleRoute = async (c: ListContext, noCache: boolean) => {
|
export const handleRoute = async (c: ListContext, noCache: boolean) => {
|
||||||
const type = c.req.query("type") || "hot";
|
const type = c.req.query("type") || "hot";
|
||||||
const { fromCache, data, updateTime } = await getList({ type }, noCache);
|
const { fromCache, data, updateTime } = await getList({ type }, noCache);
|
||||||
const routeData: RouterData = {
|
const routeData: RouterData = {
|
||||||
name: "36kr",
|
name: "36kr",
|
||||||
title: "36氪",
|
title: "36氪",
|
||||||
type: "热榜",
|
type: typeMap[type],
|
||||||
params: {
|
params: {
|
||||||
type: {
|
type: {
|
||||||
name: "热榜分类",
|
name: "热榜分类",
|
||||||
type: {
|
type: typeMap,
|
||||||
hot: "人气榜",
|
|
||||||
video: "视频榜",
|
|
||||||
comment: "热议榜",
|
|
||||||
collect: "收藏榜",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
link: "https://m.36kr.com/hot-list-m",
|
link: "https://m.36kr.com/hot-list-m",
|
||||||
@@ -30,7 +32,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
|
|||||||
return routeData;
|
return routeData;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getList = async (options: Options, noCache: boolean) => {
|
const getList = async (options: Options, noCache: boolean): Promise<RouterResType> => {
|
||||||
const { type } = options;
|
const { type } = options;
|
||||||
const url = `https://gateway.36kr.com/api/mis/nav/home/nav/rank/${type}`;
|
const url = `https://gateway.36kr.com/api/mis/nav/home/nav/rank/${type}`;
|
||||||
const result = await post({
|
const result = await post({
|
||||||
@@ -67,7 +69,7 @@ const getList = async (options: Options, noCache: boolean) => {
|
|||||||
cover: item.widgetImage,
|
cover: item.widgetImage,
|
||||||
author: item.authorName,
|
author: item.authorName,
|
||||||
timestamp: getTime(v.publishTime),
|
timestamp: getTime(v.publishTime),
|
||||||
hot: item.statCollect,
|
hot: item.statCollect || undefined,
|
||||||
url: `https://www.36kr.com/p/${v.itemId}`,
|
url: `https://www.36kr.com/p/${v.itemId}`,
|
||||||
mobileUrl: `https://m.36kr.com/p/${v.itemId}`,
|
mobileUrl: `https://m.36kr.com/p/${v.itemId}`,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { RouterData } from "../types.js";
|
import type { RouterData, RouterResType } from "../types.js";
|
||||||
import type { RouterType } from "../router.types.js";
|
import type { RouterType } from "../router.types.js";
|
||||||
import { getToken, sign } from "../utils/getToken/51cto.js";
|
import { getToken, sign } from "../utils/getToken/51cto.js";
|
||||||
import { get } from "../utils/getData.js";
|
import { get } from "../utils/getData.js";
|
||||||
@@ -19,7 +19,7 @@ export const handleRoute = async (_: undefined, noCache: boolean) => {
|
|||||||
return routeData;
|
return routeData;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getList = async (noCache: boolean) => {
|
const getList = async (noCache: boolean): Promise<RouterResType> => {
|
||||||
const url = `https://api-media.51cto.com/index/index/recommend`;
|
const url = `https://api-media.51cto.com/index/index/recommend`;
|
||||||
const params = {
|
const params = {
|
||||||
page: 1,
|
page: 1,
|
||||||
@@ -49,6 +49,7 @@ const getList = async (noCache: boolean) => {
|
|||||||
cover: v.cover,
|
cover: v.cover,
|
||||||
desc: v.abstract,
|
desc: v.abstract,
|
||||||
timestamp: getTime(v.pubdate),
|
timestamp: getTime(v.pubdate),
|
||||||
|
hot: undefined,
|
||||||
url: v.url,
|
url: v.url,
|
||||||
mobileUrl: v.url,
|
mobileUrl: v.url,
|
||||||
})),
|
})),
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { RouterData, ListContext, Options } from "../types.js";
|
import type { RouterData, ListContext, Options, RouterResType } from "../types.js";
|
||||||
import type { RouterType } from "../router.types.js";
|
|
||||||
import { web } from "../utils/getData.js";
|
import { web } from "../utils/getData.js";
|
||||||
import { extractRss, parseRSS } from "../utils/parseRSS.js";
|
import { extractRss, parseRSS } from "../utils/parseRSS.js";
|
||||||
import { getTime } from "../utils/getTime.js";
|
import { getTime } from "../utils/getTime.js";
|
||||||
@@ -31,7 +30,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
|
|||||||
return routeData;
|
return routeData;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getList = async (options: Options, noCache: boolean) => {
|
const getList = async (options: Options, noCache: boolean): Promise<RouterResType> => {
|
||||||
const { type } = options;
|
const { type } = options;
|
||||||
const url = `https://www.52pojie.cn/forum.php?mod=guide&view=${type}&rss=1`;
|
const url = `https://www.52pojie.cn/forum.php?mod=guide&view=${type}&rss=1`;
|
||||||
const result = await web({
|
const result = await web({
|
||||||
@@ -41,26 +40,24 @@ const getList = async (options: Options, noCache: boolean) => {
|
|||||||
"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Mobile Safari/537.36",
|
"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Mobile Safari/537.36",
|
||||||
});
|
});
|
||||||
const parseData = async () => {
|
const parseData = async () => {
|
||||||
if (typeof result?.data === "string") {
|
if (typeof result?.data !== "string") return [];
|
||||||
const rssContent = extractRss(result.data);
|
const rssContent = extractRss(result.data);
|
||||||
return await parseRSS(rssContent);
|
if (!rssContent) return [];
|
||||||
} else {
|
return await parseRSS(rssContent);
|
||||||
return [];
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
const list = await parseData();
|
const list = await parseData();
|
||||||
return {
|
return {
|
||||||
fromCache: result.fromCache,
|
fromCache: result.fromCache,
|
||||||
updateTime: result.updateTime,
|
updateTime: result.updateTime,
|
||||||
data: list.map((v: RouterType["discuz"]) => ({
|
data: list.map((v, i) => ({
|
||||||
id: v.guid,
|
id: v.guid || i,
|
||||||
title: v.title,
|
title: v.title || "",
|
||||||
desc: v.content,
|
desc: v.content || "",
|
||||||
author: v.author,
|
author: v.author || "",
|
||||||
timestamp: getTime(v.pubDate),
|
timestamp: getTime(v.pubDate || 0),
|
||||||
hot: null,
|
hot: 0,
|
||||||
url: v.link,
|
url: v.link || "",
|
||||||
mobileUrl: v.link,
|
mobileUrl: v.link || "",
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,29 @@
|
|||||||
import type { RouterData, ListContext, Options } from "../types.js";
|
import type { RouterData, ListContext, Options, RouterResType } from "../types.js";
|
||||||
import type { RouterType } from "../router.types.js";
|
import type { RouterType } from "../router.types.js";
|
||||||
import { get } from "../utils/getData.js";
|
import { get } from "../utils/getData.js";
|
||||||
import { getTime } from "../utils/getTime.js";
|
import { getTime } from "../utils/getTime.js";
|
||||||
|
|
||||||
|
const typeMap: Record<string, string> = {
|
||||||
|
"-1": "综合",
|
||||||
|
"155": "番剧",
|
||||||
|
"1": "动画",
|
||||||
|
"60": "娱乐",
|
||||||
|
"201": "生活",
|
||||||
|
"58": "音乐",
|
||||||
|
"123": "舞蹈·偶像",
|
||||||
|
"59": "游戏",
|
||||||
|
"70": "科技",
|
||||||
|
"68": "影视",
|
||||||
|
"69": "体育",
|
||||||
|
"125": "鱼塘",
|
||||||
|
};
|
||||||
|
|
||||||
|
const rangeMap: Record<string, string> = {
|
||||||
|
DAY: "今日",
|
||||||
|
THREE_DAYS: "三日",
|
||||||
|
WEEK: "本周",
|
||||||
|
};
|
||||||
|
|
||||||
export const handleRoute = async (c: ListContext, noCache: boolean) => {
|
export const handleRoute = async (c: ListContext, noCache: boolean) => {
|
||||||
const type = c.req.query("type") || "-1";
|
const type = c.req.query("type") || "-1";
|
||||||
const range = c.req.query("range") || "DAY";
|
const range = c.req.query("range") || "DAY";
|
||||||
@@ -10,33 +31,16 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
|
|||||||
const routeData: RouterData = {
|
const routeData: RouterData = {
|
||||||
name: "acfun",
|
name: "acfun",
|
||||||
title: "AcFun",
|
title: "AcFun",
|
||||||
type: "排行榜",
|
type: `排行榜 · ${typeMap[type]}`,
|
||||||
description: "AcFun是一家弹幕视频网站,致力于为每一个人带来欢乐。",
|
description: "AcFun是一家弹幕视频网站,致力于为每一个人带来欢乐。",
|
||||||
params: {
|
params: {
|
||||||
type: {
|
type: {
|
||||||
name: "频道",
|
name: "频道",
|
||||||
type: {
|
type: typeMap,
|
||||||
"-1": "全站综合",
|
|
||||||
"155": "番剧",
|
|
||||||
"1": "动画",
|
|
||||||
"60": "娱乐",
|
|
||||||
"201": "生活",
|
|
||||||
"58": "音乐",
|
|
||||||
"123": "舞蹈·偶像",
|
|
||||||
"59": "游戏",
|
|
||||||
"70": "科技",
|
|
||||||
"68": "影视",
|
|
||||||
"69": "体育",
|
|
||||||
"125": "鱼塘",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
range: {
|
range: {
|
||||||
name: "时间",
|
name: "时间",
|
||||||
type: {
|
type: rangeMap,
|
||||||
DAY: "今日",
|
|
||||||
THREE_DAYS: "三日",
|
|
||||||
WEEK: "本周",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
link: "https://www.acfun.cn/rank/list/",
|
link: "https://www.acfun.cn/rank/list/",
|
||||||
@@ -48,7 +52,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
|
|||||||
return routeData;
|
return routeData;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getList = async (options: Options, noCache: boolean) => {
|
const getList = async (options: Options, noCache: boolean): Promise<RouterResType> => {
|
||||||
const { type, range } = options;
|
const { type, range } = options;
|
||||||
const url = `https://www.acfun.cn/rest/pc-direct/rank/channel?channelId=${type === "-1" ? "" : type}&rankLimit=30&rankPeriod=${range}`;
|
const url = `https://www.acfun.cn/rest/pc-direct/rank/channel?channelId=${type === "-1" ? "" : type}&rankLimit=30&rankPeriod=${range}`;
|
||||||
const result = await get({
|
const result = await get({
|
||||||
|
|||||||
@@ -1,25 +1,27 @@
|
|||||||
import type { RouterData, ListContext, Options } from "../types.js";
|
import type { RouterData, ListContext, Options, RouterResType } from "../types.js";
|
||||||
import type { RouterType } from "../router.types.js";
|
import type { RouterType } from "../router.types.js";
|
||||||
import { get } from "../utils/getData.js";
|
import { get } from "../utils/getData.js";
|
||||||
|
|
||||||
|
const typeMap: Record<string, string> = {
|
||||||
|
realtime: "热搜",
|
||||||
|
novel: "小说",
|
||||||
|
movie: "电影",
|
||||||
|
teleplay: "电视剧",
|
||||||
|
car: "汽车",
|
||||||
|
game: "游戏",
|
||||||
|
};
|
||||||
|
|
||||||
export const handleRoute = async (c: ListContext, noCache: boolean) => {
|
export const handleRoute = async (c: ListContext, noCache: boolean) => {
|
||||||
const type = c.req.query("type") || "realtime";
|
const type = c.req.query("type") || "realtime";
|
||||||
const { fromCache, data, updateTime } = await getList({ type }, noCache);
|
const { fromCache, data, updateTime } = await getList({ type }, noCache);
|
||||||
const routeData: RouterData = {
|
const routeData: RouterData = {
|
||||||
name: "baidu",
|
name: "baidu",
|
||||||
title: "百度",
|
title: "百度",
|
||||||
type: "热搜榜",
|
type: typeMap[type],
|
||||||
params: {
|
params: {
|
||||||
type: {
|
type: {
|
||||||
name: "热搜类别",
|
name: "热搜类别",
|
||||||
type: {
|
type: typeMap,
|
||||||
realtime: "热搜",
|
|
||||||
novel: "小说",
|
|
||||||
movie: "电影",
|
|
||||||
teleplay: "电视剧",
|
|
||||||
car: "汽车",
|
|
||||||
game: "游戏",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
link: "https://top.baidu.com/board",
|
link: "https://top.baidu.com/board",
|
||||||
@@ -31,7 +33,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
|
|||||||
return routeData;
|
return routeData;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getList = async (options: Options, noCache: boolean) => {
|
const getList = async (options: Options, noCache: boolean): Promise<RouterResType> => {
|
||||||
const { type } = options;
|
const { type } = options;
|
||||||
const url = `https://top.baidu.com/board?tab=${type}`;
|
const url = `https://top.baidu.com/board?tab=${type}`;
|
||||||
const result = await get({
|
const result = await get({
|
||||||
@@ -55,8 +57,8 @@ const getList = async (options: Options, noCache: boolean) => {
|
|||||||
desc: v.desc,
|
desc: v.desc,
|
||||||
cover: v.img,
|
cover: v.img,
|
||||||
author: v.show?.length ? v.show : "",
|
author: v.show?.length ? v.show : "",
|
||||||
timestamp: null,
|
timestamp: 0,
|
||||||
hot: Number(v.hotScore),
|
hot: Number(v.hotScore || 0),
|
||||||
url: `https://www.baidu.com/s?wd=${encodeURIComponent(v.query)}`,
|
url: `https://www.baidu.com/s?wd=${encodeURIComponent(v.query)}`,
|
||||||
mobileUrl: v.rawUrl,
|
mobileUrl: v.rawUrl,
|
||||||
})),
|
})),
|
||||||
|
|||||||
@@ -1,35 +1,37 @@
|
|||||||
import type { RouterData, ListContext, Options } from "../types.js";
|
import type { RouterData, ListContext, Options, RouterResType } from "../types.js";
|
||||||
import type { RouterType } from "../router.types.js";
|
import type { RouterType } from "../router.types.js";
|
||||||
import { get } from "../utils/getData.js";
|
import { get } from "../utils/getData.js";
|
||||||
import getBiliWbi from "../utils/getToken/bilibili.js";
|
import getBiliWbi from "../utils/getToken/bilibili.js";
|
||||||
import { getTime } from "../utils/getTime.js";
|
import { getTime } from "../utils/getTime.js";
|
||||||
|
|
||||||
|
const typeMap: Record<string, string> = {
|
||||||
|
"0": "全站",
|
||||||
|
"1": "动画",
|
||||||
|
"3": "音乐",
|
||||||
|
"4": "游戏",
|
||||||
|
"5": "娱乐",
|
||||||
|
"36": "科技",
|
||||||
|
"119": "鬼畜",
|
||||||
|
"129": "舞蹈",
|
||||||
|
"155": "时尚",
|
||||||
|
"160": "生活",
|
||||||
|
"168": "国创相关",
|
||||||
|
"188": "数码",
|
||||||
|
"181": "影视",
|
||||||
|
};
|
||||||
|
|
||||||
export const handleRoute = async (c: ListContext, noCache: boolean) => {
|
export const handleRoute = async (c: ListContext, noCache: boolean) => {
|
||||||
const type = c.req.query("type") || "0";
|
const type = c.req.query("type") || "0";
|
||||||
const { fromCache, data, updateTime } = await getList({ type }, noCache);
|
const { fromCache, data, updateTime } = await getList({ type }, noCache);
|
||||||
const routeData: RouterData = {
|
const routeData: RouterData = {
|
||||||
name: "bilibili",
|
name: "bilibili",
|
||||||
title: "哔哩哔哩",
|
title: "哔哩哔哩",
|
||||||
type: "热门榜",
|
type: `热榜 · ${typeMap[type]}`,
|
||||||
description: "你所热爱的,就是你的生活",
|
description: "你所热爱的,就是你的生活",
|
||||||
params: {
|
params: {
|
||||||
type: {
|
type: {
|
||||||
name: "排行榜分区",
|
name: "排行榜分区",
|
||||||
type: {
|
type: typeMap,
|
||||||
0: "全站",
|
|
||||||
1: "动画",
|
|
||||||
3: "音乐",
|
|
||||||
4: "游戏",
|
|
||||||
5: "娱乐",
|
|
||||||
36: "科技",
|
|
||||||
119: "鬼畜",
|
|
||||||
129: "舞蹈",
|
|
||||||
155: "时尚",
|
|
||||||
160: "生活",
|
|
||||||
168: "国创相关",
|
|
||||||
188: "数码",
|
|
||||||
181: "影视",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
link: "https://www.bilibili.com/v/popular/rank/all",
|
link: "https://www.bilibili.com/v/popular/rank/all",
|
||||||
@@ -41,7 +43,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
|
|||||||
return routeData;
|
return routeData;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getList = async (options: Options, noCache: boolean) => {
|
const getList = async (options: Options, noCache: boolean): Promise<RouterResType> => {
|
||||||
const { type } = options;
|
const { type } = options;
|
||||||
const wbiData = await getBiliWbi();
|
const wbiData = await getBiliWbi();
|
||||||
const url = `https://api.bilibili.com/x/web-interface/ranking/v2?tid=${type}&type=all&${wbiData}`;
|
const url = `https://api.bilibili.com/x/web-interface/ranking/v2?tid=${type}&type=all&${wbiData}`;
|
||||||
@@ -64,10 +66,10 @@ const getList = async (options: Options, noCache: boolean) => {
|
|||||||
id: v.bvid,
|
id: v.bvid,
|
||||||
title: v.title,
|
title: v.title,
|
||||||
desc: v.desc || "该视频暂无简介",
|
desc: v.desc || "该视频暂无简介",
|
||||||
cover: v.pic.replace(/http:/, "https:"),
|
cover: v.pic?.replace(/http:/, "https:"),
|
||||||
author: v.owner.name,
|
author: v.owner?.name,
|
||||||
timestamp: getTime(v.pubdate),
|
timestamp: getTime(v.pubdate),
|
||||||
hot: v.stat.view,
|
hot: v.stat?.view || 0,
|
||||||
url: v.short_link_v2 || `https://www.bilibili.com/video/${v.bvid}`,
|
url: v.short_link_v2 || `https://www.bilibili.com/video/${v.bvid}`,
|
||||||
mobileUrl: `https://m.bilibili.com/video/${v.bvid}`,
|
mobileUrl: `https://m.bilibili.com/video/${v.bvid}`,
|
||||||
})),
|
})),
|
||||||
@@ -93,7 +95,7 @@ const getList = async (options: Options, noCache: boolean) => {
|
|||||||
id: v.bvid,
|
id: v.bvid,
|
||||||
title: v.title,
|
title: v.title,
|
||||||
desc: v.desc || "该视频暂无简介",
|
desc: v.desc || "该视频暂无简介",
|
||||||
cover: v.pic.replace(/http:/, "https:"),
|
cover: v.pic?.replace(/http:/, "https:"),
|
||||||
author: v.author,
|
author: v.author,
|
||||||
timestamp: null,
|
timestamp: null,
|
||||||
hot: v.video_review,
|
hot: v.video_review,
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ const getList = async (noCache: boolean) => {
|
|||||||
cover: v.tpic,
|
cover: v.tpic,
|
||||||
author: v.username,
|
author: v.username,
|
||||||
desc: v.ttitle,
|
desc: v.ttitle,
|
||||||
timestamp: null,
|
timestamp: undefined,
|
||||||
hot: null,
|
hot: undefined,
|
||||||
url: v.shareUrl,
|
url: v.shareUrl,
|
||||||
mobileUrl: v.shareUrl,
|
mobileUrl: v.shareUrl,
|
||||||
})),
|
})),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { RouterData } from "../types.js";
|
import type { RouterData, RouterResType } from "../types.js";
|
||||||
import type { RouterType } from "../router.types.js";
|
import type { RouterType } from "../router.types.js";
|
||||||
import { get } from "../utils/getData.js";
|
import { get } from "../utils/getData.js";
|
||||||
import { getTime } from "../utils/getTime.js";
|
import { getTime } from "../utils/getTime.js";
|
||||||
@@ -19,7 +19,7 @@ export const handleRoute = async (_: undefined, noCache: boolean) => {
|
|||||||
return routeData;
|
return routeData;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getList = async (noCache: boolean) => {
|
const getList = async (noCache: boolean): Promise<RouterResType> => {
|
||||||
const url = "https://blog.csdn.net/phoenix/web/blog/hot-rank?page=0&pageSize=30";
|
const url = "https://blog.csdn.net/phoenix/web/blog/hot-rank?page=0&pageSize=30";
|
||||||
const result = await get({ url, noCache });
|
const result = await get({ url, noCache });
|
||||||
const list = result.data.data;
|
const list = result.data.data;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { RouterData } from "../types.js";
|
import type { RouterData } from "../types.js";
|
||||||
import { load } from "cheerio";
|
import { load } from "cheerio";
|
||||||
import { get } from "../utils/getData.js";
|
import { get } from "../utils/getData.js";
|
||||||
|
import { getTime } from "../utils/getTime.js";
|
||||||
|
|
||||||
export const handleRoute = async (_: undefined, noCache: boolean) => {
|
export const handleRoute = async (_: undefined, noCache: boolean) => {
|
||||||
const { fromCache, data, updateTime } = await getList(noCache);
|
const { fromCache, data, updateTime } = await getList(noCache);
|
||||||
@@ -42,9 +43,9 @@ const getList = async (noCache: boolean) => {
|
|||||||
title: dom.find("h3 a").text().trim(),
|
title: dom.find("h3 a").text().trim(),
|
||||||
cover: dom.find(".pic-wrap img").attr("src"),
|
cover: dom.find(".pic-wrap img").attr("src"),
|
||||||
desc: dom.find(".block p").text().trim(),
|
desc: dom.find(".block p").text().trim(),
|
||||||
timestamp: dom.find("span.pubtime").text().trim(),
|
timestamp: getTime(dom.find("span.pubtime").text().trim()),
|
||||||
hot: null,
|
hot: 0,
|
||||||
url,
|
url: url || `https://www.douban.com/group/topic/${getNumbers(url)}`,
|
||||||
mobileUrl: `https://m.douban.com/group/topic/${getNumbers(url)}/`,
|
mobileUrl: `https://m.douban.com/group/topic/${getNumbers(url)}/`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export const handleRoute = async (_: undefined, noCache: boolean) => {
|
|||||||
const routeData: RouterData = {
|
const routeData: RouterData = {
|
||||||
name: "douban-movie",
|
name: "douban-movie",
|
||||||
title: "豆瓣电影",
|
title: "豆瓣电影",
|
||||||
type: "新片排行榜",
|
type: "新片榜",
|
||||||
link: "https://movie.douban.com/chart",
|
link: "https://movie.douban.com/chart",
|
||||||
total: data?.length || 0,
|
total: data?.length || 0,
|
||||||
updateTime,
|
updateTime,
|
||||||
@@ -51,9 +51,9 @@ const getList = async (noCache: boolean) => {
|
|||||||
title: `【${score}】${dom.find("a").attr("title")}`,
|
title: `【${score}】${dom.find("a").attr("title")}`,
|
||||||
cover: dom.find("img").attr("src"),
|
cover: dom.find("img").attr("src"),
|
||||||
desc: dom.find("p.pl").text(),
|
desc: dom.find("p.pl").text(),
|
||||||
timestamp: null,
|
timestamp: undefined,
|
||||||
hot: getNumbers(dom.find("span.pl").text()),
|
hot: getNumbers(dom.find("span.pl").text()),
|
||||||
url,
|
url: url || `https://movie.douban.com/subject/${getNumbers(url)}/`,
|
||||||
mobileUrl: `https://m.douban.com/movie/subject/${getNumbers(url)}/`,
|
mobileUrl: `https://m.douban.com/movie/subject/${getNumbers(url)}/`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import type { RouterData, ListContext, Options } from "../types.js";
|
import type { RouterData } from "../types.js";
|
||||||
import type { RouterType } from "../router.types.js";
|
import type { RouterType } from "../router.types.js";
|
||||||
import { get } from "../utils/getData.js";
|
import { get } from "../utils/getData.js";
|
||||||
import { getTime } from "../utils/getTime.js";
|
import { getTime } from "../utils/getTime.js";
|
||||||
|
|
||||||
const mappings = {
|
const mappings: Record<string, string> = {
|
||||||
O_TIME: "发震时刻(UTC+8)",
|
O_TIME: "发震时刻(UTC+8)",
|
||||||
LOCATION_C: "参考位置",
|
LOCATION_C: "参考位置",
|
||||||
M: "震级(M)",
|
M: "震级(M)",
|
||||||
@@ -13,34 +13,12 @@ const mappings = {
|
|||||||
SAVE_TIME: "录入时间",
|
SAVE_TIME: "录入时间",
|
||||||
};
|
};
|
||||||
|
|
||||||
const typeMappings = {
|
export const handleRoute = async (_: undefined, noCache: boolean) => {
|
||||||
1: "最近24小时地震信息",
|
const { fromCache, data, updateTime } = await getList(noCache);
|
||||||
2: "最近48小时地震信息",
|
|
||||||
3: "最近7天地震信息",
|
|
||||||
4: "最近30天地震信息",
|
|
||||||
5: "最近一年3.0级以上地震信息",
|
|
||||||
6: "最近一年地震信息",
|
|
||||||
7: "最近一年3.0级以下地震",
|
|
||||||
8: "最近一年4.0级以上地震信息",
|
|
||||||
9: "最近一年5.0级以上地震信息",
|
|
||||||
0: "最近一年6.0级以上地震信息",
|
|
||||||
};
|
|
||||||
|
|
||||||
export const handleRoute = async (c: ListContext, noCache: boolean) => {
|
|
||||||
const type = c.req.query("type") || "5";
|
|
||||||
const { fromCache, data, updateTime } = await getList({ type }, noCache);
|
|
||||||
const routeData: RouterData = {
|
const routeData: RouterData = {
|
||||||
name: "earthquake",
|
name: "earthquake",
|
||||||
title: "中国地震台",
|
title: "中国地震台",
|
||||||
type: "地震速报",
|
type: "地震速报",
|
||||||
params: {
|
|
||||||
type: {
|
|
||||||
name: "速报分类",
|
|
||||||
type: {
|
|
||||||
...typeMappings,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
link: "https://news.ceic.ac.cn/",
|
link: "https://news.ceic.ac.cn/",
|
||||||
total: data?.length || 0,
|
total: data?.length || 0,
|
||||||
updateTime,
|
updateTime,
|
||||||
@@ -50,12 +28,12 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
|
|||||||
return routeData;
|
return routeData;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getList = async (options: Options, noCache: boolean) => {
|
const getList = async (noCache: boolean) => {
|
||||||
const { type } = options;
|
const url = `https://news.ceic.ac.cn/speedsearch.html`;
|
||||||
const url = `http://www.ceic.ac.cn/ajax/speedsearch?num=${type}`;
|
|
||||||
const result = await get({ url, noCache });
|
const result = await get({ url, noCache });
|
||||||
const data = result.data.replace(/,"page":"(.*?)","num":/, ',"num":');
|
const regex = /const newdata = (\[.*?\]);/s;
|
||||||
const list = JSON.parse(data.substring(1, data.length - 1)).shuju;
|
const match = result.data.match(regex);
|
||||||
|
const list = match && match[1] ? JSON.parse(match[1]) : [];
|
||||||
return {
|
return {
|
||||||
fromCache: result.fromCache,
|
fromCache: result.fromCache,
|
||||||
updateTime: result.updateTime,
|
updateTime: result.updateTime,
|
||||||
@@ -63,14 +41,16 @@ const getList = async (options: Options, noCache: boolean) => {
|
|||||||
const contentBuilder = [];
|
const contentBuilder = [];
|
||||||
const { NEW_DID, LOCATION_C, M } = v;
|
const { NEW_DID, LOCATION_C, M } = v;
|
||||||
for (const mappingsKey in mappings) {
|
for (const mappingsKey in mappings) {
|
||||||
contentBuilder.push(`${mappings[mappingsKey]}:${v[mappingsKey]}`);
|
contentBuilder.push(
|
||||||
|
`${mappings[mappingsKey as keyof typeof mappings]}:${v[mappingsKey as keyof typeof v]}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
id: NEW_DID,
|
id: NEW_DID,
|
||||||
title: `${LOCATION_C}发生${M}级地震`,
|
title: `${LOCATION_C}发生${M}级地震`,
|
||||||
desc: contentBuilder.join("\n"),
|
desc: contentBuilder.join("\n"),
|
||||||
timestamp: getTime(v["O_TIME"]),
|
timestamp: getTime(v["O_TIME" as keyof typeof v]),
|
||||||
hot: null,
|
hot: undefined,
|
||||||
url: `https://news.ceic.ac.cn/${NEW_DID}.html`,
|
url: `https://news.ceic.ac.cn/${NEW_DID}.html`,
|
||||||
mobileUrl: `https://news.ceic.ac.cn/${NEW_DID}.html`,
|
mobileUrl: `https://news.ceic.ac.cn/${NEW_DID}.html`,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
|
|||||||
};
|
};
|
||||||
const getList = async (options: Options, noCache: boolean) => {
|
const getList = async (options: Options, noCache: boolean) => {
|
||||||
const { month, day } = options;
|
const { month, day } = options;
|
||||||
const monthStr = month.toString().padStart(2, "0");
|
const monthStr = month?.toString().padStart(2, "0");
|
||||||
const dayStr = day.toString().padStart(2, "0");
|
const dayStr = day?.toString().padStart(2, "0");
|
||||||
const url = `https://baike.baidu.com/cms/home/eventsOnHistory/${monthStr}.json`;
|
const url = `https://baike.baidu.com/cms/home/eventsOnHistory/${monthStr}.json`;
|
||||||
const result = await get({
|
const result = await get({
|
||||||
url,
|
url,
|
||||||
@@ -37,7 +37,7 @@ const getList = async (options: Options, noCache: boolean) => {
|
|||||||
_: new Date().getTime(),
|
_: new Date().getTime(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const list = result.data[monthStr][monthStr + dayStr];
|
const list = monthStr ? result.data[monthStr][monthStr + dayStr] : [];
|
||||||
return {
|
return {
|
||||||
fromCache: result.fromCache,
|
fromCache: result.fromCache,
|
||||||
updateTime: result.updateTime,
|
updateTime: result.updateTime,
|
||||||
|
|||||||
@@ -1,25 +1,26 @@
|
|||||||
import type { RouterData, ListContext, Options } from "../types.js";
|
import type { RouterData, ListContext, Options } from "../types.js";
|
||||||
import type { RouterType } from "../router.types.js";
|
|
||||||
import { web } from "../utils/getData.js";
|
import { web } from "../utils/getData.js";
|
||||||
import { extractRss, parseRSS } from "../utils/parseRSS.js";
|
import { extractRss, parseRSS } from "../utils/parseRSS.js";
|
||||||
import { getTime } from "../utils/getTime.js";
|
import { getTime } from "../utils/getTime.js";
|
||||||
|
|
||||||
|
const typeMap: Record<string, string> = {
|
||||||
|
hot: "最新热门",
|
||||||
|
digest: "最新精华",
|
||||||
|
new: "最新回复",
|
||||||
|
newthread: "最新发表",
|
||||||
|
};
|
||||||
|
|
||||||
export const handleRoute = async (c: ListContext, noCache: boolean) => {
|
export const handleRoute = async (c: ListContext, noCache: boolean) => {
|
||||||
const type = c.req.query("type") || "hot";
|
const type = c.req.query("type") || "hot";
|
||||||
const { fromCache, data, updateTime } = await getList({ type }, noCache);
|
const { fromCache, data, updateTime } = await getList({ type }, noCache);
|
||||||
const routeData: RouterData = {
|
const routeData: RouterData = {
|
||||||
name: "hostloc",
|
name: "hostloc",
|
||||||
title: "全球主机交流",
|
title: "全球主机交流",
|
||||||
type: "榜单",
|
type: typeMap[type],
|
||||||
params: {
|
params: {
|
||||||
type: {
|
type: {
|
||||||
name: "榜单分类",
|
name: "榜单分类",
|
||||||
type: {
|
type: typeMap,
|
||||||
hot: "最新热门",
|
|
||||||
digest: "最新精华",
|
|
||||||
new: "最新回复",
|
|
||||||
newthread: "最新发表",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
link: "https://hostloc.com/",
|
link: "https://hostloc.com/",
|
||||||
@@ -41,26 +42,24 @@ const getList = async (options: Options, noCache: boolean) => {
|
|||||||
"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Mobile Safari/537.36",
|
"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Mobile Safari/537.36",
|
||||||
});
|
});
|
||||||
const parseData = async () => {
|
const parseData = async () => {
|
||||||
if (typeof result?.data === "string") {
|
if (typeof result?.data !== "string") return [];
|
||||||
const rssContent = extractRss(result.data);
|
const rssContent = extractRss(result.data);
|
||||||
return await parseRSS(rssContent);
|
if (!rssContent) return [];
|
||||||
} else {
|
return await parseRSS(rssContent);
|
||||||
return [];
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
const list = await parseData();
|
const list = await parseData();
|
||||||
return {
|
return {
|
||||||
fromCache: result.fromCache,
|
fromCache: result.fromCache,
|
||||||
updateTime: result.updateTime,
|
updateTime: result.updateTime,
|
||||||
data: list.map((v: RouterType["discuz"]) => ({
|
data: list.map((v, i) => ({
|
||||||
id: v.guid,
|
id: v.guid || i,
|
||||||
title: v.title,
|
title: v.title || "",
|
||||||
desc: v.content,
|
desc: v.content || "",
|
||||||
author: v.author,
|
author: v.author || "",
|
||||||
timestamp: getTime(v.pubDate),
|
timestamp: getTime(v.pubDate || 0),
|
||||||
hot: null,
|
hot: undefined,
|
||||||
url: v.link,
|
url: v.link || "",
|
||||||
mobileUrl: v.link,
|
mobileUrl: v.link || "",
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export const handleRoute = async (_: undefined, noCache: boolean) => {
|
|||||||
// 标题处理
|
// 标题处理
|
||||||
const titleProcessing = (text: string) => {
|
const titleProcessing = (text: string) => {
|
||||||
const paragraphs = text.split("<br><br>");
|
const paragraphs = text.split("<br><br>");
|
||||||
const title = paragraphs.shift().replace(/。$/, "");
|
const title = paragraphs.shift()?.replace(/。$/, "");
|
||||||
const intro = paragraphs.join("<br><br>");
|
const intro = paragraphs.join("<br><br>");
|
||||||
return { title, intro };
|
return { title, intro };
|
||||||
};
|
};
|
||||||
@@ -47,8 +47,8 @@ const getList = async (noCache: boolean) => {
|
|||||||
author: v.user_info.username,
|
author: v.user_info.username,
|
||||||
timestamp: getTime(v.publish_time),
|
timestamp: getTime(v.publish_time),
|
||||||
hot: null,
|
hot: null,
|
||||||
url: v.url || "https://www.huxiu.com/moment/",
|
url: v.url || `https://www.huxiu.com/moment/${v.object_id}.html`,
|
||||||
mobileUrl: v.url || "https://m.huxiu.com/moment/",
|
mobileUrl: v.url || `https://m.huxiu.com/moment/${v.object_id}.html`,
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ const getList = async (noCache: boolean) => {
|
|||||||
desc: v.post_content,
|
desc: v.post_content,
|
||||||
timestamp: getTime(v.created_at),
|
timestamp: getTime(v.created_at),
|
||||||
hot: v.like_count || v.comment_count,
|
hot: v.like_count || v.comment_count,
|
||||||
url: `https://www.ifanr.com/${v.id}` || v.buzz_original_url,
|
url: v.buzz_original_url || `https://www.ifanr.com/${v.id}`,
|
||||||
mobileUrl: `https://www.ifanr.com/digest/${v.id}` || v.buzz_original_url,
|
mobileUrl: v.buzz_original_url || `https://www.ifanr.com/digest/${v.id}`,
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -44,10 +44,10 @@ const getList = async (noCache: boolean) => {
|
|||||||
title: dom.find(".newsbody h2").text().trim(),
|
title: dom.find(".newsbody h2").text().trim(),
|
||||||
desc: dom.find(".newsbody p").text().trim(),
|
desc: dom.find(".newsbody p").text().trim(),
|
||||||
cover: dom.find("img").attr("data-original"),
|
cover: dom.find("img").attr("data-original"),
|
||||||
timestamp: getTime(dateTime),
|
timestamp: getTime(dateTime || 0),
|
||||||
hot: Number(dom.find(".comment").text().replace(/\D/g, "")),
|
hot: Number(dom.find(".comment").text().replace(/\D/g, "")),
|
||||||
url: href || undefined,
|
url: href || "",
|
||||||
mobileUrl: href ? replaceLink(href) : undefined,
|
mobileUrl: href ? replaceLink(href) : "",
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { RouterData } from "../types.js";
|
import type { RouterData } from "../types.js";
|
||||||
import { load } from "cheerio";
|
import { load } from "cheerio";
|
||||||
import { get } from "../utils/getData.js";
|
import { get } from "../utils/getData.js";
|
||||||
|
import { getTime } from "../utils/getTime.js";
|
||||||
|
|
||||||
export const handleRoute = async (_: undefined, noCache: boolean) => {
|
export const handleRoute = async (_: undefined, noCache: boolean) => {
|
||||||
const { fromCache, data, updateTime } = await getList(noCache);
|
const { fromCache, data, updateTime } = await getList(noCache);
|
||||||
@@ -43,10 +44,10 @@ const getList = async (noCache: boolean) => {
|
|||||||
id: href ? Number(replaceLink(href, true)) : 100000,
|
id: href ? Number(replaceLink(href, true)) : 100000,
|
||||||
title: dom.find(".plc-title").text().trim(),
|
title: dom.find(".plc-title").text().trim(),
|
||||||
cover: dom.find("img").attr("data-original"),
|
cover: dom.find("img").attr("data-original"),
|
||||||
timestamp: dom.find("span.post-time").text().trim(),
|
timestamp: getTime(dom.find("span.post-time").text().trim()),
|
||||||
hot: Number(dom.find(".review-num").text().replace(/\D/g, "")),
|
hot: Number(dom.find(".review-num").text().replace(/\D/g, "")),
|
||||||
url: href ? replaceLink(href) : undefined,
|
url: href ? replaceLink(href) : "",
|
||||||
mobileUrl: href || undefined,
|
mobileUrl: href ? replaceLink(href) : "",
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ const getList = async (noCache: boolean) => {
|
|||||||
cover: dom.find("img").attr("src"),
|
cover: dom.find("img").attr("src"),
|
||||||
desc: dom.find("p.abstract").text()?.trim(),
|
desc: dom.find("p.abstract").text()?.trim(),
|
||||||
author: dom.find("a.nickname").text()?.trim(),
|
author: dom.find("a.nickname").text()?.trim(),
|
||||||
hot: null,
|
hot: undefined,
|
||||||
timestamp: null,
|
timestamp: undefined,
|
||||||
url: `https://www.jianshu.com${href}`,
|
url: `https://www.jianshu.com${href}`,
|
||||||
mobileUrl: `https://www.jianshu.com${href}`,
|
mobileUrl: `https://www.jianshu.com${href}`,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const getList = async (noCache: boolean) => {
|
|||||||
title: v.content.title,
|
title: v.content.title,
|
||||||
author: v.author.name,
|
author: v.author.name,
|
||||||
hot: v.content_counter.hot_rank,
|
hot: v.content_counter.hot_rank,
|
||||||
timestamp: null,
|
timestamp: undefined,
|
||||||
url: `https://juejin.cn/post/${v.content.content_id}`,
|
url: `https://juejin.cn/post/${v.content.content_id}`,
|
||||||
mobileUrl: `https://juejin.cn/post/${v.content.content_id}`,
|
mobileUrl: `https://juejin.cn/post/${v.content.content_id}`,
|
||||||
})),
|
})),
|
||||||
|
|||||||
75
src/routes/miyoushe.ts
Normal file
75
src/routes/miyoushe.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import type { RouterData, ListContext, Options, RouterResType } from "../types.js";
|
||||||
|
import type { RouterType } from "../router.types.js";
|
||||||
|
import { get } from "../utils/getData.js";
|
||||||
|
import { getTime } from "../utils/getTime.js";
|
||||||
|
|
||||||
|
// 游戏分类
|
||||||
|
const gameMap: Record<string, string> = {
|
||||||
|
"1": "崩坏3",
|
||||||
|
"2": "原神",
|
||||||
|
"3": "崩坏学园2",
|
||||||
|
"4": "未定事件簿",
|
||||||
|
"5": "大别野",
|
||||||
|
"6": "崩坏:星穹铁道",
|
||||||
|
"7": "暂无",
|
||||||
|
"8": "绝区零",
|
||||||
|
};
|
||||||
|
|
||||||
|
// 榜单分类
|
||||||
|
const typeMap: Record<string, string> = {
|
||||||
|
"1": "公告",
|
||||||
|
"2": "活动",
|
||||||
|
"3": "资讯",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const handleRoute = async (c: ListContext, noCache: boolean) => {
|
||||||
|
const game = c.req.query("game") || "1";
|
||||||
|
const type = c.req.query("type") || "1";
|
||||||
|
const { fromCache, data, updateTime } = await getList({ game, type }, noCache);
|
||||||
|
const routeData: RouterData = {
|
||||||
|
name: "miyoushe",
|
||||||
|
title: `米游社 · ${gameMap[game]}`,
|
||||||
|
type: `最新${typeMap[type]}`,
|
||||||
|
params: {
|
||||||
|
game: {
|
||||||
|
name: "游戏分类",
|
||||||
|
type: gameMap,
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
name: "榜单分类",
|
||||||
|
type: typeMap,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
link: "https://www.miyoushe.com/",
|
||||||
|
total: data?.length || 0,
|
||||||
|
updateTime,
|
||||||
|
fromCache,
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
return routeData;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getList = async (options: Options, noCache: boolean): Promise<RouterResType> => {
|
||||||
|
const { game, type } = options;
|
||||||
|
const url = `https://bbs-api-static.miyoushe.com/painter/wapi/getNewsList?client_type=4&gids=${game}&last_id=&page_size=30&type=${type}`;
|
||||||
|
const result = await get({ url, noCache });
|
||||||
|
const list = result.data.data.list;
|
||||||
|
return {
|
||||||
|
fromCache: result.fromCache,
|
||||||
|
updateTime: result.updateTime,
|
||||||
|
data: list.map((v: RouterType["miyoushe"]) => {
|
||||||
|
const data = v.post;
|
||||||
|
return {
|
||||||
|
id: data.post_id,
|
||||||
|
title: data.subject,
|
||||||
|
desc: data.content,
|
||||||
|
cover: data.cover || data?.images?.[0],
|
||||||
|
author: v.user?.nickname || null,
|
||||||
|
timestamp: getTime(data.created_at),
|
||||||
|
hot: data.view_status || 0,
|
||||||
|
url: `https://www.miyoushe.com/ys/article/${data.post_id}`,
|
||||||
|
mobileUrl: `https://m.miyoushe.com/ys/#/article/${data.post_id}`,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import type { RouterData } from "../types.js";
|
import type { RouterData } from "../types.js";
|
||||||
|
import type { RouterType } from "../router.types.js";
|
||||||
import { get } from "../utils/getData.js";
|
import { get } from "../utils/getData.js";
|
||||||
import { parseStringPromise } from "xml2js";
|
import { parseStringPromise } from "xml2js";
|
||||||
|
import { getTime } from "../utils/getTime.js";
|
||||||
|
|
||||||
export const handleRoute = async (_: undefined, noCache: boolean) => {
|
export const handleRoute = async (_: undefined, noCache: boolean) => {
|
||||||
const { fromCache, data, updateTime } = await getList(noCache);
|
const { fromCache, data, updateTime } = await getList(noCache);
|
||||||
@@ -33,13 +35,13 @@ const getList = async (noCache: boolean) => {
|
|||||||
return {
|
return {
|
||||||
fromCache: result.fromCache,
|
fromCache: result.fromCache,
|
||||||
updateTime: result.updateTime,
|
updateTime: result.updateTime,
|
||||||
data: list.map((v) => ({
|
data: list.map((v: RouterType["nodeseek"]) => ({
|
||||||
id: v.guid[0]._,
|
id: v.guid[0]._,
|
||||||
title: v.title[0],
|
title: v.title[0],
|
||||||
desc: v.description ? v.description[0] : "",
|
desc: v.description ? v.description[0] : "",
|
||||||
author: v["dc:creator"] ? v["dc:creator"][0] : "unknown",
|
author: v["dc:creator"] ? v["dc:creator"][0] : "unknown",
|
||||||
timestamp: new Date(v.pubDate[0]).getTime(),
|
timestamp: getTime(v.pubDate[0]),
|
||||||
hot: null, // NodeSeek RSS 中没有类似于hot的字段
|
hot: undefined,
|
||||||
url: v.link[0],
|
url: v.link[0],
|
||||||
mobileUrl: v.link[0],
|
mobileUrl: v.link[0],
|
||||||
})),
|
})),
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
|
|||||||
const routeData: RouterData = {
|
const routeData: RouterData = {
|
||||||
name: "sina-news",
|
name: "sina-news",
|
||||||
title: "新浪新闻",
|
title: "新浪新闻",
|
||||||
type: listType[type].name,
|
type: listType[type as keyof typeof listType].name,
|
||||||
params: {
|
params: {
|
||||||
type: {
|
type: {
|
||||||
name: "榜单分类",
|
name: "榜单分类",
|
||||||
@@ -106,7 +106,7 @@ const parseData = (data: string) => {
|
|||||||
const jsonData = JSON.parse(jsonString);
|
const jsonData = JSON.parse(jsonString);
|
||||||
return jsonData;
|
return jsonData;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error("Failed to parse JSON: " + error.message);
|
throw new Error("Failed to parse JSON: " + error);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Invalid JSON format");
|
throw new Error("Invalid JSON format");
|
||||||
@@ -116,7 +116,7 @@ const parseData = (data: string) => {
|
|||||||
const getList = async (options: Options, noCache: boolean) => {
|
const getList = async (options: Options, noCache: boolean) => {
|
||||||
const { type } = options;
|
const { type } = options;
|
||||||
// 必要数据
|
// 必要数据
|
||||||
const { params, www } = listType[type];
|
const { params, www } = listType[type as keyof typeof listType];
|
||||||
const { year, month, day } = getCurrentDateTime(true);
|
const { year, month, day } = getCurrentDateTime(true);
|
||||||
const url = `https://top.${www}.sina.com.cn/ws/GetTopDataList.php?top_type=day&top_cat=${params}&top_time=${year + month + day}&top_show_num=50`;
|
const url = `https://top.${www}.sina.com.cn/ws/GetTopDataList.php?top_type=day&top_cat=${params}&top_time=${year + month + day}&top_show_num=50`;
|
||||||
const result = await get({ url, noCache });
|
const result = await get({ url, noCache });
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
|
|||||||
|
|
||||||
const getList = async (options: Options, noCache: boolean) => {
|
const getList = async (options: Options, noCache: boolean) => {
|
||||||
const { province } = options;
|
const { province } = options;
|
||||||
const url = `http://www.nmc.cn/rest/findAlarm?pageNo=1&pageSize=20&signaltype=&signallevel=&province=${encodeURIComponent(province)}`;
|
const url = `http://www.nmc.cn/rest/findAlarm?pageNo=1&pageSize=20&signaltype=&signallevel=&province=${encodeURIComponent(province || "")}`;
|
||||||
const result = await get({ url, noCache });
|
const result = await get({ url, noCache });
|
||||||
const list = result.data.data.page.list;
|
const list = result.data.data.page.list;
|
||||||
return {
|
return {
|
||||||
|
|||||||
42
src/types.d.ts
vendored
42
src/types.d.ts
vendored
@@ -4,20 +4,27 @@ import type { Context } from "hono";
|
|||||||
export type ListContext = Context;
|
export type ListContext = Context;
|
||||||
|
|
||||||
// 榜单数据
|
// 榜单数据
|
||||||
export type ListItem = {
|
export interface ListItem {
|
||||||
id: number | string;
|
id: number | string;
|
||||||
title: string;
|
title: string;
|
||||||
cover?: string;
|
cover?: string;
|
||||||
author?: string;
|
author?: string;
|
||||||
desc?: string;
|
desc?: string;
|
||||||
hot: number | null;
|
hot: number | undefined;
|
||||||
timestamp: number | string | null;
|
timestamp: number | undefined;
|
||||||
url: string | undefined;
|
url: string;
|
||||||
mobileUrl: string | undefined;
|
mobileUrl: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
// 路由接口数据
|
||||||
|
export interface RouterResType {
|
||||||
|
updateTime: string;
|
||||||
|
fromCache: boolean;
|
||||||
|
data: ListItem[];
|
||||||
|
}
|
||||||
|
|
||||||
// 路由数据
|
// 路由数据
|
||||||
export type RouterData = {
|
export interface RouterData extends RouterResType {
|
||||||
name: string;
|
name: string;
|
||||||
title: string;
|
title: string;
|
||||||
type: string;
|
type: string;
|
||||||
@@ -25,13 +32,10 @@ export type RouterData = {
|
|||||||
params?: Record<string, string | object>;
|
params?: Record<string, string | object>;
|
||||||
total: number;
|
total: number;
|
||||||
link?: string;
|
link?: string;
|
||||||
updateTime: string;
|
}
|
||||||
fromCache: boolean;
|
|
||||||
data: ListItem[];
|
|
||||||
};
|
|
||||||
|
|
||||||
// 请求类型
|
// 请求类型
|
||||||
export type Get = {
|
export interface Get {
|
||||||
url: string;
|
url: string;
|
||||||
headers?: Record<string, string | string[]>;
|
headers?: Record<string, string | string[]>;
|
||||||
params?: Record<string, string | number>;
|
params?: Record<string, string | number>;
|
||||||
@@ -39,9 +43,9 @@ export type Get = {
|
|||||||
noCache?: boolean;
|
noCache?: boolean;
|
||||||
ttl?: number;
|
ttl?: number;
|
||||||
originaInfo?: boolean;
|
originaInfo?: boolean;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type Post = {
|
export interface Post {
|
||||||
url: string;
|
url: string;
|
||||||
headers?: Record<string, string | string[]>;
|
headers?: Record<string, string | string[]>;
|
||||||
body?: string | object | Buffer | undefined;
|
body?: string | object | Buffer | undefined;
|
||||||
@@ -49,17 +53,17 @@ export type Post = {
|
|||||||
noCache?: boolean;
|
noCache?: boolean;
|
||||||
ttl?: number;
|
ttl?: number;
|
||||||
originaInfo?: boolean;
|
originaInfo?: boolean;
|
||||||
};
|
}
|
||||||
|
|
||||||
export type Web = {
|
export interface Web {
|
||||||
url: string;
|
url: string;
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
noCache?: boolean;
|
noCache?: boolean;
|
||||||
ttl?: number;
|
ttl?: number;
|
||||||
userAgent?: string;
|
userAgent?: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
// 参数类型
|
// 参数类型
|
||||||
export type Options = {
|
export interface Options {
|
||||||
[key: string]: string | number | undefined;
|
[key: string]: string | number | undefined;
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { Get, Post, Web } from "../types.ts";
|
import type { Get, Post, Web } from "../types.ts";
|
||||||
import { config } from "../config.js";
|
import { config } from "../config.js";
|
||||||
import { getCache, setCache, delCache } from "./cache.js";
|
import { getCache, setCache, delCache } from "./cache.js";
|
||||||
// import { Cluster } from "puppeteer-cluster";
|
import { Cluster } from "puppeteer-cluster";
|
||||||
import logger from "./logger.js";
|
import logger from "./logger.js";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
@@ -13,26 +13,41 @@ const request = axios.create({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// puppeteer-cluster
|
// puppeteer-cluster
|
||||||
// export const createCluster = async () => {
|
export const createCluster = async () => {
|
||||||
// return await Cluster.launch({
|
return await Cluster.launch({
|
||||||
// concurrency: Cluster.CONCURRENCY_BROWSER,
|
concurrency: Cluster.CONCURRENCY_BROWSER,
|
||||||
// maxConcurrency: 5,
|
maxConcurrency: 5,
|
||||||
// });
|
// puppeteer
|
||||||
// };
|
puppeteerOptions: {
|
||||||
|
headless: true,
|
||||||
|
args: ["--no-sandbox", "--disable-setuid-sandbox"],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// Cluster
|
// Cluster
|
||||||
// const cluster = await createCluster();
|
const cluster = await createCluster();
|
||||||
const cluster = null;
|
|
||||||
|
|
||||||
// Cluster configuration
|
// Cluster configuration
|
||||||
// cluster.task(async ({ page, data: { url, userAgent } }) => {
|
cluster.task(async ({ page, data: { url, userAgent } }) => {
|
||||||
// if (userAgent) {
|
// 用户代理
|
||||||
// await page.setUserAgent(userAgent);
|
if (userAgent) await page.setUserAgent(userAgent);
|
||||||
// }
|
// 请求拦截
|
||||||
// await page.goto(url, { waitUntil: "networkidle0" });
|
await page.setRequestInterception(true);
|
||||||
// const pageContent = await page.content();
|
// 拦截非必要资源
|
||||||
// return pageContent;
|
page.on("request", (request) => {
|
||||||
// });
|
const type = request.resourceType();
|
||||||
|
if (type === "document" || type === "script") {
|
||||||
|
request.continue();
|
||||||
|
} else {
|
||||||
|
request.abort();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 加载页面
|
||||||
|
await page.goto(url, { waitUntil: "networkidle0", timeout: config.REQUEST_TIMEOUT });
|
||||||
|
const pageContent = await page.content();
|
||||||
|
return pageContent;
|
||||||
|
});
|
||||||
|
|
||||||
// 请求拦截
|
// 请求拦截
|
||||||
request.interceptors.request.use(
|
request.interceptors.request.use(
|
||||||
@@ -127,6 +142,7 @@ export const web = async (options: Web) => {
|
|||||||
const { url, noCache, ttl = config.CACHE_TTL, userAgent } = options;
|
const { url, noCache, ttl = config.CACHE_TTL, userAgent } = options;
|
||||||
logger.info("使用 Puppeteer 发起页面请求", options);
|
logger.info("使用 Puppeteer 发起页面请求", options);
|
||||||
try {
|
try {
|
||||||
|
if (!cluster) throw new Error("Cluster is not initialized");
|
||||||
// 检查缓存
|
// 检查缓存
|
||||||
if (noCache) {
|
if (noCache) {
|
||||||
delCache(url);
|
delCache(url);
|
||||||
|
|||||||
@@ -8,7 +8,13 @@ interface CurrentDateTime {
|
|||||||
minute: string;
|
minute: string;
|
||||||
second: string;
|
second: string;
|
||||||
}
|
}
|
||||||
export const getTime = (timeInput: string | number): number | null => {
|
|
||||||
|
/**
|
||||||
|
* 将时间字符串或数字转换为时间戳
|
||||||
|
* @param timeInput 时间字符串或数字
|
||||||
|
* @returns 时间戳
|
||||||
|
*/
|
||||||
|
export const getTime = (timeInput: string | number): number | undefined => {
|
||||||
try {
|
try {
|
||||||
let num: number;
|
let num: number;
|
||||||
|
|
||||||
@@ -18,7 +24,78 @@ export const getTime = (timeInput: string | number): number | null => {
|
|||||||
num = Number(timeInput);
|
num = Number(timeInput);
|
||||||
|
|
||||||
if (isNaN(num)) {
|
if (isNaN(num)) {
|
||||||
// 将各种分隔符替换为标准格式
|
const now = dayjs();
|
||||||
|
|
||||||
|
// 处理 "00:00"
|
||||||
|
if (/^\d{2}:\d{2}$/.test(timeInput)) {
|
||||||
|
const [hour, minute] = timeInput.split(":").map(Number);
|
||||||
|
return now.set("hour", hour).set("minute", minute).set("second", 0).valueOf();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理 昨天的时间
|
||||||
|
if (/^昨日\s+\d{2}:\d{2}$/.test(timeInput)) {
|
||||||
|
const timeStr = timeInput.replace("昨日", "").trim();
|
||||||
|
const [hour, minute] = timeStr.split(":").map(Number);
|
||||||
|
return now
|
||||||
|
.subtract(1, "day")
|
||||||
|
.set("hour", hour)
|
||||||
|
.set("minute", minute)
|
||||||
|
.set("second", 0)
|
||||||
|
.valueOf();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理 今年的日期
|
||||||
|
if (/^\d{1,2}月\d{1,2}日$/.test(timeInput)) {
|
||||||
|
const [month, day] = timeInput
|
||||||
|
.replace("月", "-")
|
||||||
|
.replace("日", "")
|
||||||
|
.split("-")
|
||||||
|
.map(Number);
|
||||||
|
return now
|
||||||
|
.set("month", month - 1)
|
||||||
|
.set("date", day)
|
||||||
|
.startOf("day")
|
||||||
|
.valueOf();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理 今年的日期+时间
|
||||||
|
if (/^\d{1,2}月\d{1,2}日\s+\d{2}:\d{2}$/.test(timeInput)) {
|
||||||
|
const [datePart, timePart] = timeInput.split(" ");
|
||||||
|
const [month, day] = datePart.replace("月", "-").replace("日", "").split("-").map(Number);
|
||||||
|
const [hour, minute] = timePart.split(":").map(Number);
|
||||||
|
return now
|
||||||
|
.set("month", month - 1)
|
||||||
|
.set("date", day)
|
||||||
|
.set("hour", hour)
|
||||||
|
.set("minute", minute)
|
||||||
|
.set("second", 0)
|
||||||
|
.valueOf();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理相对时间
|
||||||
|
if (/今天/.test(timeInput)) {
|
||||||
|
const timeStr = timeInput.replace("今天", "").trim();
|
||||||
|
return dayjs()
|
||||||
|
.set("hour", parseInt(timeStr.split(":")[0]))
|
||||||
|
.set("minute", parseInt(timeStr.split(":")[1]))
|
||||||
|
.valueOf();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/昨天/.test(timeInput)) {
|
||||||
|
const timeStr = timeInput.replace("昨天", "").trim();
|
||||||
|
return dayjs()
|
||||||
|
.subtract(1, "day")
|
||||||
|
.set("hour", parseInt(timeStr.split(":")[0]))
|
||||||
|
.set("minute", parseInt(timeStr.split(":")[1]))
|
||||||
|
.valueOf();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/分钟前/.test(timeInput)) {
|
||||||
|
const minutesAgo = parseInt(timeInput.replace("分钟前", ""));
|
||||||
|
return dayjs().subtract(minutesAgo, "minute").valueOf();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理为标准格式
|
||||||
let standardizedInput = timeInput
|
let standardizedInput = timeInput
|
||||||
.replace(/(\d{4})-(\d{2})-(\d{2})-(\d{2})/, "$1-$2-$3 $4") // "YYYY-MM-DD-HH" -> "YYYY-MM-DD HH"
|
.replace(/(\d{4})-(\d{2})-(\d{2})-(\d{2})/, "$1-$2-$3 $4") // "YYYY-MM-DD-HH" -> "YYYY-MM-DD HH"
|
||||||
.replace(/(\d{4})-(\d{2})-(\d{2})[T\s](\d{2}):?(\d{2})?:?(\d{2})?/, "$1-$2-$3 $4:$5:$6") // "YYYY-MM-DDTHH:mm:ss" -> "YYYY-MM-DD HH:mm:ss"
|
.replace(/(\d{4})-(\d{2})-(\d{2})[T\s](\d{2}):?(\d{2})?:?(\d{2})?/, "$1-$2-$3 $4:$5:$6") // "YYYY-MM-DDTHH:mm:ss" -> "YYYY-MM-DD HH:mm:ss"
|
||||||
@@ -46,7 +123,7 @@ export const getTime = (timeInput: string | number): number | null => {
|
|||||||
if (parsedDate && parsedDate.isValid()) {
|
if (parsedDate && parsedDate.isValid()) {
|
||||||
return parsedDate.valueOf();
|
return parsedDate.valueOf();
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -61,10 +138,15 @@ export const getTime = (timeInput: string | number): number | null => {
|
|||||||
return num * 1000;
|
return num * 1000;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return null;
|
console.error(error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前日期时间
|
||||||
|
* @param padZero 是否补零
|
||||||
|
* @returns 当前日期时间
|
||||||
|
*/
|
||||||
export const getCurrentDateTime = (padZero: boolean = false): CurrentDateTime => {
|
export const getCurrentDateTime = (padZero: boolean = false): CurrentDateTime => {
|
||||||
const now = dayjs();
|
const now = dayjs();
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,23 @@
|
|||||||
import RSSParser from "rss-parser";
|
import RSSParser from "rss-parser";
|
||||||
import logger from "./logger.js";
|
import logger from "./logger.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提取 RSS 内容
|
||||||
|
* @param content HTML 内容
|
||||||
|
* @returns RSS 内容
|
||||||
|
*/
|
||||||
export const extractRss = (content: string): string | null => {
|
export const extractRss = (content: string): string | null => {
|
||||||
// 匹配 <rss> 标签及内容
|
// 匹配 <rss> 标签及内容
|
||||||
const rssRegex = /(<rss[\s\S]*?<\/rss>)/i;
|
const rssRegex = /(<rss[\s\S]*?<\/rss>)/i;
|
||||||
const matches = content.match(rssRegex);
|
const matches = content.match(rssRegex);
|
||||||
return matches ? matches[0] : null;
|
return matches ? matches[0] : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析 RSS 内容
|
||||||
|
* @param rssContent RSS 内容
|
||||||
|
* @returns 解析后的 RSS 内容
|
||||||
|
*/
|
||||||
export const parseRSS = async (rssContent: string) => {
|
export const parseRSS = async (rssContent: string) => {
|
||||||
const parser = new RSSParser();
|
const parser = new RSSParser();
|
||||||
// 是否为网址
|
// 是否为网址
|
||||||
@@ -14,7 +25,8 @@ export const parseRSS = async (rssContent: string) => {
|
|||||||
try {
|
try {
|
||||||
new URL(url);
|
new URL(url);
|
||||||
return true;
|
return true;
|
||||||
} catch (_) {
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -36,6 +48,6 @@ export const parseRSS = async (rssContent: string) => {
|
|||||||
return items;
|
return items;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("解析 RSS 内容时出错:", error);
|
logger.error("解析 RSS 内容时出错:", error);
|
||||||
return null;
|
return [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"strict": true,
|
||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "Bundler",
|
"moduleResolution": "Bundler",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"declarationMap": true,
|
"declarationMap": true,
|
||||||
"strict": false,
|
|
||||||
"types": ["node"],
|
"types": ["node"],
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"jsxImportSource": "hono/jsx",
|
"jsxImportSource": "hono/jsx",
|
||||||
|
|||||||
Reference in New Issue
Block a user