fix(route): duplicate-object-key (#15494)

* fix(route): duplicate-object-key

* fix: split routes

* fix: split routes

* fix: typo
This commit is contained in:
Tony 2024-05-07 05:29:08 +08:00 committed by GitHub
parent 7892a54431
commit 936e7032d2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
41 changed files with 495 additions and 420 deletions

View File

@ -0,0 +1,23 @@
import { Route } from '@/types';
import { handler } from './common';
export const route: Route = {
name: '分类',
path: ['/category/:id'],
example: '/cnbeta/category/movie',
maintainers: ['nczitzk'],
parameters: {
id: '分类 id可在对应分类页的 URL 中找到',
},
radar: [
{
source: ['cnbeta.com.tw/category/:id'],
target: (params) => `/cnbeta/category/${params.id.replace('.htm', '')}`,
},
],
handler,
url: 'cnbeta.com.tw',
description: `| 影视 | 音乐 | 游戏 | 动漫 | 趣闻 | 科学 | 软件 |
| ----- | ----- | ---- | ----- | ----- | ------- | ---- |
| movie | music | game | comic | funny | science | soft |`,
};

View File

@ -1,4 +1,3 @@
import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@ -7,22 +6,7 @@ import { parseDate } from '@/utils/parse-date';
import { rootUrl, ProcessItems } from './utils';
export const route: Route = {
path: ['/:type/:id', '/'],
radar: [
{
source: ['cnbeta.com.tw/'],
target: '',
},
],
name: 'Unknown',
maintainers: [],
handler,
url: 'cnbeta.com.tw/',
url: 'cnbeta.com.tw/',
};
async function handler(ctx) {
export async function handler(ctx) {
const { type, id } = ctx.req.param();
const currentUrl = type ? `${rootUrl}/${type}/${id}.htm` : rootUrl;

View File

@ -0,0 +1,16 @@
import { Route } from '@/types';
import { handler } from './common';
export const route: Route = {
name: '头条资讯',
path: ['/'],
example: '/cnbeta',
radar: [
{
source: ['cnbeta.com.tw/'],
},
],
maintainers: ['kt286', 'HaitianLiu', 'nczitzk'],
handler,
url: 'cnbeta.com.tw',
};

View File

@ -3,4 +3,5 @@ import type { Namespace } from '@/types';
export const namespace: Namespace = {
name: 'cnBeta.COM',
url: 'cnbeta.com.tw',
categories: ['new-media'],
};

View File

@ -0,0 +1,23 @@
import { Route } from '@/types';
import { handler } from './common';
export const route: Route = {
name: '主题',
path: ['/topics/:id'],
example: '/cnbeta/topics/453',
maintainers: ['cczhong11', 'nczitzk'],
parameters: {
id: '主题 id可在对应主题页的 URL 中找到',
},
radar: [
{
source: ['cnbeta.com.tw/topics/:id'],
target: (params) => `/cnbeta/topics/${params.id.replace('.htm', '')}`,
},
],
handler,
url: 'cnbeta.com.tw',
description: `::: tip
[](https://www.cnbeta.com.tw/topics.htm)
:::`,
};

View File

@ -29,8 +29,6 @@ export const route: Route = {
url: 'www.cnblogs.com/pick',
description: `在博客园主页的分类出可查看所有类型。例如go 的分类地址为: \`https://www.cnblogs.com/cate/go/\`, 则: [\`/cnblogs/cate/go\`](https://rsshub.app/cnblogs/cate/go)`,
url: 'www.cnblogs.com/aggsite/headline',
url: 'www.cnblogs.com/aggsite/topviews',
url: 'www.cnblogs.com/aggsite/topdiggs',
};
async function handler(ctx) {

View File

@ -2,7 +2,7 @@ import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import utils from './utils';
import { ProcessItem } from './utils';
const rootUrl = 'https://kns.cnki.net';
@ -70,7 +70,7 @@ async function handler(ctx) {
};
});
const items = await Promise.all(list.map((item) => cache.tryGet(item.link, () => utils.ProcessItem(item))));
const items = await Promise.all(list.map((item) => cache.tryGet(item.link, () => ProcessItem(item))));
return {
title: `知网 ${authorName} ${companyName}`,

View File

@ -24,4 +24,4 @@ const ProcessItem = async (item) => {
return item;
};
export default { ProcessItem };
export { ProcessItem };

View File

@ -6,7 +6,7 @@ import cache from '@/utils/cache';
import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';
import { getUuid, getBook, getChapter, getChapters, getImgEncrypted, getImgKey, decrypt, getRealKey, siteHost } from './utils';
import { getUuid, getBook, getChapter, getChapters, getImgEncrypted, getImgKey, decrypt, getRealKey, apiHost } from './utils';
export const route: Route = {
path: '/book/:id/:coverOnly?/:quality?',
@ -62,7 +62,7 @@ async function handler(ctx) {
const realKey = getRealKey(imgKey);
const encrypted = await getImgEncrypted(p.id, quality);
return cache.tryGet(`${siteHost}/fs/chapter_content/encrypt/${p.id}/${quality}`, () => decrypt(encrypted, realKey));
return cache.tryGet(`${apiHost}/fs/chapter_content/encrypt/${p.id}/${quality}`, () => decrypt(encrypted, realKey));
})
);
}

View File

@ -82,4 +82,4 @@ const getRealKey = (imgKey, token = DEFAULT_TOKEN) => {
};
};
export { getBook, getChapter, getChapters, getImgEncrypted, getImgKey, getUuid, decrypt, token2Key, getRealKey };
export { apiHost, getBook, getChapter, getChapters, getImgEncrypted, getImgKey, getUuid, decrypt, token2Key, getRealKey };

