rsshub
Version:
Make RSS Great Again!
154 lines (139 loc) • 5.33 kB
text/typescript
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
.toArray()
.filter((elem) => {
const timeStr = $('.date', elem).eq(0).text();
const pubDate = timeStr.endsWith('前') ? parseRelativeDate(timeStr) : parseDate(timeStr.slice(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.slice(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,
};
});
})
: []
);
return {
title,
link: baseUrl + path,
item: resultItem,
allowEmpty: true,
};
}