diff --git a/docs/en/new-media.md b/docs/en/new-media.md
index fb8c7628c2c17..ddde6afd89d25 100644
--- a/docs/en/new-media.md
+++ b/docs/en/new-media.md
@@ -434,6 +434,16 @@ Provides a better reading experience (full text articles) over the official one.
+## Kuwait Local
+
+### Latest News
+
+
+
+### Categorised News
+
+
+
## Letterboxd
### User diary
diff --git a/docs/en/traditional-media.md b/docs/en/traditional-media.md
index 7e7a3aab3626a..0be094d436bd7 100644
--- a/docs/en/traditional-media.md
+++ b/docs/en/traditional-media.md
@@ -160,6 +160,18 @@ Generates full-text feeds that the official feed doesn't provide.
+## CNBC
+
+### Full article RSS
+
+
+
+Provides a better reading experience (full articles) over the official ones.
+
+Support all channels, refer to [CNBC RSS feeds](https://www.cnbc.com/rss-feeds/).
+
+
+
## Deutsche Welle
### News
diff --git a/docs/programming.md b/docs/programming.md
index c510b5bc1306b..3bbe87dc03f87 100644
--- a/docs/programming.md
+++ b/docs/programming.md
@@ -720,7 +720,7 @@ GitHub 官方也提供了一些 RSS:
### 分类订阅
-
+
| 360 网络安全周报 | 活动 | 知识 | 资讯 | 招聘 | 工具 |
| ---------- | -------- | --------- | ---- | --- | ---- |
diff --git a/docs/traditional-media.md b/docs/traditional-media.md
index 98371dc0ef48b..6a0b39e628d08 100644
--- a/docs/traditional-media.md
+++ b/docs/traditional-media.md
@@ -141,6 +141,18 @@ pageClass: routes
+## CNBC
+
+### 全文 RSS
+
+
+
+通过提取文章全文,以提供比官方源更佳的阅读体验。
+
+支持所有频道,频道名称见 [官方频道 RSS](https://www.cnbc.com/rss-feeds/)。
+
+
+
## Deutsche Welle 德国之声
### 新闻
diff --git a/lib/v2/anquanke/category.js b/lib/v2/anquanke/category.js
index cc4262e491131..be7ec7bddc3b1 100644
--- a/lib/v2/anquanke/category.js
+++ b/lib/v2/anquanke/category.js
@@ -1,21 +1,35 @@
const got = require('@/utils/got');
+const cheerio = require('cheerio');
const { parseDate } = require('@/utils/parse-date');
const timezone = require('@/utils/timezone');
module.exports = async (ctx) => {
const api = 'https://api.anquanke.com/data/v1/posts?size=10&page=1&category=';
const type = ctx.params.category;
+ const fulltext = ctx.params.fulltext;
const host = 'https://www.anquanke.com';
const res = await got(`${api}${type}`);
const dataArray = res.data.data;
- const items = dataArray.map((item) => ({
- title: item.title,
- description: item.desc,
- pubDate: timezone(parseDate(item.date), +8),
- link: `${host}/${type === 'week' ? 'week' : 'post'}/id/${item.id}`,
- author: item.author.nickname,
- }));
+ const items = await Promise.all(
+ dataArray.map(async (item) => {
+ const art_url = `${host}/${type === 'week' ? 'week' : 'post'}/id/${item.id}`;
+ return {
+ title: item.title,
+ description:
+ fulltext === 'fulltext' || fulltext === 'quanwen'
+ ? await ctx.cache.tryGet(art_url, async () => {
+ const { data: res } = await got(art_url);
+ const content = cheerio.load(res);
+ return content('#js-article').html();
+ })
+ : item.desc,
+ pubDate: timezone(parseDate(item.date), +8),
+ link: art_url,
+ author: item.author.nickname,
+ };
+ })
+ );
ctx.state.data = {
title: `安全客-${dataArray[0].category_name}`,
diff --git a/lib/v2/anquanke/maintainer.js b/lib/v2/anquanke/maintainer.js
index e247fa46d95a2..0dc8f87b540c9 100644
--- a/lib/v2/anquanke/maintainer.js
+++ b/lib/v2/anquanke/maintainer.js
@@ -1,4 +1,4 @@
module.exports = {
// '/vul': ['qwertyuiop6'],
- '/:category': ['qwertyuiop6'],
+ '/:category/:fulltext?': ['qwertyuiop6'],
};
diff --git a/lib/v2/anquanke/router.js b/lib/v2/anquanke/router.js
index 7cc1f079a5ad4..08674bf4e3f15 100644
--- a/lib/v2/anquanke/router.js
+++ b/lib/v2/anquanke/router.js
@@ -1,4 +1,4 @@
module.exports = (router) => {
// router.get('/vul', require('./vul')); // 404
- router.get('/:category', require('./category'));
+ router.get('/:category/:fulltext?', require('./category'));
};
diff --git a/lib/v2/cnbc/maintainer.js b/lib/v2/cnbc/maintainer.js
new file mode 100644
index 0000000000000..3507ac9104802
--- /dev/null
+++ b/lib/v2/cnbc/maintainer.js
@@ -0,0 +1,3 @@
+module.exports = {
+ '/rss/:id?': ['TonyRL'],
+};
diff --git a/lib/v2/cnbc/radar.js b/lib/v2/cnbc/radar.js
new file mode 100644
index 0000000000000..4bb265ec7eb79
--- /dev/null
+++ b/lib/v2/cnbc/radar.js
@@ -0,0 +1,21 @@
+module.exports = {
+ 'cnbc.com': {
+ _name: 'CNBC',
+ search: [
+ {
+ title: '全文 RSS',
+ docs: 'https://docs.rsshub.app/traditional-media.html#cnbc',
+ source: ['/rs/search/combinedcms/view.xml'],
+ target: (_, url) => `/cnbc/rss/${new URL(url).searchParams.get('id')}`,
+ },
+ ],
+ www: [
+ {
+ title: '全文 RSS',
+ docs: 'https://docs.rsshub.app/traditional-media.html#cnbc',
+ source: ['/id/:id/device/rss/rss.html'],
+ target: '/cnbc/rss/:id',
+ },
+ ],
+ },
+};
diff --git a/lib/v2/cnbc/router.js b/lib/v2/cnbc/router.js
new file mode 100644
index 0000000000000..14adb726dd19f
--- /dev/null
+++ b/lib/v2/cnbc/router.js
@@ -0,0 +1,3 @@
+module.exports = (router) => {
+ router.get('/rss/:id?', require('./rss'));
+};
diff --git a/lib/v2/cnbc/rss.js b/lib/v2/cnbc/rss.js
new file mode 100644
index 0000000000000..3ca95705fa60b
--- /dev/null
+++ b/lib/v2/cnbc/rss.js
@@ -0,0 +1,54 @@
+const got = require('@/utils/got');
+const cheerio = require('cheerio');
+const parser = require('@/utils/rss-parser');
+
+module.exports = async (ctx) => {
+ const { id = '100003114' } = ctx.params;
+ const feed = await parser.parseURL(`https://search.cnbc.com/rs/search/combinedcms/view.xml?partnerId=wrss01&id=${id}`);
+
+ const items = await Promise.all(
+ feed.items
+ .filter((i) => !i.link.startsWith('https://www.cnbc.com/select/'))
+ .map((item) =>
+ ctx.cache.tryGet(item.link, async () => {
+ const { data: response } = await got(item.link);
+ const $ = cheerio.load(response);
+
+ delete item.content;
+ delete item.contentSnippet;
+ delete item.isoDate;
+
+ item.description = '';
+ if ($('.RenderKeyPoints-keyPoints').length) {
+ $('.RenderKeyPoints-keyPoints').html();
+ }
+ if ($('.FeaturedContent-articleBody').length) {
+ item.description += $('.FeaturedContent-articleBody').html();
+ }
+ if ($('.ArticleBody-articleBody').length) {
+ item.description += $('.ArticleBody-articleBody').html();
+ }
+ if ($('.LiveBlogBody-articleBody').length) {
+ item.description += $('.LiveBlogBody-articleBody').html();
+ }
+ if ($('.ClipPlayer-clipPlayer').length) {
+ item.description += $('.ClipPlayer-clipPlayer').html();
+ }
+
+ const meta = JSON.parse($('[type=application/ld+json]').last().text());
+ item.author = meta.author ? (meta.author.name ? meta.author.name : meta.author.map((a) => a.name).join(', ')) : null;
+ item.category = meta.keywords;
+
+ return item;
+ })
+ )
+ );
+
+ ctx.state.data = {
+ title: feed.title,
+ link: feed.link,
+ description: feed.description,
+ item: items,
+ language: feed.language,
+ };
+};
diff --git a/lib/v2/kuwaitlocal/index.js b/lib/v2/kuwaitlocal/index.js
new file mode 100644
index 0000000000000..0184b2ca7eca0
--- /dev/null
+++ b/lib/v2/kuwaitlocal/index.js
@@ -0,0 +1,47 @@
+const got = require('@/utils/got');
+const cheerio = require('cheerio');
+const { parseDate } = require('@/utils/parse-date');
+
+const baseUrl = 'https://kuwaitlocal.com';
+
+module.exports = async (ctx) => {
+ const { category = 'latest' } = ctx.params;
+ const url = `${baseUrl}/news/${category === 'latest' ? category : `categories/${category}`}`;
+
+ const { data: response } = await got(url);
+ const $ = cheerio.load(response);
+ const list = $('.news_list a')
+ .toArray()
+ .map((item) => {
+ item = $(item);
+ return {
+ title: item.text(),
+ link: baseUrl + item.attr('href'),
+ };
+ });
+
+ const items = await Promise.all(
+ list.map((item) =>
+ ctx.cache.tryGet(item.link, async () => {
+ const { data: response } = await got(item.link);
+ const $ = cheerio.load(response);
+
+ item.pubDate = parseDate($('.date_icon').next().text());
+ $('[id^=div-gpt-ad], .pad_10_0, .hide_desktop').remove();
+ item.description = $('.mob_pad_view').html();
+ item.category = $('.news_tags a')
+ .toArray()
+ .map((item) => $(item).text());
+ return item;
+ })
+ )
+ );
+
+ ctx.state.data = {
+ title: $('head title').text().trim(),
+ description: $('head meta[name="description"]').attr('content').trim(),
+ link: url,
+ item: items,
+ language: 'en',
+ };
+};
diff --git a/lib/v2/kuwaitlocal/maintainer.js b/lib/v2/kuwaitlocal/maintainer.js
new file mode 100644
index 0000000000000..385dc7d47a51a
--- /dev/null
+++ b/lib/v2/kuwaitlocal/maintainer.js
@@ -0,0 +1,4 @@
+module.exports = {
+ '/': ['TonyRL'],
+ '/:category?': ['TonyRL'],
+};
diff --git a/lib/v2/kuwaitlocal/radar.js b/lib/v2/kuwaitlocal/radar.js
new file mode 100644
index 0000000000000..c60853ca36cca
--- /dev/null
+++ b/lib/v2/kuwaitlocal/radar.js
@@ -0,0 +1,19 @@
+module.exports = {
+ 'kuwaitlocal.com': {
+ _name: 'Kuwait Local',
+ '.': [
+ {
+ title: 'Latest News',
+ docs: 'https://docs.rsshub.app/en/new-media.html#kuwait-local',
+ source: ['/news/latest', '/news', '/'],
+ target: '/kuwaitlocal',
+ },
+ {
+ title: 'Categorised News',
+ docs: 'https://docs.rsshub.app/en/new-media.html#kuwait-local',
+ source: ['/news/categories/:category'],
+ target: '/kuwaitlocal/:category',
+ },
+ ],
+ },
+};
diff --git a/lib/v2/kuwaitlocal/router.js b/lib/v2/kuwaitlocal/router.js
new file mode 100644
index 0000000000000..9885c36f13481
--- /dev/null
+++ b/lib/v2/kuwaitlocal/router.js
@@ -0,0 +1,3 @@
+module.exports = (router) => {
+ router.get('/:category?', require('./index'));
+};
diff --git a/lib/v2/wallstreetcn/live.js b/lib/v2/wallstreetcn/live.js
index 7e584ddd164e3..ee32c0c7a6651 100644
--- a/lib/v2/wallstreetcn/live.js
+++ b/lib/v2/wallstreetcn/live.js
@@ -1,5 +1,7 @@
const got = require('@/utils/got');
const { parseDate } = require('@/utils/parse-date');
+const { art } = require('@/utils/render');
+const path = require('path');
const titles = {
global: '要闻',
@@ -30,9 +32,13 @@ module.exports = async (ctx) => {
.map((item) => ({
link: item.uri,
title: item.title || item.content_text,
- description: item.content + (item.content_more ?? ''),
pubDate: parseDate(item.display_time * 1000),
author: item.author?.display_name ?? '',
+ description: art(path.join(__dirname, 'templates/description.art'), {
+ description: item.content,
+ more: item.content_more,
+ images: item.images,
+ }),
}));
ctx.state.data = {
diff --git a/lib/v2/wallstreetcn/templates/description.art b/lib/v2/wallstreetcn/templates/description.art
new file mode 100644
index 0000000000000..ee1f9e6f22831
--- /dev/null
+++ b/lib/v2/wallstreetcn/templates/description.art
@@ -0,0 +1,11 @@
+{{ if description }}
+{{@ description }}
+{{ /if }}
+{{ if more }}
+{{@ more }}
+{{ /if }}
+{{ if images }}
+{{ each images image }}
+
+{{ /each }}
+{{ /if }}
\ No newline at end of file