View File

@ -4,18 +4,17 @@ import got from '@/utils/got';
import { load } from 'cheerio';
export const route: Route = {
path: ['/all', '/'],
name: 'Award Winners',
example: '/darwinawards',
path: '/',
radar: [
{
source: ['darwinawards.com/darwin', 'darwinawards.com/'],
target: '',
},
],
name: 'Unknown',
maintainers: ['zoenglinghou', 'nczitzk'],
handler,
url: 'darwinawards.com/darwin',
url: 'darwinawards.com/darwin',
};
async function handler() {

View File

@ -3,4 +3,5 @@ import type { Namespace } from '@/types';
export const namespace: Namespace = {
name: 'Darwin Awards',
url: 'darwinawards.com',
categories: ['other'],
};

View File

@ -1,105 +0,0 @@
import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
import logger from '@/utils/logger';
export const route: Route = {
path: ['/:category', '/topic/:topic'],
categories: ['traditional-media'],
example: '/dnaindia/headlines',
parameters: { category: 'Find it in the URL, or tables below' },
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
radar: [
{
source: ['dnaindia.com/:category'],
},
],
name: 'News',
maintainers: ['Rjnishant530'],
handler,
description: `Topics:
| DNA verified |
| ------------ |
| dna-verified |
:::tip Topic
The URL of the form \`https://www.dnaindia.com/topic/dna-verified\` demonstrates the utilization of the subdomain \`topic\`
:::`,
description: `Categories:
| Headlines | Explainer | India | Entertainment | Sports | Viral | Lifestyle | Education | Business | World |
| --------- | --------- | ----- | ------------- | ------ | ----- | --------- | --------- | -------- | ----- |
| headlines | explainer | india | entertainment | sports | viral | lifestyle | education | business | world |`,
};
async function handler(ctx) {
const { category, topic } = ctx.req.param();
const baseUrl = 'https://www.dnaindia.com';
let route;
if (category) {
route = `/${category}`;
} else if (topic) {
route = `/topic/${topic}`;
} else {
logger.error('Invalid URL');
}
const { data: response } = await got(`${baseUrl}${route}`);
const $ = load(response);
const listItems = $('div.col-lg-6 div.list-news')
.toArray()
.map((item) => {
item = $(item);
const a = item.find('div.explainer-subtext a');
return {
title: a.text(),
link: `${baseUrl}${a.attr('href')}`,
};
});
const items = await Promise.all(
listItems.map((item) =>
cache.tryGet(item.link, async () => {
const { data: response } = await got(item.link);
const $ = load(response);
item.itunes_item_image = $('div.article-img img').attr('src');
item.category = $('div.tags ul li')
.toArray()
.map((item) => $(item).find('a').text());
const time = $('p.dna-update').text().split('Updated:')[1];
item.pubDate = timezone(parseDate(time, 'MMMDD,YYYY,hh:mmA'), +5.5);
item.author = 'DNA Web Team';
item.description = $('div.article-description')
.clone()
.children('div')
.remove()
.end()
.toArray()
.map((element) => $(element).html())
.join('');
return item;
})
)
);
return {
title: 'DNA India',
link: baseUrl,
item: items,
description: 'Latest News on dnaIndia.com',
logo: 'https://cdn.dnaindia.com/sites/all/themes/dnaindia/favicon-1016.ico',
icon: 'https://cdn.dnaindia.com/sites/all/themes/dnaindia/favicon-1016.ico',
language: 'en-us',
};
}

View File

@ -0,0 +1,68 @@
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
import logger from '@/utils/logger';
export async function handler(ctx) {
const { category, topic } = ctx.req.param();
const baseUrl = 'https://www.dnaindia.com';
let route;
if (category) {
route = `/${category}`;
} else if (topic) {
route = `/topic/${topic}`;
} else {
logger.error('Invalid URL');
}
const link = `${baseUrl}${route}`;
const { data: response } = await got(link);
const $ = load(response);
const listItems = $('div.col-lg-6 div.list-news')
.toArray()
.map((item) => {
item = $(item);
const a = item.find('div.explainer-subtext a');
return {
title: a.text(),
link: `${baseUrl}${a.attr('href')}`,
};
});
const items = await Promise.all(
listItems.map((item) =>
cache.tryGet(item.link, async () => {
const { data: response } = await got(item.link);
const $ = load(response);
item.itunes_item_image = $('div.article-img img').attr('src');
item.category = $('div.tags ul li')
.toArray()
.map((item) => $(item).find('a').text());
const time = $('p.dna-update').text().split('Updated:')[1];
item.pubDate = timezone(parseDate(time, 'MMMDD,YYYY,hh:mmA'), +5.5);
item.author = 'DNA Web Team';
item.description = $('div.article-description')
.clone()
.children('div')
.remove()
.end()
.toArray()
.map((element) => $(element).html())
.join('');
return item;
})
)
);
return {
title: 'DNA India',
link,
item: items,
description: 'Latest News on dnaIndia.com',
logo: 'https://cdn.dnaindia.com/sites/all/themes/dnaindia/favicon-1016.ico',
icon: 'https://cdn.dnaindia.com/sites/all/themes/dnaindia/favicon-1016.ico',
language: 'en-us',
};
}

View File

@ -3,4 +3,5 @@ import type { Namespace } from '@/types';
export const namespace: Namespace = {
name: 'DNA India',
url: 'dnaindia.com',
categories: ['traditional-media'],
};

View File

@ -0,0 +1,24 @@
import { Route } from '@/types';
import { handler } from './common';
export const route: Route = {
name: 'News',
maintainers: ['Rjnishant530'],
path: ['/:category'],
example: '/dnaindia/headlines',
parameters: {
category: 'Find it in the URL, or tables below',
},
radar: [
{
source: ['www.dnaindia.com/:category'],
},
],
handler,
url: 'www.dnaindia.com',
description: `Categories:
| Headlines | Explainer | India | Entertainment | Sports | Viral | Lifestyle | Education | Business | World |
| --------- | --------- | ----- | ------------- | ------ | ----- | --------- | --------- | -------- | ----- |
| headlines | explainer | india | entertainment | sports | viral | lifestyle | education | business | world |`,
};

View File

@ -0,0 +1,28 @@
import { Route } from '@/types';
import { handler } from './common';
export const route: Route = {
name: 'Topic',
maintainers: ['Rjnishant530'],
path: ['/topic/:topic'],
example: '/dnaindia/topic/dna-verified',
parameters: {
category: 'Find it in the URL',
},
radar: [
{
source: ['www.dnaindia.com/topic/:topic'],
},
],
handler,
url: 'www.dnaindia.com',
description: `Topics:
| DNA verified |
| ------------ |
| dna-verified |
:::tip
The URL of the form \`https://www.dnaindia.com/topic/dna-verified\` demonstrates the utilization of the subdomain \`topic\`.
:::`,
};

View File

@ -42,7 +42,6 @@ export const route: Route = {
maintainers: ['nczitzk'],
handler,
url: 'farmatters.com/news',
url: 'farmatters.com/exclusive',
};
async function handler(ctx) {

View File

@ -3,4 +3,5 @@ import type { Namespace } from '@/types';
export const namespace: Namespace = {
name: 'Farmatters',
url: 'farmatters.com',
categories: ['new-media'],
};

View File

@ -11,15 +11,6 @@ export const route: Route = {
path: '/events',
categories: ['new-media'],
example: '/houxu/events',
parameters: {},
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
radar: [
{
source: ['houxu.app/events', 'houxu.app/'],

View File

@ -8,19 +8,17 @@ import { art } from '@/utils/render';
import path from 'node:path';
export const route: Route = {
path: ['/featured', '/index', '/'],
name: '热点',
maintainers: ['nczitzk'],
example: '/houxu',
path: '/',
radar: [
{
source: ['houxu.app/'],
target: '',
},
],
name: 'Unknown',
maintainers: [],
handler,
url: 'houxu.app/',
url: 'houxu.app/',
url: 'houxu.app/',
};
async function handler(ctx) {

View File

@ -11,15 +11,6 @@ export const route: Route = {
path: '/memory',
categories: ['new-media'],
example: '/houxu/memory',
parameters: {},
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
radar: [
{
source: ['houxu.app/memory', 'houxu.app/'],

View File

@ -30,7 +30,6 @@ export const route: Route = {
| | | | |
| ---- | ------ | ------ | ------ |
| 0 | 1 | 2 | 3 |`,
url: 'javdb.com/',
};
async function handler(ctx) {

View File

@ -21,9 +21,7 @@ export const route: Route = {
maintainers: ['nczitzk'],
handler,
url: 'logonews.cn/',
url: 'logonews.cn/',
description: `如 [中国 - 标志情报局](https://www.logonews.cn/tag/china) 的 URL 为 \`https://www.logonews.cn/tag/china\`,可得路由为 [\`/logonews/tag/china\`](https://rsshub.app/logonews/tag/china)。`,
url: 'logonews.cn/work',
};
async function handler(ctx) {

19
lib/routes/nber/all.ts Normal file
View File

@ -0,0 +1,19 @@
import { Route } from '@/types';
import { handler } from './common';
export const route: Route = {
name: 'All Papers',
maintainers: ['5upernova-heng'],
path: '/papers',
example: '/nber/papers',
features: {
supportScihub: true,
},
radar: [
{
source: ['nber.org/papers'],
},
],
handler,
url: 'nber.org/papers',
};

View File

@ -1,10 +1,9 @@
import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
import { getSubPath } from '@/utils/common-utils';
import cache from '@/utils/cache';
import got from '@/utils/got';
import ofetch from '@/utils/ofetch';
import { load } from 'cheerio';
import path from 'node:path';
import { art } from '@/utils/render';
@ -12,37 +11,10 @@ import { parseDate } from '@/utils/parse-date';
import { config } from '@/config';
async function getData(url) {
const response = await got(url).json();
const response = await ofetch(url);
return response.results;
}
export const route: Route = {
path: ['/papers', '/news'],
categories: ['journal'],
example: '/nber/papers',
parameters: {},
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: true,
},
radar: [
{
source: ['nber.org/papers'],
},
],
name: 'All Papers',
maintainers: [],
handler,
url: 'nber.org/papers',
description: `Papers that are published in this week.`,
url: 'nber.org/papers',
};
async function handler(ctx) {
export async function handler(ctx) {
const url = 'https://www.nber.org/api/v1/working_page_listing/contentType/working_paper/_/_/search';
const baseUrl = 'https://www.nber.org';
const data = await cache.tryGet(url, () => getData(url), config.cache.routeExpire, false);
@ -52,8 +24,8 @@ async function handler(ctx) {
.map((article) => {
const link = `${baseUrl}${article.url}`;
return cache.tryGet(link, async () => {
const response = await got(link);
const $ = load(response.data);
const response = await ofetch(link);
const $ = load(response);
const downloadLink = $('meta[name="citation_pdf_url"]').attr('content');
const fullAbstract = $('.page-header__intro-inner').html();
return {
@ -76,6 +48,5 @@ async function handler(ctx) {
link: 'https://www.nber.org/papers',
item: items,
description: `National Bureau of Economic Research Working Papers articles`,
language: $('html').attr('lang'),
};
}

View File

@ -3,4 +3,5 @@ import type { Namespace } from '@/types';
export const namespace: Namespace = {
name: 'National Bureau of Economic Research',
url: 'nber.org',
categories: ['journal'],
};

20
lib/routes/nber/new.ts Normal file
View File

@ -0,0 +1,20 @@
import { Route } from '@/types';
import { handler } from './common';
export const route: Route = {
name: 'New Papers',
maintainers: ['5upernova-heng'],
path: '/new',
example: '/nber/new',
features: {
supportScihub: true,
},
radar: [
{
source: ['nber.org/papers'],
},
],
handler,
url: 'nber.org/papers',
description: 'Papers that are published in this week.',
};

View File

@ -3,18 +3,9 @@ import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
export const route: Route = {
path: ['/news', '/newsflash'],
path: '/news',
categories: ['new-media'],
example: '/panewslab/news',
parameters: {},
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
radar: [
{
source: ['panewslab.com/'],
@ -24,7 +15,6 @@ export const route: Route = {
maintainers: ['nczitzk'],
handler,
url: 'panewslab.com/',
url: 'panewslab.com/',
};
async function handler(ctx) {

View File

@ -20,21 +20,13 @@ const categories = {
};
export const route: Route = {
path: '/:category?',
path: '/profundity/:category?',
categories: ['new-media'],
example: '/panewslab',
example: '/panewslab/profundity',
parameters: { category: '分类,见下表,默认为精选' },
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
radar: [
{
source: ['panewslab.com/'],
source: ['panewslab.com/', 'www.panewslab.com/zh/profundity/index.html'],
},
],
name: '深度',

View File

@ -104,6 +104,29 @@ const fetchIntlItems = async (params) => {
});
};
async function handler(ctx) {
const is_intl = ctx.req.url.indexOf('/intl/') === 0;
const id = ctx.req.param('id');
const order = ctx.req.param('order') ?? 'default';
const lang = ctx.req.param('lang') ?? (is_intl ? 'en_US' : 'zh_CN');
const app_detail = await appDetail(id, lang, is_intl);
const app_img = app_detail.app.icon.original_url;
const app_name = app_detail.app.title;
const items = is_intl ? await fetchIntlItems(ctx.params) : await fetchMainlandItems(ctx.params);
const ret = {
title: `TapTap 评价 ${app_name} - ${(is_intl ? intlSortMap : sortMap)[order][lang]}排序`,
link: `${getRootUrl(is_intl)}/app/${id}/review?${makeSortParam(is_intl, order)}`,
image: app_img,
item: items,
};
ctx.set('json', ret);
return ret;
}
export const route: Route = {
path: ['/review/:id/:order?/:lang?', '/intl/review/:id/:order?/:lang?'],
categories: ['game'],
@ -141,26 +164,3 @@ export const route: Route = {
| ------ | ---- | -------- | -------- |
| update | hot | spent | default |`,
};
async function handler(ctx) {
const is_intl = ctx.req.url.indexOf('/intl/') === 0;
const id = ctx.req.param('id');
const order = ctx.req.param('order') ?? 'default';
const lang = ctx.req.param('lang') ?? (is_intl ? 'en_US' : 'zh_CN');
const app_detail = await appDetail(id, lang, is_intl);
const app_img = app_detail.app.icon.original_url;
const app_name = app_detail.app.title;
const items = is_intl ? await fetchIntlItems(ctx.params) : await fetchMainlandItems(ctx.params);
const ret = {
title: `TapTap 评价 ${app_name} - ${(is_intl ? intlSortMap : sortMap)[order][lang]}排序`,
link: `${getRootUrl(is_intl)}/app/${id}/review?${makeSortParam(is_intl, order)}`,
image: app_img,
item: items,
};
ctx.set('json', ret);
return ret;
}

View File

@ -5,18 +5,9 @@ import { parseDate } from '@/utils/parse-date';
import dayjs from 'dayjs';
export const route: Route = {
path: ['/express', '/newsflash'],
path: '/express',
categories: ['finance'],
example: '/techflowpost/express',
parameters: {},
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
radar: [
{
source: ['techflowpost.com/newsletter/index.html'],
@ -26,7 +17,6 @@ export const route: Route = {
maintainers: ['nczitzk'],
handler,
url: 'techflowpost.com/',
url: 'techflowpost.com/newsletter/index.html',
};
async function handler(ctx) {

View File

@ -5,13 +5,13 @@ import { parseDate } from '@/utils/parse-date';
export const route: Route = {
path: '/',
example: '/techflowpost',
radar: [
{
source: ['techflowpost.com/'],
target: '',
},
],
name: 'Unknown',
name: '首页',
maintainers: ['nczitzk'],
handler,
url: 'techflowpost.com/',

View File

@ -0,0 +1,38 @@
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { baseUrl, parseList, parseItems } from './utils';
import { getSubPath } from '@/utils/common-utils';
export async function handler(ctx) {
const requestPath = getSubPath(ctx);
const isTopic = requestPath.startsWith('/topic/');
let link = baseUrl;
if (isTopic) {
link += `/topic/${ctx.req.param('id')}`;
} else if (requestPath === '/') {
link += `/articles/report`;
} else {
link += `/articles${requestPath}`;
}
const { data: response } = await got(link);
const $ = load(response);
const list = $(`${isTopic ? '.view-grouping' : '.pane-clone-of-article'} .views-row-inner`)
.toArray()
.map((item) => parseList($(item)));
const items = await parseItems(list, cache.tryGet);
return {
title: $('head title').text(),
description: $('head meta[name="description"]').attr('content'),
image: $('head meta[property="og:image"]').attr('content'),
logo: $('head link[rel="shortcut icon"]').attr('href'),
icon: $('head link[rel="shortcut icon"]').attr('href'),
link,
item: items,
};
}

View File

@ -1,47 +1,17 @@
import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { baseUrl, parseList, parseItems } from './utils';
import { handler } from './common';
export const route: Route = {
path: ['/', '/category/:id{.+}', '/info', '/report', '/topic/:id'],
name: 'Unknown',
name: '最新相關資訊 / 最新查核報告',
maintainers: ['TonyRL'],
example: '/tfc-taiwan',
path: '/:type?',
parameters: {
type: '分類,見下表,預設為 `report`',
},
handler,
url: 'tfc-taiwan.org.tw/articles/report',
url: 'tfc-taiwan.org.tw/articles/info',
description: `| 最新相關資訊 | 最新查核報告 |
| ------------ | ------------ |
| info | report |`,
};
async function handler(ctx) {
const requestPath = ctx.req.path;
const isTopic = requestPath.startsWith('/topic/');
let link = baseUrl;
if (isTopic) {
link += `/topic/${ctx.req.param('id')}`;
} else if (requestPath === '/') {
link += `/articles/report`;
} else {
link += `/articles${requestPath}`;
}
const { data: response } = await got(link);
const $ = load(response);
const list = $(`${isTopic ? '.view-grouping' : '.pane-clone-of-article'} .views-row-inner`)
.toArray()
.map((item) => parseList($(item)));
const items = await parseItems(list, cache.tryGet);
return {
title: $('head title').text(),
description: $('head meta[name="description"]').attr('content'),
image: $('head meta[property="og:image"]').attr('content'),
logo: $('head link[rel="shortcut icon"]').attr('href'),
icon: $('head link[rel="shortcut icon"]').attr('href'),
link,
item: items,
};
}

View File

@ -0,0 +1,17 @@
import { Route } from '@/types';
import { handler } from './common';
export const route: Route = {
name: '專題 / 重點專區',
maintainers: ['TonyRL'],
example: '/tfc-taiwan/category/242',
path: '/:type/:id{.+}',
parameters: {
type: '分類,見下表,預設為 `report`',
},
handler,
url: 'tfc-taiwan.org.tw/articles/report',
description: `| 專題 | 重點專區 |
| -------- | -------- |
| category | topic |`,
};

View File

@ -1,5 +1,5 @@
import { Route } from '@/types';
import got from '@/utils/got';
import ofetch from '@/utils/ofetch';
import { parseDate } from '@/utils/parse-date';
export const route: Route = {
@ -30,14 +30,9 @@ async function handler(ctx) {
const rootUrl = 'https://trendingpapers.com';
const currentUrl = `${rootUrl}/api/papers?p=1&o=pagerank_growth&pd=${time}&cc=${cited}&c=${category}`;
const response = await got({
method: 'get',
url: currentUrl,
});
const response = await ofetch(currentUrl);
const $ = response.data;
const papers = $.data.map((_) => {
const papers = response.data.map((_) => {
const title = _.title;
const abstract = _.abstract;
const url = _.url;
@ -60,6 +55,5 @@ async function handler(ctx) {
title: `Trending Papers on arXiv.org | ${category} | ${time} | ${cited} | `,
link: currentUrl,
item: papers,
language: $('html').attr('lang'),
};
}

View File

@ -0,0 +1,17 @@
import { Route } from '@/types';
import { handler } from './common';
export const route: Route = {
name: '分类更新',
maintainers: ['Gandum2077'],
path: '/category/:cid',
example: '/wnacg/category/6',
radar: [
{
source: ['wnacg.com/*'],
target: (_, url) => `/wnacg/category/${new URL(url).pathname.match(/albums-index-cate-(\d+)\.html$/)[1]}`,
},
],
handler,
url: 'wnacg.com/albums.html',
};

111
lib/routes/wnacg/common.ts Normal file
View File

@ -0,0 +1,111 @@
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';
import InvalidParameterError from '@/errors/types/invalid-parameter';
const categories = {
1: '同人誌 漢化',
2: '同人誌 CG畫集',
3: '同人誌 Cosplay',
5: '同人誌',
6: '單行本',
7: '雜誌&短篇',
9: '單行本 漢化',
10: '雜誌&短篇 漢化',
12: '同人誌 日語',
13: '單行本 日語',
14: '雜誌&短篇 日語',
16: '同人誌 English',
17: '單行本 English',
18: '雜誌&短篇 English',
19: '韓漫',
20: '韓漫 漢化',
21: '韓漫 生肉',
22: '同人誌 3D漫畫',
};
const baseUrl = 'https://www.wnacg.com';
export async function handler(ctx) {
const { cid, tag } = ctx.req.param();
if (cid && !Object.keys(categories).includes(cid)) {
throw new InvalidParameterError('此分类不存在');
}
const url = `${baseUrl}/albums${cid ? `-index-cate-${cid}` : ''}${tag ? `-index-tag-${tag}` : ''}.html`;
const { data } = await got(url);
const $ = load(data);
const list = $('.gallary_item')
.toArray()
.map((item) => {
item = $(item);
const href = item.find('a').attr('href');
const aid = href.match(/^\/photos-index-aid-(\d+)\.html$/)[1];
return {
title: item.find('a').attr('title'),
link: `${baseUrl}${href}`,
pubDate: parseDate(
item
.find('.info_col')
.text()
.replace(/\d+張照片,\n創建於/, ''),
'YYYY-MM-DD'
),
aid,
};
});
const items = await Promise.all(
list.map((item) =>
cache.tryGet(item.link, async () => {
const { data: descRes } = await got(item.link, {
headers: {
referer: encodeURI(url),
},
});
let $ = load(descRes);
const author = $('.uwuinfo p').first().text();
const category = $('.tagshow')
.toArray()
.map((item) => $(item).text());
$('.addtags').remove();
const description = $('.uwconn').html();
const { data } = await got(`${baseUrl}/photos-gallery-aid-${item.aid}.html`, {
headers: {
referer: `${baseUrl}/photos-slide-aid-${item.aid}.html`,
},
});
$ = load(data);
const imgListMatch = $('script')
.text()
.match(/var imglist = (\[.*]);"\);/)[1];
const imgList = JSON.parse(imgListMatch.replaceAll('url:', '"url":').replaceAll('caption:', '"caption":').replaceAll('fast_img_host+\\', '').replaceAll('\\', ''));
item.author = author;
item.category = category;
item.description = art(path.join(__dirname, 'templates/manga.art'), {
description,
imgList,
});
return item;
})
)
);
return {
title: $('head title').text(),
link: url,
item: items,
};
}

View File

@ -1,126 +1,16 @@
import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';
import InvalidParameterError from '@/errors/types/invalid-parameter';
const categories = {
1: '同人誌 漢化',
2: '同人誌 CG畫集',
3: '同人誌 Cosplay',
5: '同人誌',
6: '單行本',
7: '雜誌&短篇',
9: '單行本 漢化',
10: '雜誌&短篇 漢化',
12: '同人誌 日語',
13: '單行本 日語',
14: '雜誌&短篇 日語',
16: '同人誌 English',
17: '單行本 English',
19: '韓漫',
20: '韓漫 漢化',
21: '韓漫 生肉',
};
const baseUrl = 'https://www.wnacg.com';
import { handler } from './common';
export const route: Route = {
path: ['/', '/category/:cid', '/tag/:tag'],
name: '最新',
maintainers: ['KenMizz'],
path: '/',
example: '/wnacg',
radar: [
{
source: ['wnacg.org/albums.html', 'wnacg.org/'],
target: '',
source: ['wnacg.com/albums.html', 'wnacg.com/'],
},
],
name: 'Unknown',
maintainers: ['KenMizz'],
handler,
url: 'wnacg.org/albums.html',
url: 'wnacg.org/albums.html',
url: 'wnacg.org/albums.html',
url: 'wnacg.com/albums.html',
};
async function handler(ctx) {
const { cid, tag } = ctx.req.param();
if (cid && !Object.keys(categories).includes(cid)) {
throw new InvalidParameterError('此分类不存在');
}
const url = `${baseUrl}/albums${cid ? `-index-cate-${cid}` : ''}${tag ? `-index-tag-${tag}` : ''}.html`;
const { data } = await got(url);
const $ = load(data);
const list = $('.gallary_item')
.toArray()
.map((item) => {
item = $(item);
const href = item.find('a').attr('href');
const aid = href.match(/^\/photos-index-aid-(\d+)\.html$/)[1];
return {
title: item.find('a').attr('title'),
link: `${baseUrl}${href}`,
pubDate: parseDate(
item
.find('.info_col')
.text()
.replace(/\d+張照片,\n創建於/, ''),
'YYYY-MM-DD'
),
aid,
};
});
const items = await Promise.all(
list.map((item) =>
cache.tryGet(item.link, async () => {
const { data: descRes } = await got(item.link, {
headers: {
referer: encodeURI(url),
},
});
let $ = load(descRes);
const author = $('.uwuinfo p').first().text();
const category = $('.tagshow')
.toArray()
.map((item) => $(item).text());
$('.addtags').remove();
const description = $('.uwconn').html();
const { data } = await got(`${baseUrl}/photos-gallery-aid-${item.aid}.html`, {
headers: {
referer: `${baseUrl}/photos-slide-aid-${item.aid}.html`,
},
});
$ = load(data);
const imgListMatch = $('script')
.text()
.match(/var imglist = (\[.*]);"\);/)[1];
const imgList = JSON.parse(imgListMatch.replaceAll('url:', '"url":').replaceAll('caption:', '"caption":').replaceAll('fast_img_host+\\', '').replaceAll('\\', ''));
item.author = author;
item.category = category;
item.description = art(path.join(__dirname, 'templates/manga.art'), {
description,
imgList,
});
return item;
})
)
);
return {
title: $('head title').text(),
link: url,
item: items,
};
}

17
lib/routes/wnacg/tag.ts Normal file
View File

@ -0,0 +1,17 @@
import { Route } from '@/types';
import { handler } from './common';
export const route: Route = {
name: '標籤更新',
maintainers: ['Gandum2077'],
path: '/tag/:tag',
example: '/wnacg/tag/漢化',
radar: [
{
source: ['wnacg.com/*'],
target: (_, url) => `/wnacg/tag/${new URL(url).pathname.match(/albums-index-tag-(.+?)\.html$/)[1]}`,
},
],
handler,
url: 'wnacg.com/albums.html',
};