fix(route): 迁移看雪路由 (#15064)

* 迁移看雪路由

* 迁移看雪路由

* Update lib/routes/kanxue/topic.ts

Co-authored-by: Tony <TonyRL@users.noreply.github.com>

* 迁移看雪路由

---------
This commit is contained in:
zcw159357
2024-04-05 19:27:42 +08:00
committed by GitHub
parent 889c6d5fa8
commit f78ee86792
5 changed files with 160 additions and 145 deletions
+1 -1
View File
@@ -330,7 +330,7 @@ router.get('/qutoutiao/category/:cid', lazyloadRouteHandler('./routes/qutoutiao/
// router.get('/bbc/:site?/:channel?', lazyloadRouteHandler('./routes/bbc/index'));
// 看雪
router.get('/pediy/topic/:category?/:type?', lazyloadRouteHandler('./routes/pediy/topic'));
// router.get('/pediy/topic/:category?/:type?', lazyloadRouteHandler('./routes/pediy/topic'));
// 老司机
router.get('/laosiji/hot', lazyloadRouteHandler('./routes/laosiji/hot'));
-117
View File
@@ -1,117 +0,0 @@
const got = require('@/utils/got');
const pediyUtils = require('./utils');
const cheerio = require('cheerio');
const baseUrl = 'https://bbs.pediy.com/';
const categoryId = {
iot: [128, '智能设备'],
blockchain: [172, '区块链安全'],
android: [161, 'Android安全'],
ios: [166, 'iOS安全'],
re: [4, '软件逆向'],
coding: [41, '编程技术'],
unpack: [88, '加壳脱壳'],
crypto: [132, '密码算法'],
vuln: [150, '二进制漏洞'],
crackme: [37, 'CrackMe'],
pwn: [171, 'Pwn'],
web: [151, 'WEB安全'],
translate: [32, '外文翻译'],
};
module.exports = async (ctx) => {
const category = ctx.params.category || 'all';
const type = ctx.params.type || 'latest';
let path;
let title;
if (Object.hasOwn(categoryId, category)) {
if (type === 'digest') {
// type为digest时只获取精华帖
path = `forum-${categoryId[category][0]}-1.htm?digest=1`;
title = `看雪论坛精华主题 - ${categoryId[category][1]}`;
} else {
// type为空/非法/latest时则只获取最新帖
path = `forum-${categoryId[category][0]}.html`;
title = `看雪论坛最新主题 - ${categoryId[category][1]}`;
}
} else {
// category未知时则获取全站最新帖
if (category === 'digest') {
path = 'new-digest.htm';
title = '看雪论坛精华主题';
} else {
path = 'new-tid.htm';
title = '看雪论坛最新主题';
}
}
const response = await got({
method: 'get',
url: baseUrl + path,
headers: {
Referer: baseUrl,
},
});
const $ = cheerio.load(response.data);
const list = $('.thread');
const resultItem = await Promise.all(
list
? list
// fix .thread .top_3
.filter((_, elem) => {
const pubDate = pediyUtils.dateParser($('.date', elem).eq(0).text(), 8);
return !elem.attribs.class.includes('top') || Date.now() - new Date(pubDate) < 1000 * 60 * 60 * 24 * 3;
})
.map(async (_, elem) => {
const subject = $('.subject a', elem).eq(1);
const pubDate = pediyUtils.dateParser($('.date', elem).eq(0).text(), 8);
const link = `${baseUrl}${subject.attr('href')}`;
const key = `pediy: ${link}`;
const value = await ctx.cache.get(key);
let description;
if (value) {
description = value;
} else {
const postDetail = await got({
method: 'get',
url: link,
});
const $ = cheerio.load(postDetail.data);
$('.card')
.eq(0)
.find('.message img')
.each((_, item) => {
item = $(item);
const src = item.attr('src');
if (src !== undefined && !src.startsWith('https://') && !src.startsWith('http://')) {
item.attr('src', `https://bbs.pediy.com/${src}`);
}
item.attr('referrerpolicy', 'no-referrer');
});
description = $('.card').eq(0).find('.message').html();
ctx.cache.set(key, description);
}
return {
title: subject.text(),
link,
pubDate: new Date(pubDate),
description,
};
})
.get()
: []
);
ctx.state.data = {
title,
link: baseUrl + path,
item: resultItem,
};
};
-27
View File
@@ -1,27 +0,0 @@
const pediyUtils = {
dateParser: (html, timeZone) => {
let math;
let date = new Date();
if (/(\d+)分钟前/.test(html)) {
math = /(\d+)分钟前/.exec(html);
date.setMinutes(date.getMinutes() - math[1]);
return date.toUTCString();
} else if (/(\d+)小时前/.test(html)) {
math = /(\d+)小时前/.exec(html);
date.setHours(date.getHours() - math[1]);
return date.toUTCString();
} else if (/(\d+)天前/.test(html)) {
math = /(\d+)天前/.exec(html);
date.setDate(date.getDate() - math[1]);
return date.toUTCString();
} else if (/(\d+)-(\d+)-(\d+) (\d+):(\d+)/.test(html)) {
math = /(\d+)-(\d+)-(\d+) (\d+):(\d+)/.exec(html);
date = new Date(math[1], Number.parseInt(math[2]) - 1, math[3], math[4], math[5]);
const serverOffset = new Date().getTimezoneOffset() / 60;
return new Date(date.getTime() - 60 * 60 * 1000 * (timeZone + serverOffset)).toUTCString();
}
return html;
},
};
module.exports = pediyUtils;
+6
View File
@@ -0,0 +1,6 @@
import type { Namespace } from '@/types';
export const namespace: Namespace = {
name: '看雪',
url: 'kanxue.com',
};
+153
View File
@@ -0,0 +1,153 @@
import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseRelativeDate, parseDate } from '@/utils/parse-date';
const baseUrl = 'https://bbs.kanxue.com/';
const categoryId = {
iot: [128, '智能设备'],
android: [161, 'Android安全'],
ios: [166, 'iOS安全'],
harmonyos: [178, 'HarmonyOS安全'],
re: [4, '软件逆向'],
coding: [41, '编程技术'],
unpack: [88, '加壳脱壳'],
crypto: [132, '密码应用'],
vuln: [150, '二进制漏洞'],
ctf: [37, 'CTF对抗'],
pwn: [171, 'Pwn'],
web: [151, 'WEB安全'],
chat: [45, '茶余饭后'],
geekzone: [172, '极客空间'],
translate: [32, '外文翻译'],
};
export const route: Route = {
path: '/topic/:category?/:type?',
categories: ['bbs'],
example: '/kanxue/topic/android/digest',
parameters: { category: '版块, 缺省为`all`', type: '类型, 缺省为`latest`' },
name: '论坛',
maintainers: ['renzhexigua'],
handler,
description: `| 版块 | category |
| -------------- | --------- |
| 智能设备 | iot |
| Android 安全 | android |
| iOS 安全 | ios |
| HarmonyOS 安全 | harmonyos |
| 软件逆向 | re |
| 编程技术 | coding |
| 加壳脱壳 | unpack |
| 密码应用 | crypto |
| 二进制漏洞 | vuln |
| CTF 对抗 | ctf |
| Pwn | pwn |
| WEB 安全 | web |
| 茶余饭后 | chat |
| 极客空间 | geekzone |
| 外文翻译 | translate |
| 全站 | all |
| 类型 | type |
| -------- | ------ |
| 最新主题 | latest |
| 精华主题 | digest |`,
};
const timeDiff = 1000 * 60 * 60 * 24 * 3;
async function handler(ctx) {
const category = ctx.req.param('category') || 'all';
const type = ctx.req.param('type') || 'latest';
let path;
let title;
if (Object.hasOwn(categoryId, category)) {
if (type === 'digest') {
// type为digest时只获取精华帖
path = `forum-${categoryId[category][0]}-1.htm?digest=1`;
title = `看雪论坛精华主题 - ${categoryId[category][1]}`;
} else {
// type为空/非法/latest时则只获取最新帖
path = `forum-${categoryId[category][0]}.html`;
title = `看雪论坛最新主题 - ${categoryId[category][1]}`;
}
} else {
// category未知时则获取全站最新帖
if (category === 'digest') {
path = 'new-digest.htm';
title = '看雪论坛精华主题';
} else {
path = 'new-tid.htm';
title = '看雪论坛最新主题';
}
}
const response = await got({
method: 'get',
url: baseUrl + path,
headers: {
Referer: baseUrl,
},
});
const $ = load(response.data);
const list = $('.thread');
const resultItem = await Promise.all(
list
? list
// fix .thread .top_3
.filter((_, elem) => {
const timeStr = $('.date', elem).eq(0).text();
const pubDate = timeStr.endsWith("前") ? parseRelativeDate(timeStr) : parseDate(timeStr.substring(1));
return !elem.attribs.class.includes('top') || Date.now() - pubDate.valueOf() < timeDiff;
})
.map((_, elem) => {
const subject = $('.subject a', elem).eq(1);
const timeStr = $('.date', elem).eq(0).text();
const pubDate = timeStr.endsWith("前") ? parseRelativeDate(timeStr) : parseDate(timeStr.substring(1));
const link = `${baseUrl}${subject.attr('href')}`;
const key = `kanxue: ${link}`;
return cache.tryGet(key, async () => {
const postDetail = await got({
method: 'get',
url: link,
});
const $ = load(postDetail.data);
$('.card')
.eq(0)
.find('.message img')
.each((_, item) => {
item = $(item);
const src = item.attr('src');
if (src !== undefined && !src.startsWith('https://') && !src.startsWith('http://')) {
item.attr('src', `https://bbs.kanxue.com/${src}`);
}
});
const description = $('.card').eq(0).find('.message').html();
return {
title: subject.text(),
link,
pubDate,
description,
};
});
})
.get()
: []
);
return {
title,
link: baseUrl + path,
item: resultItem,
allowEmpty: true
};
};