From 6a1d5ad0a049917f9e4f43abb74bc657f9dbe1be Mon Sep 17 00:00:00 2001 From: Tony Date: Tue, 11 Oct 2022 12:48:50 -0500 Subject: [PATCH] fix(route): luogu (#11064) --- docs/programming.md | 8 ++-- lib/router.js | 6 +-- lib/routes/luogu/contest.js | 28 -------------- lib/routes/luogu/daily.js | 53 -------------------------- lib/routes/luogu/userFeed.js | 38 ------------------- lib/v2/luogu/contest.js | 72 ++++++++++++++++++++++++++++++++++++ lib/v2/luogu/daily.js | 33 +++++++++++++++++ lib/v2/luogu/maintainer.js | 5 +++ lib/v2/luogu/radar.js | 25 +++++++++++++ lib/v2/luogu/router.js | 5 +++ lib/v2/luogu/userFeed.js | 30 +++++++++++++++ 11 files changed, 177 insertions(+), 126 deletions(-) delete mode 100644 lib/routes/luogu/contest.js delete mode 100644 lib/routes/luogu/daily.js delete mode 100644 lib/routes/luogu/userFeed.js create mode 100644 lib/v2/luogu/contest.js create mode 100644 lib/v2/luogu/daily.js create mode 100644 lib/v2/luogu/maintainer.js create mode 100644 lib/v2/luogu/radar.js create mode 100644 lib/v2/luogu/router.js create mode 100644 lib/v2/luogu/userFeed.js diff --git a/docs/programming.md b/docs/programming.md index 326e5dbdc..55e3dc261 100644 --- a/docs/programming.md +++ b/docs/programming.md @@ -958,15 +958,15 @@ GitHub 官方也提供了一些 RSS: ### 日报 - + -### 近期比赛 +### 比赛列表 - + ### 用户动态 - + ## 码农俱乐部 diff --git a/lib/router.js b/lib/router.js index 80c448a5e..cbec58fa1 100644 --- a/lib/router.js +++ b/lib/router.js @@ -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('/luogu/daily/:id?', lazyloadRouteHandler('./routes/luogu/daily')); -router.get('/luogu/contest', lazyloadRouteHandler('./routes/luogu/contest')); -router.get('/luogu/user/feed/:uid', lazyloadRouteHandler('./routes/luogu/userFeed')); +// router.get('/luogu/daily/:id?', lazyloadRouteHandler('./routes/luogu/daily')); +// router.get('/luogu/contest', lazyloadRouteHandler('./routes/luogu/contest')); +// router.get('/luogu/user/feed/:uid', lazyloadRouteHandler('./routes/luogu/userFeed')); // 决胜网 router.get('/juesheng', lazyloadRouteHandler('./routes/juesheng')); diff --git a/lib/routes/luogu/contest.js b/lib/routes/luogu/contest.js deleted file mode 100644 index f6211e9a5..000000000 --- a/lib/routes/luogu/contest.js +++ /dev/null @@ -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, - }; -}; diff --git a/lib/routes/luogu/daily.js b/lib/routes/luogu/daily.js deleted file mode 100644 index 8e0c7b1eb..000000000 --- a/lib/routes/luogu/daily.js +++ /dev/null @@ -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, - }; -}; diff --git a/lib/routes/luogu/userFeed.js b/lib/routes/luogu/userFeed.js deleted file mode 100644 index 17f944c20..000000000 --- a/lib/routes/luogu/userFeed.js +++ /dev/null @@ -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, - })), - }; -}; diff --git a/lib/v2/luogu/contest.js b/lib/v2/luogu/contest.js new file mode 100644 index 000000000..62ecbeb50 --- /dev/null +++ b/lib/v2/luogu/contest.js @@ -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, + }; +}; diff --git a/lib/v2/luogu/daily.js b/lib/v2/luogu/daily.js new file mode 100644 index 000000000..cd7de0f93 --- /dev/null +++ b/lib/v2/luogu/daily.js @@ -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, + }; +}; diff --git a/lib/v2/luogu/maintainer.js b/lib/v2/luogu/maintainer.js new file mode 100644 index 000000000..8a7d98e90 --- /dev/null +++ b/lib/v2/luogu/maintainer.js @@ -0,0 +1,5 @@ +module.exports = { + '/contest': ['prnake'], + '/daily/:id?': ['LogicJake ', 'prnake ', 'nczitzk'], + '/user/feed/:uid': ['solstice23'], +}; diff --git a/lib/v2/luogu/radar.js b/lib/v2/luogu/radar.js new file mode 100644 index 000000000..7b8172918 --- /dev/null +++ b/lib/v2/luogu/radar.js @@ -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', + }, + ], + }, +}; diff --git a/lib/v2/luogu/router.js b/lib/v2/luogu/router.js new file mode 100644 index 000000000..eb1e9da77 --- /dev/null +++ b/lib/v2/luogu/router.js @@ -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')); +}; diff --git a/lib/v2/luogu/userFeed.js b/lib/v2/luogu/userFeed.js new file mode 100644 index 000000000..2b62fcf4f --- /dev/null +++ b/lib/v2/luogu/userFeed.js @@ -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`, + })), + }; +};