diff --git a/lib/routes/cnbeta/category.ts b/lib/routes/cnbeta/category.ts new file mode 100644 index 00000000000000..076f1506424401 --- /dev/null +++ b/lib/routes/cnbeta/category.ts @@ -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 |`, +}; diff --git a/lib/routes/cnbeta/type.ts b/lib/routes/cnbeta/common.ts similarity index 81% rename from lib/routes/cnbeta/type.ts rename to lib/routes/cnbeta/common.ts index ea3b5383cdb195..5f4af27818e5b6 100644 --- a/lib/routes/cnbeta/type.ts +++ b/lib/routes/cnbeta/common.ts @@ -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; diff --git a/lib/routes/cnbeta/index.ts b/lib/routes/cnbeta/index.ts new file mode 100644 index 00000000000000..9d7f3012e7e547 --- /dev/null +++ b/lib/routes/cnbeta/index.ts @@ -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', +}; diff --git a/lib/routes/cnbeta/namespace.ts b/lib/routes/cnbeta/namespace.ts index af50033d377051..16da5989dec3ef 100644 --- a/lib/routes/cnbeta/namespace.ts +++ b/lib/routes/cnbeta/namespace.ts @@ -3,4 +3,5 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: 'cnBeta.COM', url: 'cnbeta.com.tw', + categories: ['new-media'], }; diff --git a/lib/routes/cnbeta/topics.ts b/lib/routes/cnbeta/topics.ts new file mode 100644 index 00000000000000..84dc83eaf66f09 --- /dev/null +++ b/lib/routes/cnbeta/topics.ts @@ -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) +:::`, +}; diff --git a/lib/routes/cnblogs/common.ts b/lib/routes/cnblogs/common.ts index 9277ab20c1f98e..9518252198a070 100644 --- a/lib/routes/cnblogs/common.ts +++ b/lib/routes/cnblogs/common.ts @@ -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) { diff --git a/lib/routes/cnki/author.ts b/lib/routes/cnki/author.ts index 8efedd5f7e3f9b..b3391d60acde9d 100644 --- a/lib/routes/cnki/author.ts +++ b/lib/routes/cnki/author.ts @@ -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}`, diff --git a/lib/routes/cnki/utils.ts b/lib/routes/cnki/utils.ts index 5b2e7dbe00f50b..5b3f044d557da9 100644 --- a/lib/routes/cnki/utils.ts +++ b/lib/routes/cnki/utils.ts @@ -24,4 +24,4 @@ const ProcessItem = async (item) => { return item; }; -export default { ProcessItem }; +export { ProcessItem }; diff --git a/lib/routes/creative-comic/book.ts b/lib/routes/creative-comic/book.ts index e5e0359f2e8633..5edb198a5dd780 100644 --- a/lib/routes/creative-comic/book.ts +++ b/lib/routes/creative-comic/book.ts @@ -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)); }) ); } diff --git a/lib/routes/creative-comic/utils.ts b/lib/routes/creative-comic/utils.ts index dba027f1e91938..14558977dc9e15 100644 --- a/lib/routes/creative-comic/utils.ts +++ b/lib/routes/creative-comic/utils.ts @@ -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 }; diff --git a/lib/routes/darwinawards/index.ts b/lib/routes/darwinawards/index.ts index 3e8b5d813526cd..ee5567d7db7f32 100644 --- a/lib/routes/darwinawards/index.ts +++ b/lib/routes/darwinawards/index.ts @@ -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() { diff --git a/lib/routes/darwinawards/namespace.ts b/lib/routes/darwinawards/namespace.ts index 77610ba63ae3b0..20c3aa81fb7b16 100644 --- a/lib/routes/darwinawards/namespace.ts +++ b/lib/routes/darwinawards/namespace.ts @@ -3,4 +3,5 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: 'Darwin Awards', url: 'darwinawards.com', + categories: ['other'], }; diff --git a/lib/routes/dnaindia/category.ts b/lib/routes/dnaindia/category.ts index facad0317e42a7..e69de29bb2d1d6 100644 --- a/lib/routes/dnaindia/category.ts +++ b/lib/routes/dnaindia/category.ts @@ -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', - }; -} diff --git a/lib/routes/dnaindia/common.ts b/lib/routes/dnaindia/common.ts new file mode 100644 index 00000000000000..6c772bcffd8878 --- /dev/null +++ b/lib/routes/dnaindia/common.ts @@ -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', + }; +} diff --git a/lib/routes/dnaindia/namespace.ts b/lib/routes/dnaindia/namespace.ts index 89176d9dea1eb5..055403ca475ec3 100644 --- a/lib/routes/dnaindia/namespace.ts +++ b/lib/routes/dnaindia/namespace.ts @@ -3,4 +3,5 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: 'DNA India', url: 'dnaindia.com', + categories: ['traditional-media'], }; diff --git a/lib/routes/dnaindia/news.ts b/lib/routes/dnaindia/news.ts new file mode 100644 index 00000000000000..fc75655cdb4ae9 --- /dev/null +++ b/lib/routes/dnaindia/news.ts @@ -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 |`, +}; diff --git a/lib/routes/dnaindia/topic.ts b/lib/routes/dnaindia/topic.ts new file mode 100644 index 00000000000000..9b5016e7ede2af --- /dev/null +++ b/lib/routes/dnaindia/topic.ts @@ -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\`. +:::`, +}; diff --git a/lib/routes/farmatters/index.ts b/lib/routes/farmatters/index.ts index 468df1521150f8..c58071c0c5bde2 100644 --- a/lib/routes/farmatters/index.ts +++ b/lib/routes/farmatters/index.ts @@ -42,7 +42,6 @@ export const route: Route = { maintainers: ['nczitzk'], handler, url: 'farmatters.com/news', - url: 'farmatters.com/exclusive', }; async function handler(ctx) { diff --git a/lib/routes/farmatters/namespace.ts b/lib/routes/farmatters/namespace.ts index eff1a766071d2e..147fcc5a77796b 100644 --- a/lib/routes/farmatters/namespace.ts +++ b/lib/routes/farmatters/namespace.ts @@ -3,4 +3,5 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: 'Farmatters', url: 'farmatters.com', + categories: ['new-media'], }; diff --git a/lib/routes/houxu/events.ts b/lib/routes/houxu/events.ts index 5ac31cafc0ff2d..9e4d5770c11e54 100644 --- a/lib/routes/houxu/events.ts +++ b/lib/routes/houxu/events.ts @@ -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/'], diff --git a/lib/routes/houxu/index.ts b/lib/routes/houxu/index.ts index 1b1d4c0152a343..fcac8b90faeec6 100644 --- a/lib/routes/houxu/index.ts +++ b/lib/routes/houxu/index.ts @@ -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) { diff --git a/lib/routes/houxu/memory.ts b/lib/routes/houxu/memory.ts index 11c3c5c4344209..6e108b978be4d2 100644 --- a/lib/routes/houxu/memory.ts +++ b/lib/routes/houxu/memory.ts @@ -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/'], diff --git a/lib/routes/javdb/index.ts b/lib/routes/javdb/index.ts index 67d24d9f780af2..b8095a3b00a097 100644 --- a/lib/routes/javdb/index.ts +++ b/lib/routes/javdb/index.ts @@ -30,7 +30,6 @@ export const route: Route = { | 全部 | 可下载 | 含字幕 | 含短評 | | ---- | ------ | ------ | ------ | | 0 | 1 | 2 | 3 |`, - url: 'javdb.com/', }; async function handler(ctx) { diff --git a/lib/routes/logonews/index.ts b/lib/routes/logonews/index.ts index bc234e7ca8eea7..b18e2fac7b0985 100644 --- a/lib/routes/logonews/index.ts +++ b/lib/routes/logonews/index.ts @@ -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) { diff --git a/lib/routes/nber/all.ts b/lib/routes/nber/all.ts new file mode 100644 index 00000000000000..93c438691e6fb2 --- /dev/null +++ b/lib/routes/nber/all.ts @@ -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', +}; diff --git a/lib/routes/nber/index.ts b/lib/routes/nber/common.ts similarity index 69% rename from lib/routes/nber/index.ts rename to lib/routes/nber/common.ts index bcd5bfc74b9a66..fcf5c558c679e5 100644 --- a/lib/routes/nber/index.ts +++ b/lib/routes/nber/common.ts @@ -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'), }; } diff --git a/lib/routes/nber/namespace.ts b/lib/routes/nber/namespace.ts index 8de1aeb513b851..92fee974b2fba0 100644 --- a/lib/routes/nber/namespace.ts +++ b/lib/routes/nber/namespace.ts @@ -3,4 +3,5 @@ import type { Namespace } from '@/types'; export const namespace: Namespace = { name: 'National Bureau of Economic Research', url: 'nber.org', + categories: ['journal'], }; diff --git a/lib/routes/nber/new.ts b/lib/routes/nber/new.ts new file mode 100644 index 00000000000000..0fe6d061b5a0a4 --- /dev/null +++ b/lib/routes/nber/new.ts @@ -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.', +}; diff --git a/lib/routes/panewslab/news.ts b/lib/routes/panewslab/news.ts index 5a46f25bbba541..23ae5f5db8214a 100644 --- a/lib/routes/panewslab/news.ts +++ b/lib/routes/panewslab/news.ts @@ -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) { diff --git a/lib/routes/panewslab/index.ts b/lib/routes/panewslab/profundity.ts similarity index 88% rename from lib/routes/panewslab/index.ts rename to lib/routes/panewslab/profundity.ts index e85fe8fabfb9a9..18703bce39177b 100644 --- a/lib/routes/panewslab/index.ts +++ b/lib/routes/panewslab/profundity.ts @@ -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: '深度', diff --git a/lib/routes/taptap/review.ts b/lib/routes/taptap/review.ts index f2caa46ec661d0..ac3568e4ba6589 100644 --- a/lib/routes/taptap/review.ts +++ b/lib/routes/taptap/review.ts @@ -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; -} diff --git a/lib/routes/techflowpost/express.ts b/lib/routes/techflowpost/express.ts index 029f888bd952da..ae5245fd0300ab 100644 --- a/lib/routes/techflowpost/express.ts +++ b/lib/routes/techflowpost/express.ts @@ -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) { diff --git a/lib/routes/techflowpost/index.ts b/lib/routes/techflowpost/index.ts index 78b728bbe6cd61..d98ffa6c11477b 100644 --- a/lib/routes/techflowpost/index.ts +++ b/lib/routes/techflowpost/index.ts @@ -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/', diff --git a/lib/routes/tfc-taiwan/common.ts b/lib/routes/tfc-taiwan/common.ts new file mode 100644 index 00000000000000..09076850a2191b --- /dev/null +++ b/lib/routes/tfc-taiwan/common.ts @@ -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, + }; +} diff --git a/lib/routes/tfc-taiwan/index.ts b/lib/routes/tfc-taiwan/index.ts index cac1cf3581ac79..4b1653194803c8 100644 --- a/lib/routes/tfc-taiwan/index.ts +++ b/lib/routes/tfc-taiwan/index.ts @@ -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, - }; -} diff --git a/lib/routes/tfc-taiwan/topic.ts b/lib/routes/tfc-taiwan/topic.ts new file mode 100644 index 00000000000000..ba1badc3d5c236 --- /dev/null +++ b/lib/routes/tfc-taiwan/topic.ts @@ -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 |`, +}; diff --git a/lib/routes/trendingpapers/papers.ts b/lib/routes/trendingpapers/papers.ts index ba27dfa6ab2770..db5b76e20a64d6 100644 --- a/lib/routes/trendingpapers/papers.ts +++ b/lib/routes/trendingpapers/papers.ts @@ -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.data; + const response = await ofetch(currentUrl); - 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'), }; } diff --git a/lib/routes/wnacg/category.ts b/lib/routes/wnacg/category.ts new file mode 100644 index 00000000000000..8408e247ac7246 --- /dev/null +++ b/lib/routes/wnacg/category.ts @@ -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', +}; diff --git a/lib/routes/wnacg/common.ts b/lib/routes/wnacg/common.ts new file mode 100644 index 00000000000000..7c88e5d45cc2a3 --- /dev/null +++ b/lib/routes/wnacg/common.ts @@ -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, + }; +} diff --git a/lib/routes/wnacg/index.ts b/lib/routes/wnacg/index.ts index bb877eabcfc9b7..ec09c602f29b17 100644 --- a/lib/routes/wnacg/index.ts +++ b/lib/routes/wnacg/index.ts @@ -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, - }; -} diff --git a/lib/routes/wnacg/tag.ts b/lib/routes/wnacg/tag.ts new file mode 100644 index 00000000000000..c43e5dc1f763f6 --- /dev/null +++ b/lib/routes/wnacg/tag.ts @@ -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', +};