diff --git a/lib/v2/141ppv/index.js b/lib/v2/141ppv/index.js
index 2789e8d567c997..95025446459957 100644
--- a/lib/v2/141ppv/index.js
+++ b/lib/v2/141ppv/index.js
@@ -39,7 +39,10 @@ module.exports = async (ctx) => {
.map((t) => $(t).text().trim());
const magnet = item.find('a[title="Magnet torrent"]').attr('href');
const link = item.find('a[title="Download .torrent"]').attr('href');
- const image = item.find('.image').attr('src');
+ const onErrorAttr = item.find('.image').attr('onerror');
+ const backupImageRegex = /this\.src='(.*?)'/;
+ const match = backupImageRegex.exec(onErrorAttr);
+ const image = match ? match[1] : item.find('.image').attr('src');
return {
title: `${id} ${size}`,
diff --git a/lib/v2/ainvest/radar.js b/lib/v2/ainvest/radar.js
index a81c7dc87a6515..0fd1432c658e11 100644
--- a/lib/v2/ainvest/radar.js
+++ b/lib/v2/ainvest/radar.js
@@ -4,13 +4,13 @@ module.exports = {
'.': [
{
title: 'Latest Article',
- docs: 'https://docs.rsshub.app/finance#ainvest',
+ docs: 'https://docs.rsshub.app/routes/finance#ainvest',
source: ['/news'],
target: '/ainvest/article',
},
{
title: 'Latest News',
- docs: 'https://docs.rsshub.app/finance#ainvest',
+ docs: 'https://docs.rsshub.app/routes/finance#ainvest',
source: ['/news'],
target: '/ainvest/news',
},
diff --git a/lib/v2/cas/radar.js b/lib/v2/cas/radar.js
index 10f812784f6ed3..af51f17b8c1105 100644
--- a/lib/v2/cas/radar.js
+++ b/lib/v2/cas/radar.js
@@ -12,7 +12,7 @@ module.exports = {
'www.genetics': [
{
title: '遗传与生物学研究所 - 学术活动',
- docs: 'https://docs.rsshub.app/university.html#zhong-guo-ke-xue-yuan',
+ docs: 'https://docs.rsshub.app/routes/university#zhong-guo-ke-xue-yuan',
source: ['/jixs/yg', '/'],
target: '/cas/genetics/xshd',
},
diff --git a/lib/v2/economist/full.js b/lib/v2/economist/full.js
index 4ad4262a88655e..93683606096ef4 100644
--- a/lib/v2/economist/full.js
+++ b/lib/v2/economist/full.js
@@ -9,7 +9,9 @@ const getArticleDetail = (link, ctx) =>
$('div.article-audio-player__center-tooltip').remove();
const nextData = JSON.parse($('head script[type="application/ld+json"]').first().text());
- const article = ($('figure[class^=css-]').first().parent().parent().html() || '') + $('p.article__body-text').parent().html();
+ const figure = $('figure[class^=css-]').first().parent().parent().html() || '';
+ const body = $('p[data-component="paragraph"]').parent().parent().html();
+ const article = figure + body;
const categories = nextData.keywords?.map((k) => k);
return { article, categories };
@@ -20,7 +22,7 @@ module.exports = async (ctx) => {
const feed = await parser.parseURL(`https://www.economist.com/${endpoint}/rss.xml`);
const items = await Promise.all(
- feed.items.map(async (item) => {
+ feed.items.slice(0, ctx.query.limit ? parseInt(ctx.query.limit, 10) : 30).map(async (item) => {
const path = item.link.slice(item.link.lastIndexOf('/') + 1);
const isNotCollection = !/^\d{4}-\d{2}-\d{2}$/.test(path);
const itemDetails = isNotCollection ? await getArticleDetail(item.link, ctx) : null;
diff --git a/lib/v2/finology/radar.js b/lib/v2/finology/radar.js
index b8a2cc5e72a467..9ec1a03fc7440b 100644
--- a/lib/v2/finology/radar.js
+++ b/lib/v2/finology/radar.js
@@ -4,25 +4,25 @@ module.exports = {
insider: [
{
title: 'Bullets',
- docs: 'https://docs.rsshub.app/en/routes/finance#finology-insider',
+ docs: 'https://docs.rsshub.app/routes/finance#finology-insider',
source: ['/bullets'],
target: '/finology/bullets',
},
{
title: 'Category',
- docs: 'https://docs.rsshub.app/en/routes/finance#finology-insider',
+ docs: 'https://docs.rsshub.app/routes/finance#finology-insider',
source: '/:category',
target: '/finology/:category',
},
{
title: 'Most Viewed',
- docs: 'https://docs.rsshub.app/en/routes/finance#finology-insider',
+ docs: 'https://docs.rsshub.app/routes/finance#finology-insider',
source: '/most-viewed',
target: '/finology/most-viewed/monthly',
},
{
title: 'Trending Topic',
- docs: 'https://docs.rsshub.app/en/routes/finance#finology-insider',
+ docs: 'https://docs.rsshub.app/routes/finance#finology-insider',
source: ['/tag/:topic'],
target: '/finology/tag/:topic',
},
diff --git a/lib/v2/gov/chongqing/sydwgkzp.js b/lib/v2/gov/chongqing/sydwgkzp.js
new file mode 100644
index 00000000000000..0ee3d97ecc8062
--- /dev/null
+++ b/lib/v2/gov/chongqing/sydwgkzp.js
@@ -0,0 +1,47 @@
+const got = require('@/utils/got');
+const cheerio = require('cheerio');
+const { parseDate } = require('@/utils/parse-date');
+
+// 重庆市事业单位公开招聘
+const sydwgkzpUrl = 'https://rlsbj.cq.gov.cn/zwxx_182/sydw/';
+
+module.exports = async (ctx) => {
+ const { data: response } = await got(sydwgkzpUrl);
+
+ const $ = cheerio.load(response);
+
+ // 获取所有的标题
+ const list = $('div.page-list .tab-item > li')
+ .toArray()
+ .map((item) => {
+ item = $(item);
+ const title = item.find('a').first();
+ return {
+ // 文章标题
+ title: title.text(),
+ // 文章链接
+ link: `${sydwgkzpUrl}${title.attr('href')}`,
+ // 文章发布日期
+ pubDate: parseDate(item.find('span').text()),
+ };
+ });
+
+ // 获取每个通知的具体信息
+ 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.description = $('div[class="view TRS_UEDITOR trs_paper_default trs_web"]').first().html();
+ return item;
+ })
+ )
+ );
+
+ ctx.state.data = {
+ title: '重庆市事业单位公开招聘',
+ link: sydwgkzpUrl,
+ item: items,
+ };
+};
diff --git a/lib/v2/gov/maintainer.js b/lib/v2/gov/maintainer.js
index 58a5704316287a..c2b921dfe88e4b 100644
--- a/lib/v2/gov/maintainer.js
+++ b/lib/v2/gov/maintainer.js
@@ -54,6 +54,7 @@ module.exports = {
'/beijing/jw/tzgg': ['nczitzk'],
'/beijing/kw/:channel': ['Fatpandac'],
'/chongqing/rsks': ['Mai19930513'],
+ '/chongqing/sydwgkzp': ['MajexH'],
'/dianbai/:path+': ['ShuiHuo'],
'/gaozhou/:path+': ['ShuiHuo'],
'/guangdong/tqyb/sncsyjxh': ['Fatpandac'],
diff --git a/lib/v2/gov/radar.js b/lib/v2/gov/radar.js
index fb357bdb4e3ef0..e846eeff047563 100644
--- a/lib/v2/gov/radar.js
+++ b/lib/v2/gov/radar.js
@@ -437,6 +437,12 @@ module.exports = {
source: ['/'],
target: '/gov/chongqing/rsks',
},
+ {
+ title: '重庆事业单位公开招聘',
+ docs: 'https://docs.rsshub.app/routes/government#chong-qing-shi-ren-min-zheng-fu',
+ source: ['/'],
+ target: '/gov/chongqing/sydwgkzp',
+ },
],
},
'csrc.gov.cn': {
diff --git a/lib/v2/gov/router.js b/lib/v2/gov/router.js
index 3205f67b3f8820..3ab2f03d95f4b5 100644
--- a/lib/v2/gov/router.js
+++ b/lib/v2/gov/router.js
@@ -46,6 +46,7 @@ module.exports = function (router) {
router.get('/beijing/jw/tzgg', require('./beijing/jw/tzgg'));
router.get('/beijing/kw/:channel', require('./beijing/kw/index'));
router.get('/chongqing/rsks', require('./chongqing/rsks'));
+ router.get('/chongqing/sydwgkzp', require('./chongqing/sydwgkzp'));
router.get(/dianbai(\/[\w/-]+)?/, require('./dianbai/dianbai'));
router.get(/gaozhou(\/[\w/-]+)?/, require('./gaozhou/gaozhou'));
router.get('/guangdong/tqyb/sncsyjxh', require('./guangdong/tqyb/sncsyjxh'));
diff --git a/lib/v2/grist/radar.js b/lib/v2/grist/radar.js
index 03d7915c63088e..e8119724b7cd40 100644
--- a/lib/v2/grist/radar.js
+++ b/lib/v2/grist/radar.js
@@ -4,25 +4,25 @@ module.exports = {
'.': [
{
title: 'Articles',
- docs: 'https://docs.rsshub.app/en/routes/new-media#grist',
+ docs: 'https://docs.rsshub.app/routes/new-media#grist',
source: ['/articles/'],
target: '/grist/',
},
{
title: 'Featured',
- docs: 'https://docs.rsshub.app/en/routes/new-media#grist',
+ docs: 'https://docs.rsshub.app/routes/new-media#grist',
source: '/',
target: '/grist/featured',
},
{
title: 'Series',
- docs: 'https://docs.rsshub.app/en/routes/new-media#grist',
+ docs: 'https://docs.rsshub.app/routes/new-media#grist',
source: '/series/:series',
target: '/grist/series/:series',
},
{
title: 'Topic',
- docs: 'https://docs.rsshub.app/en/routes/new-media#grist',
+ docs: 'https://docs.rsshub.app/routes/new-media#grist',
source: ['/:topic'],
target: '/grist/topic/:topic',
},
diff --git a/lib/v2/icbc/radar.js b/lib/v2/icbc/radar.js
index 81641ba6155a0b..dd2acec0da567c 100644
--- a/lib/v2/icbc/radar.js
+++ b/lib/v2/icbc/radar.js
@@ -4,7 +4,7 @@ module.exports = {
'.': [
{
title: '外汇牌价',
- docs: 'https://docs.rsshub.app/other#zhong-guo-gong-shang-yin-hang',
+ docs: 'https://docs.rsshub.app/routes/other#zhong-guo-gong-shang-yin-hang',
source: ['/column/1438058341489590354.html'],
target: '/icbc/whpj',
},
diff --git a/lib/v2/liulinblog/radar.js b/lib/v2/liulinblog/radar.js
index 15c9433b5cdf06..0c305d835e45f2 100644
--- a/lib/v2/liulinblog/radar.js
+++ b/lib/v2/liulinblog/radar.js
@@ -4,7 +4,7 @@ module.exports = {
'.': [
{
title: '频道',
- docs: 'https://docs.rsshub.app/new-media.html#mu-mu-bo-ke',
+ docs: 'https://docs.rsshub.app/routes/new-media#mu-mu-bo-ke',
source: ['/:channel', '/'],
target: (params, url) => {
url = new URL(url);
@@ -15,31 +15,31 @@ module.exports = {
},
{
title: '标签',
- docs: 'https://docs.rsshub.app/new-media.html#mu-mu-bo-ke',
+ docs: 'https://docs.rsshub.app/routes/new-media#mu-mu-bo-ke',
source: ['/tag/:id', '/'],
target: '/liulinblog/tag/:id',
},
{
title: '专题',
- docs: 'https://docs.rsshub.app/new-media.html#mu-mu-bo-ke',
+ docs: 'https://docs.rsshub.app/routes/new-media#mu-mu-bo-ke',
source: ['/series/:id', '/'],
target: '/liulinblog/series/:id',
},
{
title: '搜索',
- docs: 'https://docs.rsshub.app/new-media.html#mu-mu-bo-ke',
+ docs: 'https://docs.rsshub.app/routes/new-media#mu-mu-bo-ke',
source: ['/search/:keyword', '/'],
target: '/liulinblog/search/:keyword',
},
{
title: '60秒读懂世界',
- docs: 'https://docs.rsshub.app/new-media.html#mu-mu-bo-ke',
+ docs: 'https://docs.rsshub.app/routes/new-media#mu-mu-bo-ke',
source: ['/kuaixun', '/'],
target: '/liulinblog/kuaixun',
},
{
title: '网络营销',
- docs: 'https://docs.rsshub.app/new-media.html#mu-mu-bo-ke',
+ docs: 'https://docs.rsshub.app/routes/new-media#mu-mu-bo-ke',
source: ['/:channel', '/'],
target: (params, url) => {
url = new URL(url);
diff --git a/lib/v2/lsnu/radar.js b/lib/v2/lsnu/radar.js
index f028fb3495d5a8..5c78b792f1d2f3 100644
--- a/lib/v2/lsnu/radar.js
+++ b/lib/v2/lsnu/radar.js
@@ -4,7 +4,7 @@ module.exports = {
'.': [
{
title: '教学部通知公告',
- docs: 'https://docs.rsshub.app/university.html#le-shan-shi-fan-xue-yuan',
+ docs: 'https://docs.rsshub.app/routes/university#le-shan-shi-fan-xue-yuan',
source: ['/'],
target: '/lsnu/jiaowc/tzgg',
},
diff --git a/lib/v2/niaogebiji/radar.js b/lib/v2/niaogebiji/radar.js
index a7ac4efc93988a..9a1c49a058a5f6 100644
--- a/lib/v2/niaogebiji/radar.js
+++ b/lib/v2/niaogebiji/radar.js
@@ -4,19 +4,19 @@ module.exports = {
'.': [
{
title: '首页',
- docs: 'https://docs.rsshub.app/new-media#niao-ge-bi-ji',
+ docs: 'https://docs.rsshub.app/routes/new-media#niao-ge-bi-ji',
source: ['/'],
target: '/niaogebiji',
},
{
title: '今日事',
- docs: 'https://docs.rsshub.app/new-media#niao-ge-bi-ji',
+ docs: 'https://docs.rsshub.app/routes/new-media#niao-ge-bi-ji',
source: ['/', '/bulletin'],
target: '/niaogebiji',
},
{
title: '分类目录',
- docs: 'https://docs.rsshub.app/new-media#niao-ge-bi-ji',
+ docs: 'https://docs.rsshub.app/routes/new-media#niao-ge-bi-ji',
source: ['/cat/:cat'],
target: '/niaogebiji/cat/:cat',
},
diff --git a/lib/v2/oreno3d/radar.js b/lib/v2/oreno3d/radar.js
index d99f4f59bd93b8..31bb9651023565 100644
--- a/lib/v2/oreno3d/radar.js
+++ b/lib/v2/oreno3d/radar.js
@@ -4,7 +4,7 @@ module.exports = {
'.': [
{
title: '关键词搜索',
- docs: 'https://docs.rsshub.app/anime.htm#an-の3dエロ-dong-hua-oreno3d-guan-jian-ci-sou-suo',
+ docs: 'https://docs.rsshub.app/routes/anime#an-の3dエロ-dong-hua-oreno3d-guan-jian-ci-sou-suo',
source: ['/search'],
target: (_params, url) => {
const searchParams = new URL(url).searchParams;
diff --git a/lib/v2/sourceforge/index.js b/lib/v2/sourceforge/index.js
new file mode 100644
index 00000000000000..af4daf59bd75bd
--- /dev/null
+++ b/lib/v2/sourceforge/index.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 routeParams = ctx.params.routeParams;
+
+ const baseURL = 'https://sourceforge.net';
+ const link = `https://sourceforge.net/directory/?${routeParams.toString()}`;
+
+ const response = await got.get(link);
+ const $ = cheerio.load(response.data);
+ const itemList = $('ul.projects li[itemprop=itemListElement]');
+
+ ctx.state.data = {
+ title: $('.content h1').text().trim(),
+ link,
+ item: itemList.toArray().map((element) => {
+ const item = $(element);
+ const title = item.find('.result-heading-title').text().trim();
+ const link = `${baseURL}${item.find('.result-heading-title').attr('href')}`;
+ const description = item.find('.result-heading-texts').html();
+ const pubDate = parseDate(item.find('time').attr('datetime'), 'YYYY-MM-DD');
+
+ return {
+ title,
+ link,
+ description,
+ pubDate,
+ };
+ }),
+ };
+};
diff --git a/lib/v2/sourceforge/maintainer.js b/lib/v2/sourceforge/maintainer.js
new file mode 100644
index 00000000000000..c5843ab97be053
--- /dev/null
+++ b/lib/v2/sourceforge/maintainer.js
@@ -0,0 +1,3 @@
+module.exports = {
+ '/:routeParams?': ['JimenezLi'],
+};
diff --git a/lib/v2/sourceforge/radar.js b/lib/v2/sourceforge/radar.js
new file mode 100644
index 00000000000000..a38640319709b9
--- /dev/null
+++ b/lib/v2/sourceforge/radar.js
@@ -0,0 +1,12 @@
+module.exports = {
+ 'sourceforge.net': {
+ _name: 'SourceForge',
+ www: [
+ {
+ title: 'Software',
+ docs: 'https://docs.rsshub.app/routes/program-update#sourceforge',
+ source: '/directory',
+ },
+ ],
+ },
+};
diff --git a/lib/v2/sourceforge/router.js b/lib/v2/sourceforge/router.js
new file mode 100644
index 00000000000000..4e4a3fe0bf65b4
--- /dev/null
+++ b/lib/v2/sourceforge/router.js
@@ -0,0 +1,3 @@
+module.exports = (router) => {
+ router.get('/:routeParams?', require('./index'));
+};
diff --git a/lib/v2/sqmc/radar.js b/lib/v2/sqmc/radar.js
index 0b10ff0c4a8699..d5e1620cefe00c 100644
--- a/lib/v2/sqmc/radar.js
+++ b/lib/v2/sqmc/radar.js
@@ -4,7 +4,7 @@ module.exports = {
'.': [
{
title: '官网信息',
- docs: 'https://docs.rsshub.app/university.html#xin-xiang-yi-xue-yuan-san-quan-xue-yuan',
+ docs: 'https://docs.rsshub.app/routes/university#xin-xiang-yi-xue-yuan-san-quan-xue-yuan',
source: ['/:category/list.htm'],
target: '/sqmc/www/:category?',
},
diff --git a/lib/v2/surfshark/blog.js b/lib/v2/surfshark/blog.js
new file mode 100644
index 00000000000000..674d3ba49d5ba9
--- /dev/null
+++ b/lib/v2/surfshark/blog.js
@@ -0,0 +1,91 @@
+const got = require('@/utils/got');
+const cheerio = require('cheerio');
+const { parseDate } = require('@/utils/parse-date');
+const { art } = require('@/utils/render');
+const path = require('path');
+
+module.exports = async (ctx) => {
+ const { category } = ctx.params;
+ const limit = ctx.query.limit ? parseInt(ctx.query.limit, 10) : 15;
+
+ const rootUrl = 'https://surfshark.com';
+ const currentUrl = new URL(`blog${category ? `/${category}` : ''}`, rootUrl).href;
+
+ const { data: response } = await got(currentUrl);
+
+ const $ = cheerio.load(response);
+
+ let items = $('div.article-info')
+ .slice(0, limit)
+ .toArray()
+ .map((item) => {
+ item = $(item);
+
+ const a = item.find('a');
+
+ return {
+ title: a.prop('title'),
+ link: a.prop('href'),
+ author: item.find('div.author, div.name').text().split('in')[0].trim(),
+ category: item
+ .find('div.author, div.name')
+ .find('a')
+ .toArray()
+ .map((c) => $(c).text()),
+ pubDate: parseDate(item.find('div.date, div.time').text().split('·')[0].trim(), 'YYYY, MMMM D'),
+ };
+ });
+
+ items = await Promise.all(
+ items.map((item) =>
+ ctx.cache.tryGet(item.link, async () => {
+ const { data: detailResponse } = await got(item.link);
+
+ const content = cheerio.load(detailResponse);
+
+ content('div.post-main-img').each(function () {
+ const image = content(this).find('img');
+
+ content(this).replaceWith(
+ art(path.join(__dirname, 'templates/description.art'), {
+ image: {
+ src: image
+ .prop('srcset')
+ .match(/(https?:.*?\.\w+\s)/g)
+ .pop()
+ .trim(),
+ alt: image.prop('alt'),
+ },
+ })
+ );
+ });
+
+ item.title = content('div.blog-post-title').text();
+ item.description = content('div.post-content').html();
+ item.author = content('meta[name="author"]').prop('content');
+ item.category = content('div.name')
+ .find('a')
+ .toArray()
+ .map((c) => content(c).text());
+ item.pubDate = parseDate(content('meta[property="article:published_time"]').prop('content'));
+
+ return item;
+ })
+ )
+ );
+
+ const icon = new URL($('link[rel="apple-touch-icon"]').prop('href'), rootUrl).href;
+
+ ctx.state.data = {
+ item: items,
+ title: $('title').text(),
+ link: currentUrl,
+ description: $('meta[property="og:description"]').prop('content'),
+ language: $('html').prop('lang'),
+ image: $('meta[property="og:image"]').prop('content'),
+ icon,
+ logo: icon,
+ subtitle: $('meta[property="og:title"]').prop('content'),
+ author: $('meta[property="og:site_name"]').prop('content'),
+ };
+};
diff --git a/lib/v2/surfshark/maintainer.js b/lib/v2/surfshark/maintainer.js
new file mode 100644
index 00000000000000..d8eab4f114cb81
--- /dev/null
+++ b/lib/v2/surfshark/maintainer.js
@@ -0,0 +1,3 @@
+module.exports = {
+ '/blog/:category?': ['nczitzk'],
+};
diff --git a/lib/v2/surfshark/radar.js b/lib/v2/surfshark/radar.js
new file mode 100644
index 00000000000000..4108f82db578fe
--- /dev/null
+++ b/lib/v2/surfshark/radar.js
@@ -0,0 +1,90 @@
+module.exports = {
+ 'surfshark.com': {
+ _name: 'Surfshark',
+ '.': [
+ {
+ title: 'Blog',
+ docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog',
+ source: ['/blog/blog/:category*'],
+ target: (params, url) => {
+ url = new URL(url);
+ const path = params.category ?? url.href.match(/\/blog\/(.*?)/)[1];
+
+ return `/surfshark/blog${path ? `/${path}` : ''}`;
+ },
+ },
+ {
+ title: 'Blog - Cybersecurity',
+ docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog',
+ source: ['/blog/cybersecurity'],
+ target: '/surfshark/blog/cybersecurity',
+ },
+ {
+ title: 'Blog - All things VPN',
+ docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog',
+ source: ['/blog/all-things-vpn'],
+ target: '/surfshark/blog/all-things-vpn',
+ },
+ {
+ title: 'Blog - Internet censorship',
+ docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog',
+ source: ['/blog/internet-censorship'],
+ target: '/surfshark/blog/internet-censorship',
+ },
+ {
+ title: 'Blog - Entertainment',
+ docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog',
+ source: ['/blog/entertainment'],
+ target: '/surfshark/blog/entertainment',
+ },
+ {
+ title: 'Blog - News',
+ docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog',
+ source: ['/blog/news'],
+ target: '/surfshark/blog/news',
+ },
+ {
+ title: 'Blog - Internet Security',
+ docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog',
+ source: ['/blog/cybersecurity/internet-security', '/blog/cybersecurity'],
+ target: '/surfshark/blog/cybersecurity/internet-security',
+ },
+ {
+ title: 'Blog - Mobile Security',
+ docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog',
+ source: ['/blog/cybersecurity/mobile-security', '/blog/cybersecurity'],
+ target: '/surfshark/blog/cybersecurity/mobile-security',
+ },
+ {
+ title: 'Blog - Identity Protection',
+ docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog',
+ source: ['/blog/cybersecurity/identity-protection', '/blog/cybersecurity'],
+ target: '/surfshark/blog/cybersecurity/identity-protection',
+ },
+ {
+ title: 'Blog - Phishing',
+ docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog',
+ source: ['/blog/cybersecurity/phishing', '/blog/cybersecurity'],
+ target: '/surfshark/blog/cybersecurity/phishing',
+ },
+ {
+ title: 'Blog - Must-knows',
+ docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog',
+ source: ['/blog/all-things-vpn/must-knows', '/blog/all-things-vpn'],
+ target: '/surfshark/blog/all-things-vpn/must-knows',
+ },
+ {
+ title: 'Blog - Technology',
+ docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog',
+ source: ['/blog/all-things-vpn/technology', '/blog/all-things-vpn'],
+ target: '/surfshark/blog/all-things-vpn/technology',
+ },
+ {
+ title: 'Blog - Tips & Advice',
+ docs: 'https://docs.rsshub.app/routes/blog#surfshark-blog',
+ source: ['/blog/all-things-vpn/tips-and-advice', '/blog/all-things-vpn'],
+ target: '/surfshark/blog/all-things-vpn/tips-and-advice',
+ },
+ ],
+ },
+};
diff --git a/lib/v2/surfshark/router.js b/lib/v2/surfshark/router.js
new file mode 100644
index 00000000000000..fff6a211fa8434
--- /dev/null
+++ b/lib/v2/surfshark/router.js
@@ -0,0 +1,3 @@
+module.exports = (router) => {
+ router.get('/blog/:category*', require('./blog'));
+};
diff --git a/lib/v2/surfshark/templates/description.art b/lib/v2/surfshark/templates/description.art
new file mode 100644
index 00000000000000..16a5adf2bac7e3
--- /dev/null
+++ b/lib/v2/surfshark/templates/description.art
@@ -0,0 +1,5 @@
+{{ if image }}
+
+{{ /if }}
\ No newline at end of file
diff --git a/lib/v2/thwiki/radar.js b/lib/v2/thwiki/radar.js
index ec2d7a4d66bf1f..492377bd7bea0a 100644
--- a/lib/v2/thwiki/radar.js
+++ b/lib/v2/thwiki/radar.js
@@ -4,7 +4,7 @@ module.exports = {
'.': [
{
title: '日历',
- docs: 'https://docs.rsshub.app/#thbwiki',
+ docs: 'https://docs.rsshub.app/routes/anime#thbwiki',
source: ['/', '/日程表'],
target: '/thwiki/calendar',
},
diff --git a/lib/v2/xiaote/radar.js b/lib/v2/xiaote/radar.js
index bb847657094bc6..ed045a4c642ff7 100644
--- a/lib/v2/xiaote/radar.js
+++ b/lib/v2/xiaote/radar.js
@@ -4,7 +4,7 @@ module.exports = {
'.': [
{
title: '首页帖子',
- docs: 'https://docs.rsshub.app/bbs#xiao-te-she-qu',
+ docs: 'https://docs.rsshub.app/routes/bbs#xiao-te-she-qu',
source: ['/'],
target: '/xiaote/news',
},
diff --git a/lib/v2/yyets/radar.js b/lib/v2/yyets/radar.js
index 1a6556042fb80e..577c81acd82671 100644
--- a/lib/v2/yyets/radar.js
+++ b/lib/v2/yyets/radar.js
@@ -4,13 +4,13 @@ module.exports = {
'.': [
{
title: '影视资讯',
- docs: 'https://docs.rsshub.app/multimedia#ren-ren-ying-shi',
+ docs: 'https://docs.rsshub.app/routes/multimedia#ren-ren-ying-shi',
source: '/article',
target: (_params, url) => `/yyets/article${new URL(url).searchParams.has('type') ? '/' + new URL(url).searchParams.get('type') : ''}`,
},
{
title: '今日播出',
- docs: 'https://docs.rsshub.app/multimedia#ren-ren-ying-shi',
+ docs: 'https://docs.rsshub.app/routes/multimedia#ren-ren-ying-shi',
source: ['/tv/schedule', '/'],
target: '/yyets/today',
},
diff --git a/package.json b/package.json
index 44b5fc8478307a..02d9acc0c493a3 100644
--- a/package.json
+++ b/package.json
@@ -128,7 +128,7 @@
"pidusage": "3.0.2",
"plist": "3.1.0",
"proxy-chain": "2.3.0",
- "puppeteer": "21.3.1",
+ "puppeteer": "21.3.4",
"puppeteer-extra": "3.3.6",
"puppeteer-extra-plugin-stealth": "2.11.2",
"puppeteer-extra-plugin-user-data-dir": "2.4.1",
@@ -150,7 +150,7 @@
},
"devDependencies": {
"@microsoft/eslint-formatter-sarif": "3.0.0",
- "@types/aes-js": "3.1.1",
+ "@types/aes-js": "3.1.2",
"@types/crypto-js": "4.1.2",
"@types/eslint": "8.44.2",
"@types/eslint-config-prettier": "6.11.0",
@@ -181,7 +181,7 @@
"@types/tough-cookie": "4.0.3",
"@vercel/nft": "0.24.1",
"cross-env": "7.0.3",
- "eslint": "8.49.0",
+ "eslint": "8.50.0",
"eslint-config-prettier": "9.0.0",
"eslint-plugin-n": "16.1.0",
"eslint-plugin-prettier": "5.0.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6021f8d0885c77..931eb84925f1f5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -141,11 +141,11 @@ dependencies:
specifier: 2.3.0
version: 2.3.0
puppeteer:
- specifier: 21.3.1
- version: 21.3.1
+ specifier: 21.3.4
+ version: 21.3.4
puppeteer-extra:
specifier: 3.3.6
- version: 3.3.6(puppeteer@21.3.1)
+ version: 3.3.6(puppeteer@21.3.4)
puppeteer-extra-plugin-stealth:
specifier: 2.11.2
version: 2.11.2(puppeteer-extra@3.3.6)
@@ -203,8 +203,8 @@ devDependencies:
specifier: 3.0.0
version: 3.0.0
'@types/aes-js':
- specifier: 3.1.1
- version: 3.1.1
+ specifier: 3.1.2
+ version: 3.1.2
'@types/crypto-js':
specifier: 4.1.2
version: 4.1.2
@@ -296,20 +296,20 @@ devDependencies:
specifier: 7.0.3
version: 7.0.3
eslint:
- specifier: 8.49.0
- version: 8.49.0
+ specifier: 8.50.0
+ version: 8.50.0
eslint-config-prettier:
specifier: 9.0.0
- version: 9.0.0(eslint@8.49.0)
+ version: 9.0.0(eslint@8.50.0)
eslint-plugin-n:
specifier: 16.1.0
- version: 16.1.0(eslint@8.49.0)
+ version: 16.1.0(eslint@8.50.0)
eslint-plugin-prettier:
specifier: 5.0.0
- version: 5.0.0(@types/eslint@8.44.2)(eslint-config-prettier@9.0.0)(eslint@8.49.0)(prettier@3.0.3)
+ version: 5.0.0(@types/eslint@8.44.2)(eslint-config-prettier@9.0.0)(eslint@8.50.0)(prettier@3.0.3)
eslint-plugin-yml:
specifier: 1.9.0
- version: 1.9.0(eslint@8.49.0)
+ version: 1.9.0(eslint@8.50.0)
fs-extra:
specifier: 11.1.1
version: 11.1.1
@@ -739,13 +739,13 @@ packages:
kuler: 2.0.0
dev: false
- /@eslint-community/eslint-utils@4.4.0(eslint@8.49.0):
+ /@eslint-community/eslint-utils@4.4.0(eslint@8.50.0):
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
dependencies:
- eslint: 8.49.0
+ eslint: 8.50.0
eslint-visitor-keys: 3.4.3
dev: true
@@ -771,8 +771,8 @@ packages:
- supports-color
dev: true
- /@eslint/js@8.49.0:
- resolution: {integrity: sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==}
+ /@eslint/js@8.50.0:
+ resolution: {integrity: sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
@@ -1096,7 +1096,7 @@ packages:
resolution: {integrity: sha512-KIKkT44hEqCzqxODYwFMUvYEK0CrdHx/Ll9xiOWgFbBSRuzbxmVy4d/tzfgoucGz72HJZNOMjuyzFTBKntRK5Q==}
engines: {node: '>= 14'}
dependencies:
- eslint: 8.49.0
+ eslint: 8.50.0
jschardet: 3.0.0
lodash: 4.17.21
utf8: 3.0.0
@@ -1336,8 +1336,8 @@ packages:
'@types/node': 20.5.6
dev: true
- /@types/aes-js@3.1.1:
- resolution: {integrity: sha512-SDSGgXT3LRCH6qMWk8OHT1vLSVNuHNvCpKCx2/TYtQMbMGGgxJC9fspwSkQjqzRagrWnCrxuLL3jMNXLXHHvSw==}
+ /@types/aes-js@3.1.2:
+ resolution: {integrity: sha512-bKms4q1H3YCcLMKvqE9kUefE1TXc9/QhurzMRVqY9Qevodl0Fxyt6GQcUQ4TkmuWLdGQM1xL+yhSUBTJJRfBFA==}
dev: true
/@types/babel__core@7.20.1:
@@ -2428,8 +2428,8 @@ packages:
engines: {node: '>=10'}
dev: true
- /chromium-bidi@0.4.27(devtools-protocol@0.0.1179426):
- resolution: {integrity: sha512-8Irq0FbKYN8Xmj8M62kta6wk5MyDKeYIFtNavxQ2M3xf2v5MCC4ntf+FxitQu1iHaQvGU6t5O+Nrep0RNNS0EQ==}
+ /chromium-bidi@0.4.28(devtools-protocol@0.0.1179426):
+ resolution: {integrity: sha512-2HZ74QlAApJrEwcGlU/sUu0s4VS+FI3CJ09Toc9aE9VemMyhHZXeaROQgJKNRaYMUTUx6qIv1cLBs3F+vfgjSw==}
peerDependencies:
devtools-protocol: '*'
dependencies:
@@ -3157,36 +3157,36 @@ packages:
source-map: 0.6.1
dev: false
- /eslint-config-prettier@9.0.0(eslint@8.49.0):
+ /eslint-config-prettier@9.0.0(eslint@8.50.0):
resolution: {integrity: sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==}
hasBin: true
peerDependencies:
eslint: '>=7.0.0'
dependencies:
- eslint: 8.49.0
+ eslint: 8.50.0
dev: true
- /eslint-plugin-es-x@7.2.0(eslint@8.49.0):
+ /eslint-plugin-es-x@7.2.0(eslint@8.50.0):
resolution: {integrity: sha512-9dvv5CcvNjSJPqnS5uZkqb3xmbeqRLnvXKK7iI5+oK/yTusyc46zbBZKENGsOfojm/mKfszyZb+wNqNPAPeGXA==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
eslint: '>=8'
dependencies:
- '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0)
+ '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0)
'@eslint-community/regexpp': 4.8.0
- eslint: 8.49.0
+ eslint: 8.50.0
dev: true
- /eslint-plugin-n@16.1.0(eslint@8.49.0):
+ /eslint-plugin-n@16.1.0(eslint@8.50.0):
resolution: {integrity: sha512-3wv/TooBst0N4ND+pnvffHuz9gNPmk/NkLwAxOt2JykTl/hcuECe6yhTtLJcZjIxtZwN+GX92ACp/QTLpHA3Hg==}
engines: {node: '>=16.0.0'}
peerDependencies:
eslint: '>=7.0.0'
dependencies:
- '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0)
+ '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0)
builtins: 5.0.1
- eslint: 8.49.0
- eslint-plugin-es-x: 7.2.0(eslint@8.49.0)
+ eslint: 8.50.0
+ eslint-plugin-es-x: 7.2.0(eslint@8.50.0)
get-tsconfig: 4.7.0
ignore: 5.2.4
is-core-module: 2.13.0
@@ -3195,7 +3195,7 @@ packages:
semver: 7.5.4
dev: true
- /eslint-plugin-prettier@5.0.0(@types/eslint@8.44.2)(eslint-config-prettier@9.0.0)(eslint@8.49.0)(prettier@3.0.3):
+ /eslint-plugin-prettier@5.0.0(@types/eslint@8.44.2)(eslint-config-prettier@9.0.0)(eslint@8.50.0)(prettier@3.0.3):
resolution: {integrity: sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
@@ -3210,21 +3210,21 @@ packages:
optional: true
dependencies:
'@types/eslint': 8.44.2
- eslint: 8.49.0
- eslint-config-prettier: 9.0.0(eslint@8.49.0)
+ eslint: 8.50.0
+ eslint-config-prettier: 9.0.0(eslint@8.50.0)
prettier: 3.0.3
prettier-linter-helpers: 1.0.0
synckit: 0.8.5
dev: true
- /eslint-plugin-yml@1.9.0(eslint@8.49.0):
+ /eslint-plugin-yml@1.9.0(eslint@8.50.0):
resolution: {integrity: sha512-ayuC57WyVQ5+QZ02y62GiB//5+zsiyzUGxUX/mrhLni+jfsKA4KoITjkbR65iUdjjhWpyTJHPcAIFLKQIOwgsw==}
engines: {node: ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: '>=6.0.0'
dependencies:
debug: 4.3.4
- eslint: 8.49.0
+ eslint: 8.50.0
lodash: 4.17.21
natural-compare: 1.4.0
yaml-eslint-parser: 1.2.2
@@ -3245,15 +3245,15 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
- /eslint@8.49.0:
- resolution: {integrity: sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==}
+ /eslint@8.50.0:
+ resolution: {integrity: sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
hasBin: true
dependencies:
- '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0)
+ '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0)
'@eslint-community/regexpp': 4.8.0
'@eslint/eslintrc': 2.1.2
- '@eslint/js': 8.49.0
+ '@eslint/js': 8.50.0
'@humanwhocodes/config-array': 0.11.11
'@humanwhocodes/module-importer': 1.0.1
'@nodelib/fs.walk': 1.2.8
@@ -6495,16 +6495,16 @@ packages:
resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
engines: {node: '>=6'}
- /puppeteer-core@21.3.1:
- resolution: {integrity: sha512-3VrCDEAHk0hPvE8qtfKgsT8CzRuaQrDQGXdCOuMFJM7Ap+ghpQzhPa9H3DE3gZgwDvC5Jt7SxUkAWLCeNbD0xw==}
+ /puppeteer-core@21.3.4:
+ resolution: {integrity: sha512-iaG7ScTXOm9hlsBTBGGtr5dAAsA8IiWTx8E0Ghr0b5Ntl42bdcPS8EXjcERKocDhua2YqdlnFGs/cBxHY+VNyA==}
engines: {node: '>=16.3.0'}
dependencies:
'@puppeteer/browsers': 1.7.1
- chromium-bidi: 0.4.27(devtools-protocol@0.0.1179426)
+ chromium-bidi: 0.4.28(devtools-protocol@0.0.1179426)
cross-fetch: 4.0.0
debug: 4.3.4
devtools-protocol: 0.0.1179426
- ws: 8.14.1
+ ws: 8.14.2
transitivePeerDependencies:
- bufferutil
- encoding
@@ -6525,7 +6525,7 @@ packages:
optional: true
dependencies:
debug: 4.3.4
- puppeteer-extra: 3.3.6(puppeteer@21.3.1)
+ puppeteer-extra: 3.3.6(puppeteer@21.3.4)
puppeteer-extra-plugin: 3.2.3(puppeteer-extra@3.3.6)
puppeteer-extra-plugin-user-preferences: 2.4.1(puppeteer-extra@3.3.6)
transitivePeerDependencies:
@@ -6546,7 +6546,7 @@ packages:
dependencies:
debug: 4.3.4
fs-extra: 10.1.0
- puppeteer-extra: 3.3.6(puppeteer@21.3.1)
+ puppeteer-extra: 3.3.6(puppeteer@21.3.4)
puppeteer-extra-plugin: 3.2.3(puppeteer-extra@3.3.6)
rimraf: 3.0.2
transitivePeerDependencies:
@@ -6567,7 +6567,7 @@ packages:
dependencies:
debug: 4.3.4
deepmerge: 4.3.1
- puppeteer-extra: 3.3.6(puppeteer@21.3.1)
+ puppeteer-extra: 3.3.6(puppeteer@21.3.4)
puppeteer-extra-plugin: 3.2.3(puppeteer-extra@3.3.6)
puppeteer-extra-plugin-user-data-dir: 2.4.1(puppeteer-extra@3.3.6)
transitivePeerDependencies:
@@ -6589,12 +6589,12 @@ packages:
'@types/debug': 4.1.8
debug: 4.3.4
merge-deep: 3.0.3
- puppeteer-extra: 3.3.6(puppeteer@21.3.1)
+ puppeteer-extra: 3.3.6(puppeteer@21.3.4)
transitivePeerDependencies:
- supports-color
dev: false
- /puppeteer-extra@3.3.6(puppeteer@21.3.1):
+ /puppeteer-extra@3.3.6(puppeteer@21.3.4):
resolution: {integrity: sha512-rsLBE/6mMxAjlLd06LuGacrukP2bqbzKCLzV1vrhHFavqQE/taQ2UXv3H5P0Ls7nsrASa+6x3bDbXHpqMwq+7A==}
engines: {node: '>=8'}
peerDependencies:
@@ -6612,19 +6612,19 @@ packages:
'@types/debug': 4.1.8
debug: 4.3.4
deepmerge: 4.3.1
- puppeteer: 21.3.1
+ puppeteer: 21.3.4
transitivePeerDependencies:
- supports-color
dev: false
- /puppeteer@21.3.1:
- resolution: {integrity: sha512-MhDvA+BYmzx+9vHJ/ZtknhlPbSPjTlHQnW1QYfkGpBcGW2Yy6eMahjkNuhAzN29H9tb47IcT0QsVcUy3Txx+SA==}
+ /puppeteer@21.3.4:
+ resolution: {integrity: sha512-kE67k1KR6hQs3g0Yf/i3GYOhTU8zC2dtcpHhtcSC9bGoVxRgqDo/hwVkDqlNKxJsJHuVX+qviWC7F0FdSjcFTA==}
engines: {node: '>=16.3.0'}
requiresBuild: true
dependencies:
'@puppeteer/browsers': 1.7.1
cosmiconfig: 8.3.6
- puppeteer-core: 21.3.1
+ puppeteer-core: 21.3.4
transitivePeerDependencies:
- bufferutil
- encoding
@@ -8019,8 +8019,8 @@ packages:
optional: true
dev: false
- /ws@8.14.1:
- resolution: {integrity: sha512-4OOseMUq8AzRBI/7SLMUwO+FEDnguetSk7KMb1sHwvF2w2Wv5Hoj0nlifx8vtGsftE/jWHojPy8sMMzYLJ2G/A==}
+ /ws@8.14.2:
+ resolution: {integrity: sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
diff --git a/website/docs/routes/blog.md b/website/docs/routes/blog.md
index 308991e56a1795..921bb87af9c3f5 100644
--- a/website/docs/routes/blog.md
+++ b/website/docs/routes/blog.md
@@ -264,6 +264,30 @@ Limit the number of entries to be retrieved by adding `?limit=x` to the end of t
+## Surfshark {#surfshark}
+
+### Blog {#surfshark-blog}
+
+
+
+| Home | Cybersecurity | All things VPN | Internet censorship | Entertainment | News |
+| ---- | ------------- | -------------- | ------------------- | ------------- | ---- |
+| | cybersecurity | all-things-vpn | internet-censorship | entertainment | news |
+
+#### Cybersecurity {#surfshark-blog-cybersecurity}
+
+| Internet Security | Mobile Security | Identity Protection | Phishing |
+| ------------------------------- | ----------------------------- | --------------------------------- | ---------------------- |
+| cybersecurity/internet-security | cybersecurity/mobile-security | cybersecurity/identity-protection | cybersecurity/phishing |
+
+#### All things VPN {#surfshark-blog-all-things-vpn}
+
+| Must-knows | Technology | Tips & Advice |
+| -------------- | -------------- | ------------------- |
+| vpn/must-knows | vpn/technology | vpn/tips-and-advice |
+
+
+
## Uber 优步 {#uber-you-bu}
### Engineering {#uber-you-bu-engineering}
diff --git a/website/docs/routes/government.md b/website/docs/routes/government.md
index a14f443b928beb..e6348b47eb8588 100644
--- a/website/docs/routes/government.md
+++ b/website/docs/routes/government.md
@@ -327,6 +327,10 @@ Language
+#### 事业单位公开招聘 {#chong-qing-shi-ren-min-zheng-fu-ren-li-she-bao-ju-shi-ye-dan-wei-gong-kai-zhao-pin}
+
+
+
## 德阳市人民政府 {#de-yang-shi-ren-min-zheng-fu}
### 政府公开信息 {#de-yang-shi-ren-min-zheng-fu-zheng-fu-gong-kai-xin-xi}
diff --git a/website/docs/routes/program-update.md b/website/docs/routes/program-update.md
index efe6afbf51ac7d..260e74a197e99e 100644
--- a/website/docs/routes/program-update.md
+++ b/website/docs/routes/program-update.md
@@ -638,6 +638,16 @@ Open `https://www.sony.com/electronics/support` and search for the corresponding
+## SourceForge {#sourceforge}
+
+
+
+For some URL like [https://sourceforge.net/directory/artificial-intelligence/windows/](https://sourceforge.net/directory/artificial-intelligence/windows/), it is equal to [https://sourceforge.net/directory/?topic=artificial-intelligence&os=windows"](https://sourceforge.net/directory/?topic=artificial-intelligence&os=windows), thus subscribing to `/sourceforge/topic=artificial-intelligence&os=windows`.
+
+URL params can duplicate, such as `/sourceforge/topic=artificial-intelligence&os=windows&os=linux`.
+
+
+
## Thunderbird {#thunderbird}
### Changelog {#thunderbird-changelog}