fix(route): luogu (#11064)

This commit is contained in:
Tony
2022-10-11 12:48:50 -05:00
committed by GitHub
parent 7f355ed2d5
commit 6a1d5ad0a0
11 changed files with 177 additions and 126 deletions

View File

@@ -958,15 +958,15 @@ GitHub 官方也提供了一些 RSS:
### 日报 ### 日报
<Route author="LogicJake prnake nczitzk" example="/luogu/daily" path="/luogu/daily/:id?" :paramsDesc="['年度日报所在帖子 id可在 URL 中找到,不填默认为 2020 年日报']"/> <Route author="LogicJake prnake nczitzk" example="/luogu/daily" path="/luogu/daily/:id?" :paramsDesc="['年度日报所在帖子 id可在 URL 中找到,不填默认为 `47327`']" radar="1" rssbud="1"/>
### 近期比赛 ### 比赛列表
<Route author="prnake" example="/luogu/contest" path="/luogu/contest"/> <Route author="prnake" example="/luogu/contest" path="/luogu/contest" radar="1" rssbud="1"/>
### 用户动态 ### 用户动态
<Route author="solstice23" example="/luogu/user/feed/1" path="/luogu/user/feed/:uid" :paramsDesc="['用户 UID']"/> <Route author="solstice23" example="/luogu/user/feed/1" path="/luogu/user/feed/:uid" :paramsDesc="['用户 UID']" radar="1" rssbud="1"/>
## 码农俱乐部 ## 码农俱乐部

View File

@@ -1356,9 +1356,9 @@ router.get('/ui-cn/user/:id', lazyloadRouteHandler('./routes/ui-cn/user'));
router.get('/bjp/apod', lazyloadRouteHandler('./routes/bjp/apod')); router.get('/bjp/apod', lazyloadRouteHandler('./routes/bjp/apod'));
// 洛谷 // 洛谷
router.get('/luogu/daily/:id?', lazyloadRouteHandler('./routes/luogu/daily')); // router.get('/luogu/daily/:id?', lazyloadRouteHandler('./routes/luogu/daily'));
router.get('/luogu/contest', lazyloadRouteHandler('./routes/luogu/contest')); // router.get('/luogu/contest', lazyloadRouteHandler('./routes/luogu/contest'));
router.get('/luogu/user/feed/:uid', lazyloadRouteHandler('./routes/luogu/userFeed')); // router.get('/luogu/user/feed/:uid', lazyloadRouteHandler('./routes/luogu/userFeed'));
// 决胜网 // 决胜网
router.get('/juesheng', lazyloadRouteHandler('./routes/juesheng')); router.get('/juesheng', lazyloadRouteHandler('./routes/juesheng'));

View File

@@ -1,28 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const resolve_url = require('url').resolve;
module.exports = async (ctx) => {
const link = 'https://www.luogu.com.cn/';
const response = await got.get(link);
const $ = cheerio.load(response.data);
const title = '洛谷近期比赛';
const out = $('.am-panel-hd ')
.slice(0, 10)
.map(function () {
const info = {
title: $(this).find('a').text() || $(this).text(),
description: $(this).html() + $(this).parent().find('.am-panel-bd').html(),
link: resolve_url('https://www.luogu.com.cn', $(this).find('a').attr('href')),
};
return info;
})
.get();
ctx.state.data = {
title,
link,
item: out,
};
};

View File

@@ -1,53 +0,0 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
module.exports = async (ctx) => {
const id = ctx.params.id || 179788;
const link = `https://www.luogu.org/discuss/show/${id}`;
const response = await got.get(link);
const $ = cheerio.load(response.data);
const title = $('title').text();
const out = $('div.am-comment-main > div > p')
.slice(0, 10)
.map(function () {
const info = {
title: $(this).find('strong').text() || $(this).text(),
link: $(this).find('a').attr('href'),
};
return info;
})
.get();
const items = await Promise.all(
out.map((item) =>
ctx.cache.tryGet(item.link, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
});
const content = cheerio.load(detailResponse.data);
const timeRegExp = new RegExp(/(?<=([1-9]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])\s+(20|21|22|23|[0-1]\d):[0-5]\d:[0-5]\d))/);
item.description = content('#article-content').html();
content('#article-content').remove();
const line = content
.html()
.split('\n')
.find((line) => timeRegExp.test(line));
item.pubDate = new Date(line.match(timeRegExp)[1] + ' GMT+8').toUTCString();
return item;
})
)
);
ctx.state.data = {
title,
link,
item: items,
};
};

View File

@@ -1,38 +0,0 @@
const got = require('@/utils/got');
module.exports = async (ctx) => {
const getUsernameFromUID = async (uid) => {
const key = 'luogu-username-from-uid-' + uid;
let name = await ctx.cache.get(key);
if (!name) {
const nameResponse = await got({
method: 'get',
url: `https://www.luogu.com.cn/user/${uid}?_contentOnly=1`,
});
name = nameResponse.data.currentData.user.name;
ctx.cache.set(key, name);
}
return name;
};
const uid = ctx.params.uid;
const name = await getUsernameFromUID(uid);
const response = await got({
method: 'get',
url: `https://www.luogu.com.cn/api/feed/list?user=${uid}`,
});
const data = response.data.feeds.result;
ctx.state.data = {
title: `${name} 的洛谷动态`,
link: `https://www.luogu.com.cn/user/${uid}#activity`,
allowEmpty: true,
item: data.map((item) => ({
title: item.content,
description: item.content,
pubDate: new Date(item.time * 1000).toUTCString(),
link: `https://www.luogu.com.cn/user/${uid}#activity`,
guid: item.id,
})),
};
};

