diff --git a/lib/router.js b/lib/router.js index 69d77378d2..9548129686 100644 --- a/lib/router.js +++ b/lib/router.js @@ -1308,7 +1308,7 @@ router.get('/aisixiang/thinktank/:name/:type?', lazyloadRouteHandler('./routes/a // router.get('/hackernews/:section/:type?', lazyloadRouteHandler('./routes/hackernews/story')); // LeetCode -router.get('/leetcode/articles', lazyloadRouteHandler('./routes/leetcode/articles')); +// router.get('/leetcode/articles', lazyloadRouteHandler('./routes/leetcode/articles')); router.get('/leetcode/submission/us/:user', lazyloadRouteHandler('./routes/leetcode/check-us')); router.get('/leetcode/submission/cn/:user', lazyloadRouteHandler('./routes/leetcode/check-cn')); diff --git a/lib/routes/leetcode/articles.js b/lib/routes/leetcode/articles.js deleted file mode 100644 index 45d59e0786..0000000000 --- a/lib/routes/leetcode/articles.js +++ /dev/null @@ -1,77 +0,0 @@ -const got = require('@/utils/got'); -const cheerio = require('cheerio'); -const url = require('url'); -const showdown = require('showdown'); - -const host = 'https://leetcode.com'; - -module.exports = async (ctx) => { - const link = url.resolve(host, '/articles'); - const response = await got.get(link); - const $ = cheerio.load(response.data); - - const list = $('a.list-group-item') - .slice(0, 10) - .filter((i, e) => $(e).find('h4.media-heading i').length === 0) - .map(function () { - const info = { - title: $(this).find('h4.media-heading').text().trim(), - author: $(this).find('.text-500').text(), - link: $(this).attr('href'), - date: $(this).find('p.pull-right.media-date strong').text().trim(), - }; - return info; - }) - .get(); - - const out = await Promise.all( - list.map(async (info) => { - const itemUrl = url.resolve(host, info.link); - const titelSlug = info.link.split('/')[2]; - - const cache = await ctx.cache.get(itemUrl); - if (cache) { - return Promise.resolve(JSON.parse(cache)); - } - - const questionData = await got - .post(url.resolve(host, '/graphql'), { - json: { operationName: 'questionData', variables: { titleSlug: titelSlug }, query: 'query questionData($titleSlug: String!) {\n question(titleSlug: $titleSlug) {\n content\n }\n}\n' }, - }) - .json(); - - const questionNote = await got - .post(url.resolve(host, '/graphql'), { - json: { - operationName: 'QuestionNote', - variables: { titleSlug: titelSlug }, - query: 'query QuestionNote($titleSlug: String!) {\n question(titleSlug: $titleSlug) {\n solution {\n content\n }\n }\n}\n', - }, - }) - .json(); - - const converter = new showdown.Converter(); - const solution = converter.makeHtml(questionNote.data.question.solution.content); - - const description = questionData.data.question.content.trim() + solution; - - const single = { - title: info.title, - author: info.author, - link: itemUrl, - description, - pubDate: new Date(info.date).toUTCString(), - }; - - ctx.cache.set(itemUrl, JSON.stringify(single)); - return Promise.resolve(single); - }) - ); - - ctx.state.data = { - title: 'LeetCode Articles', - description: 'LeetCode Articles, the only official solutions you will find.', - link, - item: out, - }; -}; diff --git a/lib/v2/leetcode/articles.js b/lib/v2/leetcode/articles.js new file mode 100644 index 0000000000..0ae6395b91 --- /dev/null +++ b/lib/v2/leetcode/articles.js @@ -0,0 +1,80 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { parseDate } = require('@/utils/parse-date'); +const showdown = require('showdown'); + +const host = 'https://leetcode.com'; +const gqlEndpoint = `${host}/graphql`; + +module.exports = async (ctx) => { + const link = new URL('/articles/', host).href; + const response = await got(link); + const $ = cheerio.load(response.data); + + const list = $('a.list-group-item') + .filter((i, e) => $(e).find('h4.media-heading i').length === 0) + .map(function () { + const info = { + title: $(this).find('h4.media-heading').text().trim(), + author: $(this).find('.text-500').text(), + link: new URL($(this).attr('href'), host).href, + pubDate: $(this).find('p.pull-right.media-date strong').text().trim(), + }; + return info; + }) + .get(); + + const out = await Promise.all( + list.map((info) => + ctx.cache.tryGet(info.link, async () => { + const titleSlug = info.link.split('/')[4]; + + const questionData = await got + .post(gqlEndpoint, { + json: { + operationName: 'questionData', + variables: { titleSlug }, + query: `query questionData($titleSlug: String!) { + question(titleSlug: $titleSlug) { + content + } + }`, + }, + }) + .json(); + + const questionNote = await got + .post(gqlEndpoint, { + json: { + operationName: 'QuestionNote', + variables: { titleSlug }, + query: `query QuestionNote($titleSlug: String!) { + question(titleSlug: $titleSlug) { + solution { + content + } + } + }`, + }, + }) + .json(); + + const converter = new showdown.Converter(); + const solution = converter.makeHtml(questionNote.data.question.solution.content); + + info.description = questionData.data.question.content.trim() + solution; + info.pubDate = parseDate(info.pubDate); + + return info; + }) + ) + ); + + ctx.state.data = { + title: $('head title').text(), + description: $('meta[property="og:description"]').attr('content'), + image: 'https://assets.leetcode.com/static_assets/public/icons/favicon-192x192.png', + link, + item: out, + }; +}; diff --git a/lib/v2/leetcode/maintainer.js b/lib/v2/leetcode/maintainer.js index 127d69bf0f..c29e992507 100644 --- a/lib/v2/leetcode/maintainer.js +++ b/lib/v2/leetcode/maintainer.js @@ -1,4 +1,5 @@ module.exports = { - '/dailyquestion/en': ['NavePnow'], - '/dailyquestion/cn': ['NavePnow'], + '/articles': ['LogicJake'], + '/dailyquestion/:lang': ['NavePnow'], + '/submission/:country/:user': ['NathanDai'], }; diff --git a/lib/v2/leetcode/radar.js b/lib/v2/leetcode/radar.js new file mode 100644 index 0000000000..0e3f7511a2 --- /dev/null +++ b/lib/v2/leetcode/radar.js @@ -0,0 +1,50 @@ +module.exports = { + 'leetcode.com': { + _name: 'LeetCode', + '.': [ + { + title: '文章', + docs: 'https://docs.rsshub.app/programming.html#leetcode', + source: ['/articles'], + target: '/leetcode/articles', + }, + { + title: '打卡', + docs: 'https://docs.rsshub.app/programming.html#leetcode', + source: ['/:user'], + target: (params) => { + if (params.user !== 'articles') { + return `/leetcode/submission/us/:user`; + } + }, + }, + { + title: '每日一题', + docs: 'https://docs.rsshub.app/programming.html#leetcode', + source: ['/'], + target: '/leetcode/dailyquestion/en', + }, + ], + }, + 'leetcode.cn': { + _name: 'LeetCode', + '.': [ + { + title: '打卡', + docs: 'https://docs.rsshub.app/programming.html#leetcode', + source: ['/:user'], + target: (params) => { + if (params.user !== 'articles') { + return `/leetcode/submission/cn/:user`; + } + }, + }, + { + title: '每日一题', + docs: 'https://docs.rsshub.app/programming.html#leetcode', + source: ['/'], + target: '/leetcode/dailyquestion/cn', + }, + ], + }, +}; diff --git a/lib/v2/leetcode/router.js b/lib/v2/leetcode/router.js index 011a86178d..3b8d337ac3 100644 --- a/lib/v2/leetcode/router.js +++ b/lib/v2/leetcode/router.js @@ -1,4 +1,5 @@ module.exports = function (router) { - router.get('/dailyquestion/en', require('./dailyquestion-en')); + router.get('/articles', require('./articles')); router.get('/dailyquestion/cn', require('./dailyquestion-cn')); + router.get('/dailyquestion/en', require('./dailyquestion-en')); };