diff --git a/docs/en/new-media.md b/docs/en/new-media.md index 54901f223..28046b4e6 100644 --- a/docs/en/new-media.md +++ b/docs/en/new-media.md @@ -570,6 +570,10 @@ This route provides a flexible plan with full text content to subscribe specific +### Research + + + ## Phoronix ### News & Reviews diff --git a/docs/new-media.md b/docs/new-media.md index 3650c2668..5a1274bb9 100644 --- a/docs/new-media.md +++ b/docs/new-media.md @@ -1040,6 +1040,10 @@ IPFS 网关有可能失效,那时候换成其他网关。 +### Research + + + ## OR ### 频道 diff --git a/lib/v2/openai/blog.js b/lib/v2/openai/blog.js index dabc4dd54..327005fab 100644 --- a/lib/v2/openai/blog.js +++ b/lib/v2/openai/blog.js @@ -1,8 +1,6 @@ const got = require('@/utils/got'); -const cheerio = require('cheerio'); -const { art } = require('@/utils/render'); const { toTitleCase } = require('@/utils/common-utils'); -const path = require('path'); +const { getApiUrl, parseArticle } = require('./common'); module.exports = async (ctx) => { const tag = ctx.params.tag || ''; @@ -11,18 +9,7 @@ module.exports = async (ctx) => { const blogRootUrl = 'https://openai.com/blog'; const blogOriginUrl = `${rootUrl}/blog${tag === '' ? '' : `?topics=${tag}`}`; - // Find API base URL - const initResponse = await got({ - method: 'get', - url: blogRootUrl, - }); - - let apiBaseUrl = initResponse.data - .toString() - .match(/(?<=TWILL_API_BASE:").+?(?=")/)[0] - .replaceAll('\\u002F', '/'); - apiBaseUrl = apiBaseUrl + '/api/v1/blog-details'; - const apiUrl = new URL(apiBaseUrl); + const apiUrl = new URL('/api/v1/blog-details', await getApiUrl()); // Construct API query apiUrl.searchParams.append('sort', '-publicationDate,-createdAt'); @@ -43,47 +30,7 @@ module.exports = async (ctx) => { const items = await Promise.all( list.map((item) => { const attributes = item.attributes; - const textUrl = `${blogRootUrl}/${attributes.slug}`; - return ctx.cache.tryGet(attributes.slug, async () => { - const detailResponse = await got({ - method: 'get', - url: textUrl, - }); - let content = cheerio.load(detailResponse.data); - - const authors = content('[aria-labelledby="metaAuthorsHeading"] > li > a > span > span') - .toArray() - .map((entry) => content(entry).text()) - .join(', '); - - // Leave out comments - const comments = content('*') - .contents() - .filter(function () { - return this.nodeType === 8; - }); - comments.remove(); - - content = content('#content'); - - const imageSrc = attributes.seo.ogImageSrc; - const imageAlt = attributes.seo.ogImageAlt; - - const article = art(path.join(__dirname, 'templates/article.art'), { - content, - imageSrc, - imageAlt, - }); - - return { - title: attributes.title, - author: authors, - description: article, - pubDate: attributes.createdAt, - category: attributes.tags.map((tag) => tag.title), - link: textUrl, - }; - }); + return parseArticle(ctx, blogRootUrl, attributes); }) ); diff --git a/lib/v2/openai/common.js b/lib/v2/openai/common.js new file mode 100644 index 000000000..6bf6c9ea0 --- /dev/null +++ b/lib/v2/openai/common.js @@ -0,0 +1,72 @@ +const got = require('@/utils/got'); +const cheerio = require('cheerio'); +const { art } = require('@/utils/render'); +const path = require('path'); + +const getApiUrl = async () => { + const blogRootUrl = 'https://openai.com/blog'; + + // Find API base URL + const initResponse = await got({ + method: 'get', + url: blogRootUrl, + }); + + const apiBaseUrl = initResponse.data + .toString() + .match(/(?<=TWILL_API_BASE:").+?(?=")/)[0] + .replaceAll('\\u002F', '/'); + + return new URL(apiBaseUrl); +}; + +const parseArticle = (ctx, rootUrl, attributes) => + ctx.cache.tryGet(attributes.slug, async () => { + const textUrl = `${rootUrl}/${attributes.slug}`; + const detailResponse = await got({ + method: 'get', + url: textUrl, + }); + let content = cheerio.load(detailResponse.data); + + const authors = content('[aria-labelledby="metaAuthorsHeading"] > li > a > span > span') + .toArray() + .map((entry) => content(entry).text()) + .join(', '); + + // Leave out comments + const comments = content('*') + .contents() + .filter(function () { + return this.nodeType === 8; + }); + comments.remove(); + + content = content('#content'); + + const imageSrc = attributes.seo.ogImageSrc; + const imageAlt = attributes.seo.ogImageAlt; + + const article = art(path.join(__dirname, 'templates/article.art'), { + content, + imageSrc, + imageAlt, + }); + + // Not all article has tags + attributes.tags = attributes.tags || []; + + return { + title: attributes.title, + author: authors, + description: article, + pubDate: attributes.createdAt, + category: attributes.tags.map((tag) => tag.title), + link: textUrl, + }; + }); + +module.exports = { + getApiUrl, + parseArticle, +}; diff --git a/lib/v2/openai/maintainer.js b/lib/v2/openai/maintainer.js index 65fa1d8d2..45ded734e 100644 --- a/lib/v2/openai/maintainer.js +++ b/lib/v2/openai/maintainer.js @@ -1,3 +1,4 @@ module.exports = { '/blog/:tag?': ['StevenRCE0', 'nczitzk'], + '/research': ['yuguorui'], }; diff --git a/lib/v2/openai/radar.js b/lib/v2/openai/radar.js index 59c43c164..8f2d02c46 100644 --- a/lib/v2/openai/radar.js +++ b/lib/v2/openai/radar.js @@ -23,5 +23,13 @@ module.exports = { target: () => '/openai/chatgpt/release-notes', }, ], + research: [ + { + title: 'Research', + docs: 'https://docs.rsshub.app/en/new-media.html#openai', + source: '/research', + target: () => '/openai/research', + }, + ], }, }; diff --git a/lib/v2/openai/research.js b/lib/v2/openai/research.js new file mode 100644 index 000000000..3af8174a3 --- /dev/null +++ b/lib/v2/openai/research.js @@ -0,0 +1,32 @@ +const got = require('@/utils/got'); +const { getApiUrl, parseArticle } = require('./common'); + +module.exports = async (ctx) => { + const apiUrl = new URL('/api/v1/research-publications', await getApiUrl()); + const researchRootUrl = 'https://openai.com/research'; + + // Construct API query + apiUrl.searchParams.append('sort', '-publicationDate,-createdAt'); + apiUrl.searchParams.append('include', 'media'); + + const resp = await got({ + method: 'get', + url: apiUrl, + }); + const obj = resp.data; + + const items = await Promise.all( + obj.data.map((item) => { + const attributes = item.attributes; + return parseArticle(ctx, researchRootUrl, attributes); + }) + ); + + const title = 'OpenAI Research'; + + ctx.state.data = { + title, + link: researchRootUrl, + item: items, + }; +}; diff --git a/lib/v2/openai/router.js b/lib/v2/openai/router.js index ba805cb2a..7fbd1410f 100644 --- a/lib/v2/openai/router.js +++ b/lib/v2/openai/router.js @@ -1,4 +1,5 @@ module.exports = function (router) { router.get('/blog/:tag?', require('./blog')); router.get('/chatgpt/release-notes', require('./chatgpt')); + router.get('/research', require('./research')); };