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'));
};