72
lib/v2/luogu/contest.js Normal file
View File

@@ -0,0 +1,72 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const md = require('markdown-it')();
const { parseDate } = require('@/utils/parse-date');
const asyncPool = require('tiny-async-pool');
const baseUrl = 'https://www.luogu.com.cn';
const typeMap = {
ruleType: {
1: 'OI',
2: 'ACM',
3: '乐多',
4: 'IOI',
},
visibilityType: {
1: '官方比赛',
2: '团队公开赛',
4: '个人公开赛',
},
// invitationCodeType: {
// 1: '',
// 2: '',
// },
};
module.exports = async (ctx) => {
const link = `${baseUrl}/contest/list`;
const { data: response } = await got(link);
const $ = cheerio.load(response);
const data = JSON.parse(
decodeURIComponent(
$('script')
.text()
.match(/decodeURIComponent\("(.*)"\)/)[1]
)
);
const result = [];
for await (const item of asyncPool(4, data.currentData.contests.result, (item) =>
ctx.cache.tryGet(`${baseUrl}/contest/${item.id}`, async () => {
const { data: response } = await got(`${baseUrl}/contest/${item.id}`);
const $ = cheerio.load(response);
const data = JSON.parse(
decodeURIComponent(
$('script')
.text()
.match(/decodeURIComponent\("(.*)"\)/)[1]
)
);
return {
title: item.name,
description: md.render(data.currentData.contest.description),
link: `${baseUrl}/contest/${item.id}`,
author: item.host.name,
pubDate: parseDate(item.startTime, 'X'),
category: [item.rated ? 'Rated' : null, typeMap.ruleType[item.ruleType], typeMap.visibilityType[item.visibilityType]].filter((i) => i),
};
})
)) {
result.push(item);
}
ctx.state.data = {
title: $('head title').text(),
link,
image: 'https://www.luogu.com.cn/favicon.ico',
item: result,
};
};

33
lib/v2/luogu/daily.js Normal file
View File

@@ -0,0 +1,33 @@
const got = require('@/utils/got');
const cheerio = require('cheerio');
const { parseDate } = require('@/utils/parse-date');
module.exports = async (ctx) => {
const id = ctx.params.id ?? 47327;
const link = `https://www.luogu.com.cn/discuss/${id}`;
const response = await got(link);
const $ = cheerio.load(response.data);
const title = $('head title').text();
const firstPost = $('.am-comment-main .am-comment-bd').first();
const dailyLink = firstPost.find('a').first().attr('href');
const issueHeading = firstPost.find('h1').text().trim();
const { data: dailyResponse } = await got(dailyLink);
const $daily = cheerio.load(dailyResponse);
const item = [
{
title,
description: $daily('#article-content').html(),
link,
author: firstPost.find('p').eq(1).text(),
guid: `${link}#${issueHeading}`,
pubDate: parseDate(issueHeading.match(/(\d{4} 年 \d{2} 月 \d{2} 日)/)[1], 'YYYY 年 MM 月 DD 日'),
},
];
ctx.state.data = {
title: '洛谷日报',
link,
item,
};
};

View File

@@ -0,0 +1,5 @@
module.exports = {
'/contest': ['prnake'],
'/daily/:id?': ['LogicJake ', 'prnake ', 'nczitzk'],
'/user/feed/:uid': ['solstice23'],
};

25
lib/v2/luogu/radar.js Normal file
View File

@@ -0,0 +1,25 @@
module.exports = {
'luogu.com.cn': {
_name: '洛谷',
'.': [
{
title: '日报',
docs: 'https://docs.rsshub.app/programming.html#luo-gu',
source: ['/discuss/47327', '/'],
target: '/luogu/daily',
},
{
title: '比赛列表',
docs: 'https://docs.rsshub.app/programming.html#luo-gu',
source: ['/contest/list', '/'],
target: '/luogu/contest',
},
{
title: '用户动态',
docs: 'https://docs.rsshub.app/programming.html#luo-gu',
source: ['/user/:uid'],
target: '/luogu/user/feed/:uid',
},
],
},
};

5
lib/v2/luogu/router.js Normal file
View File

@@ -0,0 +1,5 @@
module.exports = (router) => {
router.get('/contest', require('./contest'));
router.get('/daily/:id?', require('./daily'));
router.get('/user/feed/:uid', require('./userFeed'));
};

30
lib/v2/luogu/userFeed.js Normal file
View File

@@ -0,0 +1,30 @@
const got = require('@/utils/got');
const { parseDate } = require('@/utils/parse-date');
const md = require('markdown-it')();
module.exports = async (ctx) => {
const getUsernameFromUID = (uid) =>
ctx.cache.tryGet('luogu:username:' + uid, async () => {
const { data } = await got(`https://www.luogu.com.cn/user/${uid}?_contentOnly=1`);
return data.currentData.user.name;
});
const uid = ctx.params.uid;
const name = await getUsernameFromUID(uid);
const { data: response } = await got(`https://www.luogu.com.cn/api/feed/list?user=${uid}`);
const data = response.feeds.result;
ctx.state.data = {
title: `${name} 的洛谷动态`,
link: `https://www.luogu.com.cn/user/${uid}#activity`,
allowEmpty: true,
item: data.map((item) => ({
title: item.content,
description: md.render(item.content),
pubDate: parseDate(item.time, 'X'),
author: name,
link: `https://www.luogu.com.cn/user/${uid}#activity`,
})),
};
};