diff --git a/lib/registry.ts b/lib/registry.ts index 1185b2d2c..cb5aef1bb 100644 --- a/lib/registry.ts +++ b/lib/registry.ts @@ -7,8 +7,8 @@ import { serveStatic } from '@hono/node-server/serve-static'; import index from '@/routes/index'; import robotstxt from '@/routes/robots.txt'; -import { namespace as testNamespace } from './routes-new/test/namespace'; -import { route as testRoute } from '@/routes-new/test/index'; +import { namespace as testNamespace } from './routes/test/namespace'; +import { route as testRoute } from '@/routes/test/index'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); @@ -42,7 +42,7 @@ switch (process.env.NODE_ENV) { break; default: modules = directoryImport({ - targetDirectoryPath: path.join(__dirname, './routes-new'), + targetDirectoryPath: path.join(__dirname, './routes'), importPattern: /\.ts$/, }) as typeof modules; } @@ -98,7 +98,7 @@ export default function (app: Hono) { const wrapedHandler: Handler = async (ctx) => { if (!ctx.get('data')) { if (typeof namespaces[namespace].routes[path].handler !== 'function') { - const { route } = await import(`./routes-new/${namespace}/${namespaces[namespace].routes[path].location}`); + const { route } = await import(`./routes/${namespace}/${namespaces[namespace].routes[path].location}`); namespaces[namespace].routes[path].handler = route.handler; } ctx.set('data', await namespaces[namespace].routes[path].handler(ctx)); diff --git a/lib/routes/0818tuan/index.ts b/lib/routes/0818tuan/index.ts index 500385be9..0f4fff8f9 100644 --- a/lib/routes/0818tuan/index.ts +++ b/lib/routes/0818tuan/index.ts @@ -1,10 +1,29 @@ +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'; -export default async (ctx) => { +export const route: Route = { + path: '/:listId?', + categories: ['game'], + example: '/0818tuan', + parameters: { listId: '活动分类,见下表,默认为 `1`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '分类', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'http://www.0818tuan.com'; const listId = ctx.req.param('listId') || '1'; const url = `${baseUrl}/list-${listId}-0.html`; @@ -39,10 +58,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), link: url, image: 'http://www.0818tuan.com/favicon.ico', item: items, - }); -}; + }; +} diff --git a/lib/routes/12306/index.ts b/lib/routes/12306/index.ts index 8721c19af..d2d9dcf8d 100644 --- a/lib/routes/12306/index.ts +++ b/lib/routes/12306/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -49,7 +50,25 @@ function getStationInfo(stationName) { }); } -export default async (ctx) => { +export const route: Route = { + path: '/:date/:from/:to/:type?', + categories: ['shopping'], + example: '/12306/2022-02-19/重庆/永川东', + parameters: { date: '时间,格式为(YYYY-MM-DD)', from: '始发站', to: '终点站', type: '售票类型,成人和学生可选,默认为成人' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '售票信息', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const date = ctx.req.param('date'); const fromStationInfo = await getStationInfo(ctx.req.param('from')); const toStationInfo = await getStationInfo(ctx.req.param('to')); @@ -104,9 +123,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `${fromStationInfo.name} → ${toStationInfo.name} ${date}`, link: linkUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/12306/zxdt.ts b/lib/routes/12306/zxdt.ts index 164159352..d3a0ccf89 100644 --- a/lib/routes/12306/zxdt.ts +++ b/lib/routes/12306/zxdt.ts @@ -1,9 +1,32 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import * as url from 'node:url'; -export default async (ctx) => { +export const route: Route = { + path: '/zxdt/:id?', + categories: ['shopping'], + example: '/12306/zxdt', + parameters: { id: '铁路局id,可在 URL 中找到,不填默认显示所有铁路局动态' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.12306.cn/', 'www.12306.cn/mormhweb/1/:id/index_fl.html'], + target: '/zxdt/:id', + }, + name: '最新动态', + maintainers: ['LogicJake'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') || -1; const link = id === -1 ? 'https://www.12306.cn/mormhweb/zxdt/index_zxdt.html' : `https://www.12306.cn/mormhweb/1/${id}/index_fl.html`; @@ -51,9 +74,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `${name}最新动态`, link, item: out, - }); -}; + }; +} diff --git a/lib/routes/141jav/index.ts b/lib/routes/141jav/index.ts index 8c535de70..2daa0421b 100644 --- a/lib/routes/141jav/index.ts +++ b/lib/routes/141jav/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -8,7 +9,14 @@ import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const rootUrl = 'https://www.141jav.com'; const currentUrl = `${rootUrl}${getSubPath(ctx)}`; @@ -67,9 +75,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `141JAV - ${$('title').text().split('-')[0].trim()}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/141ppv/index.ts b/lib/routes/141ppv/index.ts index c43c9294a..ab65dfcf6 100644 --- a/lib/routes/141ppv/index.ts +++ b/lib/routes/141ppv/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -8,7 +9,14 @@ import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const rootUrl = 'https://www.141ppv.com'; const currentUrl = `${rootUrl}${getSubPath(ctx)}`; @@ -70,9 +78,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `141PPV - ${$('title').text().split('-')[0].trim()}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/163/ds.ts b/lib/routes/163/ds.ts index 9dd48fae3..44695ef6f 100644 --- a/lib/routes/163/ds.ts +++ b/lib/routes/163/ds.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -8,7 +9,28 @@ import * as path from 'node:path'; const root_url = 'https://inf.ds.163.com'; -export default async (ctx) => { +export const route: Route = { + path: '/ds/:id', + categories: ['reading'], + example: '/163/ds/63dfbaf4117741daaf73404601165843', + parameters: { id: '用户ID' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ds.163.com/user/:id'], + }, + name: '用户发帖', + maintainers: ['luyuhuang'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const current_url = `${root_url}/v1/web/feed/basic/getSomeOneFeeds?feedTypes=1,2,3,4,6,7,10,11&someOneUid=${id}`; @@ -28,9 +50,9 @@ export default async (ctx) => { pubDate: parseDate(feed.updateTime), })); - ctx.set('data', { + return { title: `${response.data.result.userInfos[0].user.nick} 的动态`, link: `https://ds.163.com/user/${id}`, item: list, - }); -}; + }; +} diff --git a/lib/routes/163/dy.ts b/lib/routes/163/dy.ts index e9fd1d23b..78a3d8a2a 100644 --- a/lib/routes/163/dy.ts +++ b/lib/routes/163/dy.ts @@ -1,10 +1,29 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; import { parseDyArticle } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/dy/:id', + categories: ['traditional-media'], + example: '/163/dy/W4983108759592548559', + parameters: { id: '网易号 ID' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '更新', + maintainers: ['HendricksZheng'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const response = await got(`https://dy.163.com/v2/article/list.do?pageNo=1&wemediaId=${id}&size=10`); @@ -20,11 +39,11 @@ export default async (ctx) => { const items = await Promise.all(list.map((e) => parseDyArticle(charset, e, cache.tryGet))); - ctx.set('data', { + return { title: `网易号 - ${list[0].author}`, link: items[0].feedLink, description: items[0].feedDescription, image: items[0].feedImage, item: items, - }); -}; + }; +} diff --git a/lib/routes/163/dy2.ts b/lib/routes/163/dy2.ts index 66ee62170..2d61f3ef3 100644 --- a/lib/routes/163/dy2.ts +++ b/lib/routes/163/dy2.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -6,7 +7,25 @@ import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; import { parseDyArticle } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/dy2/:id', + categories: ['traditional-media'], + example: '/163/dy2/T1555591616739', + parameters: { id: 'id,该网易号主页网址最后一项html的文件名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '网易号(通用)', + maintainers: ['mjysci'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const limit = ctx.req.query('limit') ?? 30; const url = `https://www.163.com/dy/media/${id}.html`; @@ -32,12 +51,12 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => parseDyArticle(charset, item, cache.tryGet))); - ctx.set('data', { + return { title: `${$('head title').text()} - 网易号`, link: url, description: $('.icon_line.desc').text(), image: $('.head_img').attr('src'), item: items, author: $('h2').text(), - }); -}; + }; +} diff --git a/lib/routes/163/exclusive.ts b/lib/routes/163/exclusive.ts index cebf4e1cf..80f3ca572 100644 --- a/lib/routes/163/exclusive.ts +++ b/lib/routes/163/exclusive.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -76,7 +77,28 @@ const ids = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/exclusive/:id?', + categories: ['traditional-media'], + example: '/163/exclusive/qsyk', + parameters: { id: '栏目, 默认为首页' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['3g.163.com/touch/exclusive/sub/:id'], + }, + name: '栏目', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? ''; const rootUrl = 'https://3g.163.com'; @@ -144,9 +166,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `网易独家 - ${ids[id].title}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/163/music/artist-songs.ts b/lib/routes/163/music/artist-songs.ts index 07317d37a..38cf7a1de 100644 --- a/lib/routes/163/music/artist-songs.ts +++ b/lib/routes/163/music/artist-songs.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -5,7 +6,25 @@ import got from '@/utils/got'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/music/artist/songs/:id', + categories: ['picture'], + example: '/163/music/artist/songs/2116', + parameters: { id: '歌手 id, 可在歌手详情页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '歌手歌曲', + maintainers: ['ZhongMingKun'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const { data } = await got(`https://music.163.com/api/v1/artist/songs`, { @@ -33,10 +52,10 @@ export default async (ctx) => { link: `https://music.163.com/#/song?id=${song.id}`, })); - ctx.set('data', { + return { title: `${artist.name} - 歌手歌曲`, link: `https://music.163.com/#/artist?id=${id}`, description: `网易云音乐 - 歌手歌曲 - ${artist.name}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/163/music/artist.ts b/lib/routes/163/music/artist.ts index de3a60871..beb3f98ac 100644 --- a/lib/routes/163/music/artist.ts +++ b/lib/routes/163/music/artist.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -5,7 +6,25 @@ import got from '@/utils/got'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/music/artist/:id', + categories: ['picture'], + example: '/163/music/artist/2116', + parameters: { id: '歌手 id, 可在歌手详情页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '歌手专辑', + maintainers: ['metowolf'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const response = await got(`https://music.163.com/api/artist/albums/${id}`, { @@ -16,7 +35,7 @@ export default async (ctx) => { const data = response.data; - ctx.set('data', { + return { title: data.artist.name, link: `https://music.163.com/#/artist/album?id=${id}`, description: `网易云音乐歌手专辑 - ${data.artist.name}`, @@ -38,5 +57,5 @@ export default async (ctx) => { author: singer, }; }), - }); -}; + }; +} diff --git a/lib/routes/163/music/djradio.ts b/lib/routes/163/music/djradio.ts index 43835d147..03b872ff2 100644 --- a/lib/routes/163/music/djradio.ts +++ b/lib/routes/163/music/djradio.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -6,7 +7,25 @@ import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/music/djradio/:id', + categories: ['picture'], + example: '/163/music/djradio/347317067', + parameters: { id: '节目 id, 可在电台节目页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: true, + supportScihub: false, + }, + name: '电台节目', + maintainers: ['magic-akari'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const ProcessFeed = (limit, offset) => @@ -65,7 +84,7 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: radio.name, link: `https://music.163.com/djradio?id=${id}`, subtitle: radio.desc, @@ -77,5 +96,5 @@ export default async (ctx) => { itunes_author: dj.nickname, itunes_category: radio.category, item: items.flat(), - }); -}; + }; +} diff --git a/lib/routes/163/music/playlist.ts b/lib/routes/163/music/playlist.ts index a62f63bd7..9263d66d7 100644 --- a/lib/routes/163/music/playlist.ts +++ b/lib/routes/163/music/playlist.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -6,7 +7,25 @@ import { config } from '@/config'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/music/playlist/:id', + categories: ['picture'], + example: '/163/music/playlist/35798529', + parameters: { id: '歌单 id, 可在歌单页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '歌单歌曲', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { if (!config.ncm || !config.ncm.cookies) { throw new Error('163 Music RSS is disabled due to the lack of relevant config'); } @@ -34,7 +53,7 @@ export default async (ctx) => { }); const songs = songinfo.data.songs; - ctx.set('data', { + return { title: data.name, link: `https://music.163.com/#/playlist?id=${id}`, description: `网易云音乐歌单 - ${data.name}`, @@ -54,5 +73,5 @@ export default async (ctx) => { author: singer, }; }), - }); -}; + }; +} diff --git a/lib/routes/163/music/userevents.ts b/lib/routes/163/music/userevents.ts index 374d33d4c..d510e4602 100644 --- a/lib/routes/163/music/userevents.ts +++ b/lib/routes/163/music/userevents.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -6,7 +7,14 @@ import got from '@/utils/got'; import { art } from '@/utils/render'; const renderDescription = (info) => art(path.join(__dirname, '../templates/music/userevents.art'), info); -export default async (ctx) => { +export const route: Route = { + path: '/music/user/events/:id', + name: 'Unknown', + maintainers: ['Master-Hash'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const response = await got(`https://music.163.com/api/event/get/${id}`, { @@ -18,7 +26,7 @@ export default async (ctx) => { const { data } = response; const { nickname, signature, avatarUrl } = data.events[0].user; - ctx.set('data', { + return { title: `${nickname}的云村动态`, link: `https://music.163.com/#/user/event?id=${id}`, description: `网易云音乐用户动态 - ${signature}`, @@ -51,5 +59,5 @@ export default async (ctx) => { comments: item.info.commentCount, }; }), - }); -}; + }; +} diff --git a/lib/routes/163/music/userplaylist.ts b/lib/routes/163/music/userplaylist.ts index 5ada651c1..f082a0e24 100644 --- a/lib/routes/163/music/userplaylist.ts +++ b/lib/routes/163/music/userplaylist.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -5,7 +6,25 @@ import got from '@/utils/got'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/music/user/playlist/:uid', + categories: ['picture'], + example: '/163/music/user/playlist/45441555', + parameters: { uid: '用户 uid, 可在用户主页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '用户歌单', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { const uid = ctx.req.param('uid'); const response = await got.post('https://music.163.com/api/user/playlist', { @@ -25,7 +44,7 @@ export default async (ctx) => { const { nickname, signature, avatarUrl } = creator; - ctx.set('data', { + return { title: `${nickname} 的所有歌单`, link: `https://music.163.com/user/home?id=${uid}`, subtitle: signature, @@ -55,5 +74,5 @@ export default async (ctx) => { category: pl.tags, }; }), - }); -}; + }; +} diff --git a/lib/routes/163/music/userplayrecords.ts b/lib/routes/163/music/userplayrecords.ts index 3f7476649..11dbce526 100644 --- a/lib/routes/163/music/userplayrecords.ts +++ b/lib/routes/163/music/userplayrecords.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -40,7 +41,25 @@ function getItem(records) { }); } -export default async (ctx) => { +export const route: Route = { + path: '/music/user/playrecords/:uid/:type?', + categories: ['picture'], + example: '/163/music/user/playrecords/45441555/1', + parameters: { uid: '用户 uid, 可在用户主页 URL 中找到', type: '排行榜类型,0所有时间(默认),1最近一周' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '用户听歌排行', + maintainers: ['alfredcai'], + handler, +}; + +async function handler(ctx) { const uid = ctx.req.param('uid'); const type = Number.parseInt(ctx.req.param('type')) || 0; @@ -49,10 +68,10 @@ export default async (ctx) => { const records = type === 1 ? response.data.weekData : response.data.allData; - ctx.set('data', { + return { title: `${type === 1 ? '听歌榜单(最近一周)' : '听歌榜单(所有时间}'} - ${uid}}`, link: `https://music.163.com/user/home?id=${uid}`, updated: response.headers.date, item: getItem(records), - }); -}; + }; +} diff --git a/lib/routes/163/news/rank.ts b/lib/routes/163/news/rank.ts index 6cc37f65a..bd5d14e58 100644 --- a/lib/routes/163/news/rank.ts +++ b/lib/routes/163/news/rank.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -76,7 +77,29 @@ const timeRange = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/news/rank/:category?/:type?/:time?', + categories: ['traditional-media'], + example: '/163/news/rank/whole/click/day', + parameters: { + category: '新闻分类,参见下表,默认为“全站”', + type: '排行榜类型,“点击榜”对应`click`,“跟贴榜”对应`follow`,默认为“点击榜”', + time: '统计时间,“1小时”对应`hour`,“24小时”对应`day`,“本周”对应`week`,“本月”对应`month`,默认为“24小时”', + }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '排行榜', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') || 'whole'; const type = ctx.req.param('type') || 'click'; const time = ctx.req.param('time') || 'day'; @@ -148,9 +171,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `网易新闻${timeRange[time].title}${type === 'click' ? '点击' : '跟帖'}榜 - ${cfg.title}`, link: currentUrl, item: items.filter(Boolean), - }); -}; + }; +} diff --git a/lib/routes/163/news/special.ts b/lib/routes/163/news/special.ts index fbc97c929..1852cc02a 100644 --- a/lib/routes/163/news/special.ts +++ b/lib/routes/163/news/special.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; @@ -19,7 +20,25 @@ const typeMap = { 14: '浪潮', 15: '沸点', }; -export default async (ctx) => { +export const route: Route = { + path: '/news/special/:type?', + categories: ['traditional-media'], + example: '/163/news/special/1', + parameters: { type: '栏目' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '专栏', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { if (!ctx.req.param('type')) { throw new Error('Bad parameter. See https://docs.rsshub.app/routes/game#wang-yi-da-shen'); } @@ -107,9 +126,9 @@ export default async (ctx) => { const selectedTypeName = typeMap[selectedType]; - ctx.set('data', { + return { title: selectedTypeName ? `${selectedTypeName} - 网易专栏` : '网易专栏', link: 'https://3g.163.com/touch/exclusive/?referFrom=163', item: items, - }); -}; + }; +} diff --git a/lib/routes/163/open/vip.ts b/lib/routes/163/open/vip.ts index 0c602e692..afb72007d 100644 --- a/lib/routes/163/open/vip.ts +++ b/lib/routes/163/open/vip.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -8,7 +9,28 @@ import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/open/vip', + categories: ['journal'], + example: '/163/open/vip', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['vip.open.163.com/'], + }, + name: '精品课程', + maintainers: ['hoilc'], + handler, +}; + +async function handler() { const url = 'https://vip.open.163.com'; const list_response = await got(url); @@ -59,9 +81,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '网易公开课 - 精品课程', link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/163/renjian.ts b/lib/routes/163/renjian.ts index a50a5d2f2..0b8f8d256 100644 --- a/lib/routes/163/renjian.ts +++ b/lib/routes/163/renjian.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -13,7 +14,28 @@ const titles = { kanke: '看客', }; -export default async (ctx) => { +export const route: Route = { + path: '/renjian/:category?', + categories: ['traditional-media'], + example: '/163/renjian/texie', + parameters: { category: '分类,见下表,默认为特写' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['renjian.163.com/:category', 'renjian.163.com/'], + }, + name: '人间', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'texie'; const rootUrl = 'https://renjian.163.com'; @@ -70,9 +92,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `人间 - ${titles[category]} - 网易新闻`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/163/today.ts b/lib/routes/163/today.ts index f70cfdf82..1d5a5bda8 100644 --- a/lib/routes/163/today.ts +++ b/lib/routes/163/today.ts @@ -1,10 +1,33 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/today/:need_content?', + categories: ['traditional-media'], + example: '/163/today', + parameters: { need_content: '需要获取全文,填写 true/yes 表示需要,默认需要' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['wp.m.163.com/163/html/newsapp/todayFocus/index.html', 'wp.m.163.com/'], + target: '/today', + }, + name: '今日关注', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const needContent = /t|y/i.test(ctx.req.param('need_content') ?? 'true'); const rootUrl = 'https://gw.m.163.com'; @@ -48,9 +71,9 @@ export default async (ctx) => { ); } - ctx.set('data', { + return { title: '今日关注 - 网易新闻', link: 'https://wp.m.163.com/163/html/newsapp/todayFocus/index.html', item: items, - }); -}; + }; +} diff --git a/lib/routes/18comic/album.ts b/lib/routes/18comic/album.ts index 1509af63a..ce191fa06 100644 --- a/lib/routes/18comic/album.ts +++ b/lib/routes/18comic/album.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -5,7 +6,28 @@ import { parseDate } from '@/utils/parse-date'; import { defaultDomain, getRootUrl } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/album/:id', + categories: ['program-update'], + example: '/18comic/album/292282', + parameters: { id: '专辑 id,可在专辑页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['jmcomic.group/'], + }, + name: '专辑', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const { domain = defaultDomain } = ctx.req.query(); const rootUrl = getRootUrl(domain); @@ -70,10 +92,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, description: $('meta[property="og:description"]').attr('content'), - }); -}; + }; +} diff --git a/lib/routes/18comic/blogs.ts b/lib/routes/18comic/blogs.ts index 6e703c2ae..6b6ff8417 100644 --- a/lib/routes/18comic/blogs.ts +++ b/lib/routes/18comic/blogs.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -5,7 +6,28 @@ import { parseDate } from '@/utils/parse-date'; import { defaultDomain, getRootUrl } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/blogs/:category?', + categories: ['program-update'], + example: '/18comic/blogs', + parameters: { category: '分类,见下表,默认为空即全部' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['jmcomic.group/'], + }, + name: '文庫', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? ''; const { domain = defaultDomain } = ctx.req.query(); const rootUrl = getRootUrl(domain); @@ -56,12 +78,12 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title') .text() .replace(/最新的/, $('.article-nav .active').text()), link: currentUrl, item: items, description: $('meta[property="og:description"]').attr('content'), - }); -}; + }; +} diff --git a/lib/routes/18comic/index.ts b/lib/routes/18comic/index.ts index da6eaaf9b..67831176c 100644 --- a/lib/routes/18comic/index.ts +++ b/lib/routes/18comic/index.ts @@ -1,6 +1,28 @@ +import { Route } from '@/types'; import { defaultDomain, getRootUrl, ProcessItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/:category?/:time?/:order?/:keyword?', + categories: ['program-update'], + example: '/18comic', + parameters: { category: '分类,见下表,默认为 `all` 即全部', time: '时间范围,见下表,默认为 `a` 即全部', order: '排列顺序,见下表,默认为 `mr` 即最新', keyword: '关键字,见下表,默认为空' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['jmcomic.group/'], + }, + name: '成人 A 漫', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'all'; const keyword = ctx.req.param('keyword') ?? ''; const time = ctx.req.param('time') ?? 'a'; @@ -11,4 +33,4 @@ export default async (ctx) => { const currentUrl = `${rootUrl}/albums${category === 'all' ? '' : `/${category}`}${keyword ? `?screen=${keyword}` : '?'}${time === 'a' ? '' : `&t=${time}`}${order === 'mr' ? '' : `&o=${order}`}`; ctx.set('data', await ProcessItems(ctx, currentUrl, rootUrl)); -}; +} diff --git a/lib/routes/18comic/search.ts b/lib/routes/18comic/search.ts index 0ead59784..0ee09ac4b 100644 --- a/lib/routes/18comic/search.ts +++ b/lib/routes/18comic/search.ts @@ -1,6 +1,35 @@ +import { Route } from '@/types'; import { defaultDomain, getRootUrl, ProcessItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/search/:option?/:category?/:keyword?/:time?/:order?', + categories: ['program-update'], + example: '/18comic/search/photos/all/NTR', + parameters: { + option: '选项,可选 `video` 和 `photos`,默认为 `photos`', + category: '分类,同上表,默认为 `all` 即全部', + keyword: '关键字,同上表,默认为空', + time: '时间范围,同上表,默认为 `a` 即全部', + order: '排列顺序,同上表,默认为 `mr` 即最新', + }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['jmcomic.group/'], + target: '/:category?/:time?/:order?/:keyword?', + }, + name: '搜索', + maintainers: [], + handler, +}; + +async function handler(ctx) { const option = ctx.req.param('option') ?? 'photos'; const category = ctx.req.param('category') ?? 'all'; const keyword = ctx.req.param('keyword') ?? ''; @@ -12,4 +41,4 @@ export default async (ctx) => { const currentUrl = `${rootUrl}/search/${option}${category === 'all' ? '' : `/${category}`}${keyword ? `?search_query=${keyword}` : '?'}${time === 'a' ? '' : `&t=${time}`}${order === 'mr' ? '' : `&o=${order}`}`; ctx.set('data', await ProcessItems(ctx, currentUrl, rootUrl)); -}; +} diff --git a/lib/routes/19lou/index.ts b/lib/routes/19lou/index.ts index 97be42443..e47e36dd0 100644 --- a/lib/routes/19lou/index.ts +++ b/lib/routes/19lou/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -15,7 +16,25 @@ const setCookie = function (cookieName, cookieValue, seconds, path, domain, secu return [encodeURI(cookieName), '=', encodeURI(cookieValue), expires ? '; expires=' + expires.toGMTString() : '', path ? '; path=' + path : '/', domain ? '; domain=' + domain : '', secure ? '; secure' : ''].join(''); }; -export default async (ctx) => { +export const route: Route = { + path: '/:city?', + categories: ['blog'], + example: '/19lou/jiaxing', + parameters: { city: '分类,见下表,默认为 www,即杭州' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '头条', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const city = ctx.req.param('city') ?? 'www'; if (!isValidHost(city)) { throw new Error('Invalid city'); @@ -71,9 +90,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text().split('-')[0], link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/1lou/index.ts b/lib/routes/1lou/index.ts index 8cbf4389b..a58fb7502 100644 --- a/lib/routes/1lou/index.ts +++ b/lib/routes/1lou/index.ts @@ -1,10 +1,33 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:path?', + categories: ['picture'], + example: '/1lou/search-繁花.htm', + parameters: { path: '路径信息在URL里找到,主页为 index' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['1lou.me/:path'], + target: '/:path', + }, + name: '搜索', + maintainers: ['falling'], + handler, +}; + +async function handler(ctx) { const path = ctx.req.param('path') ?? ''; const rootUrl = `https://www.1lou.me`; const currentUrl = `${rootUrl}/${path}`; @@ -46,9 +69,9 @@ export default async (ctx) => { }) ) ); - ctx.set('data', { + return { title: '1Lou', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/1point3acres/blog.ts b/lib/routes/1point3acres/blog.ts index a17d0675b..d31d8f652 100644 --- a/lib/routes/1point3acres/blog.ts +++ b/lib/routes/1point3acres/blog.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/blog/:category?', + categories: ['blog'], + example: '/1point3acres/blog', + parameters: { category: '分类,见下表,可在对应分类页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['blog.1point3acres.com/:category'], + }, + name: '博客', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const categoryMap = { studyinusa: { title: '留学申请', @@ -59,9 +81,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `${category ? `${categoryMap[category].title} | ` : ''}美国留学就业生活攻略`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/1point3acres/category.ts b/lib/routes/1point3acres/category.ts index 31b6e7214..34324c0d7 100644 --- a/lib/routes/1point3acres/category.ts +++ b/lib/routes/1point3acres/category.ts @@ -1,7 +1,29 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { rootUrl, apiRootUrl, types, ProcessThreads } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/category/:id?/:type?/:order?', + categories: ['blog'], + example: '/1point3acres/category/h1b', + parameters: { id: '标签 id,默认为全部', type: '帖子分类, 见下表,默认为 hot,即热门帖子', order: '排序方式,见下表,默认为空,即最新回复' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['instant.1point3acres.com/section/:id', 'instant.1point3acres.com/'], + }, + name: '标签', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? ''; const type = ctx.req.param('type') ?? 'hot'; const order = ctx.req.param('order') ?? ''; @@ -10,9 +32,9 @@ export default async (ctx) => { const currentUrl = `${rootUrl}${id ? `/category/${id}` : ''}`; const apiUrl = `${apiRootUrl}/api${id ? `/tags/${id}/` : ''}threads?type=${type}&includes=tags,forum_name,summary&ps=${limit}&pg=1&order=${order === '' ? '' : 'time_desc'}&is_groupid=1`; - ctx.set('data', { + return { title: `一亩三分地 - ${id}${types[type]}`, link: currentUrl, item: await ProcessThreads(cache.tryGet, apiUrl, order), - }); -}; + }; +} diff --git a/lib/routes/1point3acres/offer.ts b/lib/routes/1point3acres/offer.ts index 03764105f..739e802bb 100644 --- a/lib/routes/1point3acres/offer.ts +++ b/lib/routes/1point3acres/offer.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -6,7 +7,29 @@ import { art } from '@/utils/render'; import { parseDate } from '@/utils/parse-date'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/offer/:year?/:major?/:school?', + categories: ['blog'], + example: '/1point3acres/offer/12/null/CMU', + parameters: { year: '录取年份 id,空为null', major: '录取专业 id,空为null', school: '录取学校 id,空为null' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['offer.1point3acres.com/'], + target: '/offer', + }, + name: '录取结果', + maintainers: ['EthanWng97'], + handler, +}; + +async function handler(ctx) { // year 2017-2022 // 2017:6 2018:11 2019:12 2020:13 2021:14 2022:15 // CS:1 MIS:2 @@ -56,7 +79,7 @@ export default async (ctx) => { // }, // }); // const tid = responseBasic_1.data.background.tid; - ctx.set('data', { + return { title: '录取结果 - 一亩三分地', link: 'https://offer.1point3acres.com', item: data.map((item) => ({ @@ -68,5 +91,5 @@ export default async (ctx) => { link: 'https://offer.1point3acres.com', guid: `1point3acres:offer:${year}:${major}:${school}:${item.id}`, })), - }); -}; + }; +} diff --git a/lib/routes/1point3acres/section.ts b/lib/routes/1point3acres/section.ts index d1eddbde7..52e94cb2d 100644 --- a/lib/routes/1point3acres/section.ts +++ b/lib/routes/1point3acres/section.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { rootUrl, apiRootUrl, types, ProcessThreads } from './utils'; @@ -12,7 +13,28 @@ const sections = { 265: '签证移民', }; -export default async (ctx) => { +export const route: Route = { + path: '/section/:id?/:type?/:order?', + categories: ['blog'], + example: '/1point3acres/section/345', + parameters: { id: '分区 id,见下表,默认为全部', type: '帖子分类, 见下表,默认为 hot,即热门帖子', order: '排序方式,见下表,默认为空,即最新回复' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['instant.1point3acres.com/section/:id', 'instant.1point3acres.com/'], + }, + name: '分区', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? ''; const type = ctx.req.param('type') ?? 'hot'; const order = ctx.req.param('order') ?? ''; @@ -21,9 +43,9 @@ export default async (ctx) => { const currentUrl = `${rootUrl}${id ? (isNaN(id) ? `/category/${id}` : `/section/${id}`) : ''}`; const apiUrl = `${apiRootUrl}/api${id ? (isNaN(id) ? `/tags/${id}/` : `/forums/${id}/`) : ''}threads?type=${type}&includes=tags,forum_name,summary&ps=${limit}&pg=1&order=${order === '' ? '' : 'time_desc'}&is_groupid=1`; - ctx.set('data', { + return { title: `一亩三分地 - ${Object.hasOwn(sections, id) ? sections[id] : id}${types[type]}`, link: currentUrl, item: await ProcessThreads(cache.tryGet, apiUrl, order), - }); -}; + }; +} diff --git a/lib/routes/1point3acres/thread.ts b/lib/routes/1point3acres/thread.ts index 97fdb49c3..d76b193fc 100644 --- a/lib/routes/1point3acres/thread.ts +++ b/lib/routes/1point3acres/thread.ts @@ -1,16 +1,24 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { rootUrl, apiRootUrl, types, ProcessThreads } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: ['/post/:type?/:order?', '/thread/:type?/:order?'], + name: 'Unknown', + maintainers: ['EthanWng97', 'DIYgod', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') ?? 'hot'; const order = ctx.req.param('order') ?? ''; const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 10; const apiUrl = `${apiRootUrl}/api/threads?type=${type}&includes=tags,forum_name,summary&ps=${limit}&pg=1&order=${order === '' ? '' : 'time_desc'}&is_groupid=1`; - ctx.set('data', { + return { title: `一亩三分地 - ${types[type]}`, link: rootUrl, item: await ProcessThreads(cache.tryGet, apiUrl, order), - }); -}; + }; +} diff --git a/lib/routes/1point3acres/user/post.ts b/lib/routes/1point3acres/user/post.ts index 0dbd420b6..884ab731f 100644 --- a/lib/routes/1point3acres/user/post.ts +++ b/lib/routes/1point3acres/user/post.ts @@ -1,12 +1,34 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/user/:id/posts', + categories: ['blog'], + example: '/1point3acres/user/1/posts', + parameters: { id: '用户 id,可在 Instant 版网站的个人主页 URL 找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['instant.1point3acres.com/profile/:id', 'instant.1point3acres.com/'], + }, + name: '用户回帖', + maintainers: ['Maecenas'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const { posts } = (await got.get(`https://instant.1point3acres.com/v2/api/user/post?pg=1&ps=10&user_id=${id}`)).data; const [{ author_name: author }] = posts; - ctx.set('data', { + return { title: `${author}的回复 - 一亩三分地`, link: `https://instant.1point3acres.com/profile/${id}`, description: `${author}的回复 - 一亩三分地`, @@ -17,5 +39,5 @@ export default async (ctx) => { pubDate: new Date(item.create_time + ' GMT+8').toUTCString(), link: `https://instant.1point3acres.com/thread/${item.thread_id}/post/${item.id}`, })), - }); -}; + }; +} diff --git a/lib/routes/1point3acres/user/thread.ts b/lib/routes/1point3acres/user/thread.ts index e37d23a11..37ade8341 100644 --- a/lib/routes/1point3acres/user/thread.ts +++ b/lib/routes/1point3acres/user/thread.ts @@ -1,12 +1,34 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/user/:id/threads', + categories: ['blog'], + example: '/1point3acres/user/1/threads', + parameters: { id: '用户 id,可在 Instant 版网站的个人主页 URL 找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['instant.1point3acres.com/profile/:id', 'instant.1point3acres.com/'], + }, + name: '用户主题帖', + maintainers: ['Maecenas'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const { threads } = (await got.get(`https://instant.1point3acres.com/v2/api/user/thread?pg=1&ps=10&user_id=${id}`)).data; const [{ author_name: author }] = threads; - ctx.set('data', { + return { title: `${author}的主题帖 - 一亩三分地`, link: `https://instant.1point3acres.com/profile/${id}`, description: `${author}的主题帖 - 一亩三分地`, @@ -17,5 +39,5 @@ export default async (ctx) => { pubDate: new Date(item.update_time + ' GMT+8').toUTCString(), link: `https://instant.1point3acres.com/thread/${item.id}`, })), - }); -}; + }; +} diff --git a/lib/routes/2048/index.ts b/lib/routes/2048/index.ts index a112777f3..7a9373c40 100644 --- a/lib/routes/2048/index.ts +++ b/lib/routes/2048/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -9,7 +10,25 @@ import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/:id?', + categories: ['picture'], + example: '/2048/2', + parameters: { id: '板块 ID, 见下表,默认为最新合集,即 `3`,亦可在 URL 中找到, 例如, `thread.php?fid-3.html`中, 板块 ID 为`3`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: true, + supportPodcast: false, + supportScihub: false, + }, + name: '论坛', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? '2'; const rootUrl = 'https://hjd2048.com'; @@ -108,9 +127,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('#main #breadCrumb a').last().text()} - 2048核基地`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/2cycd/index.ts b/lib/routes/2cycd/index.ts index d0d355ae7..4cb76df1b 100644 --- a/lib/routes/2cycd/index.ts +++ b/lib/routes/2cycd/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -7,7 +8,14 @@ import iconv from 'iconv-lite'; // http://www.2cycd.com/forum.php?mod=forumdisplay&fid=43&orderby=dateline -export default async (ctx) => { +export const route: Route = { + path: '/:fid/:sort?', + name: 'Unknown', + maintainers: ['shelken'], + handler, +}; + +async function handler(ctx) { const fid = ctx.req.param('fid') ?? '43'; const sort = ctx.req.param('sort') ?? 'dateline'; @@ -50,9 +58,9 @@ export default async (ctx) => { }) ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/36kr/hot-list.ts b/lib/routes/36kr/hot-list.ts index 7ee7dd248..ece65391c 100644 --- a/lib/routes/36kr/hot-list.ts +++ b/lib/routes/36kr/hot-list.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; @@ -23,7 +24,29 @@ const categories = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/hot-list/:category?', + categories: ['traditional-media'], + example: '/36kr/hot-list', + parameters: { category: '分类,默认为24小时热榜' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['36kr.com/hot-list/:category', '36kr.com/'], + target: '/hot-list/:category', + }, + name: '资讯热榜', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? '24'; const currentUrl = category === '24' ? rootUrl : `${rootUrl}/hot-list/catalog`; @@ -52,9 +75,9 @@ export default async (ctx) => { items = await Promise.all(items.map((item) => ProcessItem(item, cache.tryGet))); - ctx.set('data', { + return { title: `36氪 - ${categories[category].title}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/36kr/index.ts b/lib/routes/36kr/index.ts index ccd9892ea..b29827e22 100644 --- a/lib/routes/36kr/index.ts +++ b/lib/routes/36kr/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getSubPath } from '@/utils/common-utils'; import cache from '@/utils/cache'; import got from '@/utils/got'; @@ -15,7 +16,14 @@ const shortcuts = { '/information/workplace': '/information/web_zhichang', }; -export default async (ctx) => { +export const route: Route = { + path: '*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const path = getSubPath(ctx) .replace(/^\/news(?!flashes)/, '/information') .replace(/\/search\/article/, '/search/articles'); @@ -49,9 +57,9 @@ export default async (ctx) => { items = await Promise.all(items.map((item) => ProcessItem(item, cache.tryGet))); } - ctx.set('data', { + return { title: `36氪 - ${$('title').text().split('_')[0]}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/3dmgame/game.ts b/lib/routes/3dmgame/game.ts index 1ffb15265..e03e37b06 100644 --- a/lib/routes/3dmgame/game.ts +++ b/lib/routes/3dmgame/game.ts @@ -1,10 +1,21 @@ +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 { parseArticle } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/:name/:type?', + radar: { + source: ['3dmgame.com/games/:name/:type'], + }, + name: 'Unknown', + maintainers: ['sinchang', 'jacky2001114', 'HenryQW'], + handler, +}; + +async function handler(ctx) { const { name, type = 'news' } = ctx.req.param(); const url = `https://www.3dmgame.com/games/${name}/${type}/`; @@ -26,10 +37,10 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => parseArticle(item, cache.tryGet))); - ctx.set('data', { + return { title: $('head title').text().split('_')[0], description: $('head meta[name="Description"]').attr('content'), link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/3dmgame/news-center.ts b/lib/routes/3dmgame/news-center.ts index 21aa55327..535f88a1b 100644 --- a/lib/routes/3dmgame/news-center.ts +++ b/lib/routes/3dmgame/news-center.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -5,7 +6,28 @@ import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; import { parseArticle } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/news/:category?', + categories: ['reading'], + example: '/3dmgame/news', + parameters: { category: '分类名或 ID,见下表,默认为新闻推荐,ID 可从分类 URL 中找到,如 Steam 为 `22221`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['3dmgame.com/news/:category?', '3dmgame.com/news'], + }, + name: '新闻中心', + maintainers: ['zhboner'], + handler, +}; + +async function handler(ctx) { const { category = '' } = ctx.req.param(); const isArcPost = category && !isNaN(category); // https://www.3dmgame.com/news/\d+/ const url = `https://www.3dmgame.com/${category && category !== 'news_36_1' ? 'news/' : ''}${category ?? 'news'}/`; @@ -34,10 +56,10 @@ export default async (ctx) => { const out = await Promise.all(list.map((item) => parseArticle(item, cache.tryGet))); - ctx.set('data', { + return { title: '3DM - ' + $('title').text().split('_')[0], description: $('meta[name="Description"]').attr('content'), link: url, item: out, - }); -}; + }; +} diff --git a/lib/routes/423down/index.ts b/lib/routes/423down/index.ts index 4384c5f07..dd754109a 100644 --- a/lib/routes/423down/index.ts +++ b/lib/routes/423down/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -65,7 +66,14 @@ const titleMap = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/:category/:type?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { category, type } = ctx.req.param(); const url = `${rootUrl}/${categeoryMap[category][type]}`; @@ -107,9 +115,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `423down-${titleMap[category][type]}`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/4gamers/category.ts b/lib/routes/4gamers/category.ts index dcd62cb9c..8428b7705 100644 --- a/lib/routes/4gamers/category.ts +++ b/lib/routes/4gamers/category.ts @@ -1,8 +1,20 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseList, parseItem, getCategories } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: ['/', '/category/:category'], + radar: { + source: ['www.4gamers.com.tw/news', 'www.4gamers.com.tw/'], + target: '', + }, + name: 'Unknown', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category'); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 25; const isLatest = !category; @@ -24,9 +36,9 @@ export default async (ctx) => { categoryName = categories.find((c) => c.id === Number.parseInt(category)).name; } - ctx.set('data', { + return { title: `4Gamers - ${categoryName}`, link: `https://www.4gamers.com.tw/news${isLatest ? '' : `/category/${category}/${categoryName}`}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/4gamers/tag.ts b/lib/routes/4gamers/tag.ts index 8ac0a9bc6..130fb1521 100644 --- a/lib/routes/4gamers/tag.ts +++ b/lib/routes/4gamers/tag.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseList, parseItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/tag/:tag', + categories: ['reading'], + example: '/4gamers/tag/限時免費', + parameters: { tag: '标签名,可在标签 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.4gamers.com.tw/news/tag/:tag'], + }, + name: '标签', + maintainers: ['hoilc'], + handler, +}; + +async function handler(ctx) { const tag = ctx.req.param('tag'); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 25; @@ -16,9 +38,9 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => cache.tryGet(item.link, () => parseItem(item)))); - ctx.set('data', { + return { title: `4Gamers - #${tag}`, link: `https://www.4gamers.com.tw/news/tag/${tag}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/4gamers/topic.ts b/lib/routes/4gamers/topic.ts index 1c5b1262a..53693d3a0 100644 --- a/lib/routes/4gamers/topic.ts +++ b/lib/routes/4gamers/topic.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseList, parseItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/topic/:topic', + categories: ['reading'], + example: '/4gamers/topic/gentlemen-topic', + parameters: { topic: '主题,可在首页上方页面内找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.4gamers.com.tw/news/option-cfg/:topic'], + }, + name: '主題', + maintainers: ['bestpika'], + handler, +}; + +async function handler(ctx) { const topic = ctx.req.param('topic'); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 25; @@ -15,9 +37,9 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => cache.tryGet(item.link, () => parseItem(item)))); - ctx.set('data', { + return { title: `4Gamers - ${topic}`, link: `https://www.4gamers.com.tw/news/option-cfg/${topic}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/4ksj/forum.ts b/lib/routes/4ksj/forum.ts index a54ff7eeb..ecc79a657 100644 --- a/lib/routes/4ksj/forum.ts +++ b/lib/routes/4ksj/forum.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -10,7 +11,25 @@ import { art } from '@/utils/render'; import * as path from 'node:path'; import iconv from 'iconv-lite'; -export default async (ctx) => { +export const route: Route = { + path: '/forum/:id?', + categories: ['picture'], + example: '/4ksj/forum', + parameters: { id: '分类 id,默认为最新4K电影' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '分类', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? '2-1'; const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 25; @@ -108,12 +127,12 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `4k世界 - ${$('#fontsearch ul.cl li.a') .toArray() .map((a) => $(a).text()) .join('+')}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/500px/tribe-set.ts b/lib/routes/500px/tribe-set.ts index 0dcba68af..8d5ffdfc0 100644 --- a/lib/routes/500px/tribe-set.ts +++ b/lib/routes/500px/tribe-set.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -8,7 +9,25 @@ import * as path from 'node:path'; import { baseUrl, getTribeDetail, getTribeSets } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/tribe/set/:id', + categories: ['anime'], + example: '/500px/tribe/set/f5de0b8aa6d54ec486f5e79616418001', + parameters: { id: '部落 ID' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '部落影集', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const limit = Number.parseInt(ctx.req.query('limit')) || 100; @@ -23,11 +42,11 @@ export default async (ctx) => { link: `${baseUrl}/community/set/${item.id}/details`, })); - ctx.set('data', { + return { title: tribe.name, description: `${tribe.watchword} - ${tribe.introduce}`, link: `${baseUrl}/page/tribe/detail?tribeId=${id}&pagev=set`, image: tribe.avatar.a1, item: items, - }); -}; + }; +} diff --git a/lib/routes/500px/user.ts b/lib/routes/500px/user.ts index 59be7c5a8..24ebe9ea4 100644 --- a/lib/routes/500px/user.ts +++ b/lib/routes/500px/user.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -7,7 +8,28 @@ import { parseDate } from '@/utils/parse-date'; import * as path from 'node:path'; import { baseUrl, getUserInfoFromUsername, getUserInfoFromId, getUserWorks } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/user/works/:id', + categories: ['anime'], + example: '/500px/user/works/hujunli', + parameters: { id: '摄影师 ID' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['500px.com.cn/:id', '500px.com.cn/community/user-details/:id', '500px.com.cn/community/user-details/:id/*'], + }, + name: '摄影师作品', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { let { id } = ctx.req.param(); const limit = Number.parseInt(ctx.req.query('limit')) || 100; @@ -26,11 +48,11 @@ export default async (ctx) => { link: `${baseUrl}/community/photo-details/${item.id}`, })); - ctx.set('data', { + return { title: userInfo.nickName, description: userInfo.about, image: userInfo.avatar.a1, link: `${baseUrl}/${id}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/50forum/zhuanjia.ts b/lib/routes/50forum/zhuanjia.ts index dc9be176a..3b85c3b6b 100644 --- a/lib/routes/50forum/zhuanjia.ts +++ b/lib/routes/50forum/zhuanjia.ts @@ -1,10 +1,22 @@ +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'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['50forum.org.cn/home/article/index/category/zhuanjia.html', '50forum.org.cn/'], + target: '', + }, + name: 'Unknown', + maintainers: ['sddiky'], + handler, +}; + +async function handler() { const rootUrl = 'http://www.50forum.org.cn'; const response = await got({ method: 'get', @@ -43,10 +55,10 @@ export default async (ctx) => { }) ) ); - ctx.set('data', { + return { title: `中国经济50人论坛专家文章`, link: 'http://www.50forum.org.cn/home/article/index/category/zhuanjia.html', description: '中国经济50人论坛专家文章', item: out, - }); -}; + }; +} diff --git a/lib/routes/52hrtt/index.ts b/lib/routes/52hrtt/index.ts index 8e8df76fd..a349a3e15 100644 --- a/lib/routes/52hrtt/index.ts +++ b/lib/routes/52hrtt/index.ts @@ -1,10 +1,29 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:area?/:type?', + categories: ['traditional-media'], + example: '/52hrtt/global', + parameters: { area: '地区,默认为全球', type: '分类,默认为新闻' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '新闻', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const area = ctx.req.param('area') ?? 'global'; const type = ctx.req.param('type') ?? ''; @@ -49,9 +68,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${response.data.data.area.areaName} - ${$('.router-link-active').eq(0).text()} - 华人头条`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/52hrtt/symposium.ts b/lib/routes/52hrtt/symposium.ts index 81cdfe691..ffeb58a85 100644 --- a/lib/routes/52hrtt/symposium.ts +++ b/lib/routes/52hrtt/symposium.ts @@ -1,10 +1,33 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/symposium/:id?/:classId?', + categories: ['traditional-media'], + example: '/52hrtt/symposium/F1626082387819', + parameters: { id: '专题 id', classId: '子分类 id' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['52hrtt.com/global/n/w/symposium/:id'], + target: '/symposium/:id', + }, + name: '专题', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? ''; const classId = ctx.req.param('classId') ?? ''; @@ -49,9 +72,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/56kog/class.ts b/lib/routes/56kog/class.ts index e45055d59..6204346fa 100644 --- a/lib/routes/56kog/class.ts +++ b/lib/routes/56kog/class.ts @@ -1,11 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { rootUrl, fetchItems } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/class/:category?', + categories: ['government'], + example: '/56kog/class/1_1', + parameters: { category: '分类,见下表,默认为玄幻魔法' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '分类', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const { category = '1_1' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; const currentUrl = new URL(`class/${category}.html`, rootUrl).href; ctx.set('data', await fetchItems(limit, currentUrl, cache.tryGet)); -}; +} diff --git a/lib/routes/56kog/top.ts b/lib/routes/56kog/top.ts index c0472c594..07701cb83 100644 --- a/lib/routes/56kog/top.ts +++ b/lib/routes/56kog/top.ts @@ -1,11 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { rootUrl, fetchItems } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/top/:category?', + categories: ['government'], + example: '/56kog/top/weekvisit', + parameters: { category: '分类,见下表,默认为周点击榜' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '榜单', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const { category = 'weekvisit' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; const currentUrl = new URL(`top/${category.split(/_/)[0]}_1.html`, rootUrl).href; ctx.set('data', await fetchItems(limit, currentUrl, cache.tryGet)); -}; +} diff --git a/lib/routes/591/list.ts b/lib/routes/591/list.ts index d97b6836f..4c6b287c8 100644 --- a/lib/routes/591/list.ts +++ b/lib/routes/591/list.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -106,7 +107,25 @@ async function getHouseList(houseListURL) { const renderHouse = (house) => art(path.join(__dirname, 'templates/house.art'), { house }); -export default async (ctx) => { +export const route: Route = { + path: '/:country/rent/:query?', + categories: ['other'], + example: '/591/tw/rent/order=posttime&orderType=desc', + parameters: { country: 'Country code. Only tw is supported now', query: 'Query Parameters' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Rental house', + maintainers: ['Yukaii'], + handler, +}; + +async function handler(ctx) { const query = ctx.req.param('query') ?? ''; const country = ctx.req.param('country') ?? 'tw'; @@ -134,14 +153,14 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: '591 租屋 - 自訂查詢', link: queryUrl, description: `591 租屋 - 自訂查詢, query: ${query}`, item: items, - }); + }; ctx.set('json', { houses, }); -}; +} diff --git a/lib/routes/5eplay/index.ts b/lib/routes/5eplay/index.ts index 86e81b640..a66ff652f 100644 --- a/lib/routes/5eplay/index.ts +++ b/lib/routes/5eplay/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { load } from 'cheerio'; import zlib from 'zlib'; @@ -6,7 +7,28 @@ import { parseDate } from '@/utils/parse-date'; import { config } from '@/config'; import { getAcwScV2ByArg1 } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/article', + categories: ['reading'], + example: '/5eplay/article', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['csgo.5eplay.com/', 'csgo.5eplay.com/article'], + }, + name: '新闻列表', + maintainers: ['Dlouxgit'], + handler, +}; + +async function handler() { const rootUrl = 'https://csgo.5eplay.com/'; const apiUrl = `${rootUrl}api/article?page=1&type_id=0&time=0&order_by=0`; const articleUrl = `${rootUrl}article`; @@ -93,9 +115,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '5EPLAY', link: 'https://csgo.5eplay.com/article', item: items, - }); -}; + }; +} diff --git a/lib/routes/6park/index.ts b/lib/routes/6park/index.ts index b48fc200e..8a63fd9e0 100644 --- a/lib/routes/6park/index.ts +++ b/lib/routes/6park/index.ts @@ -1,10 +1,22 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:id?/:type?/:keyword?', + radar: { + source: ['club.6parkbbs.com/:id/index.php', 'club.6parkbbs.com/'], + target: '/:id?', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? 'chan1'; const type = ctx.req.param('type') ?? ''; const keyword = ctx.req.param('keyword') ?? ''; @@ -58,9 +70,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/6park/news.ts b/lib/routes/6park/news.ts index b10b8cba2..0523b2667 100644 --- a/lib/routes/6park/news.ts +++ b/lib/routes/6park/news.ts @@ -1,10 +1,22 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/news/:site?/:id?/:keyword?', + radar: { + source: ['club.6parkbbs.com/:id/index.php', 'club.6parkbbs.com/'], + target: '/:id?', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const site = ctx.req.param('site') ?? 'newspark'; const id = ctx.req.param('id') ?? ''; const keyword = ctx.req.param('keyword') ?? ''; @@ -67,9 +79,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/6v123/latest-movies.ts b/lib/routes/6v123/latest-movies.ts index 563d9dc6f..4a2b70c47 100644 --- a/lib/routes/6v123/latest-movies.ts +++ b/lib/routes/6v123/latest-movies.ts @@ -1,14 +1,36 @@ +import { Route } from '@/types'; import { processItems } from './utils'; const baseURL = 'https://www.hao6v.cc/gvod/zx.html'; -export default async (ctx) => { +export const route: Route = { + path: '/latestMovies', + categories: ['picture'], + example: '/6v123/latestMovies', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: true, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['hao6v.com/', 'hao6v.com/gvod/zx.html'], + }, + name: '最新电影', + maintainers: ['tc9011'], + handler, +}; + +async function handler(ctx) { const item = await processItems(ctx, baseURL); - ctx.set('data', { + return { title: '6v电影-最新电影', link: baseURL, description: '6v最新电影RSS', item, - }); -}; + }; +} diff --git a/lib/routes/6v123/latest-tvseries.ts b/lib/routes/6v123/latest-tvseries.ts index 4ac742dd7..808d2e6ab 100644 --- a/lib/routes/6v123/latest-tvseries.ts +++ b/lib/routes/6v123/latest-tvseries.ts @@ -1,14 +1,36 @@ +import { Route } from '@/types'; import { processItems } from './utils'; const baseURL = 'https://www.hao6v.tv/gvod/dsj.html'; -export default async (ctx) => { +export const route: Route = { + path: '/latestTVSeries', + categories: ['picture'], + example: '/6v123/latestTVSeries', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: true, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['hao6v.com/', 'hao6v.com/gvod/dsj.html'], + }, + name: '最新电视剧', + maintainers: ['tc9011'], + handler, +}; + +async function handler(ctx) { const item = await processItems(ctx, baseURL); - ctx.set('data', { + return { title: '6v电影-最新电影', link: baseURL, description: '6v最新电影RSS', item, - }); -}; + }; +} diff --git a/lib/routes/78dm/index.ts b/lib/routes/78dm/index.ts index c1b3d7e0c..6a8c20cd6 100644 --- a/lib/routes/78dm/index.ts +++ b/lib/routes/78dm/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -10,7 +11,14 @@ import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const rootUrl = 'https://www.78dm.net'; const currentUrl = `${rootUrl}${getSubPath(ctx) === '/' ? '/news' : /\/\d+$/.test(getSubPath(ctx)) ? `${getSubPath(ctx)}.html` : getSubPath(ctx)}`; @@ -71,9 +79,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `78动漫 - ${$('title').text().split('_')[0]} - ${$('.actived').first().text()}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/7mmtv/index.ts b/lib/routes/7mmtv/index.ts index 41a3dc0c9..262187e7d 100644 --- a/lib/routes/7mmtv/index.ts +++ b/lib/routes/7mmtv/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -8,7 +9,25 @@ import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/:language?/:category?/:type?', + categories: ['picture'], + example: '/7mmtv/zh/censored_list/all', + parameters: { language: 'Language, see below, `en` as English by default', category: 'Category, see below, `censored_list` as Censored by default', type: 'Server, see below, all server by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Category', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const language = ctx.req.param('language') ?? 'en'; const category = ctx.req.param('category') ?? 'censored_list'; const type = ctx.req.param('type') ?? 'all'; @@ -71,12 +90,12 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title') .text() .replace(/ - Watch JAV Online/, ''), link: currentUrl, item: items, description: $('meta[name="description"]').attr('content'), - }); -}; + }; +} diff --git a/lib/routes/81/81rc/index.ts b/lib/routes/81/81rc/index.ts index 1cd637595..fe56b0af5 100644 --- a/lib/routes/81/81rc/index.ts +++ b/lib/routes/81/81rc/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getSubPath } from '@/utils/common-utils'; import cache from '@/utils/cache'; import got from '@/utils/got'; @@ -5,7 +6,14 @@ import { load } from 'cheerio'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/81rc/*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const thePath = getSubPath(ctx).replace(/^\/81rc/, ''); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; @@ -48,7 +56,7 @@ export default async (ctx) => { const icon = $('link[rel="icon"]').prop('href'); - ctx.set('data', { + return { item: items, title: `军队人才网 - ${$('div.left-word') .find('a') @@ -60,5 +68,5 @@ export default async (ctx) => { language: 'zh-cn', icon, logo: icon, - }); -}; + }; +} diff --git a/lib/routes/8264/list.ts b/lib/routes/8264/list.ts index 339575922..d2be1de1b 100644 --- a/lib/routes/8264/list.ts +++ b/lib/routes/8264/list.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -10,7 +11,25 @@ import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/list/:id?', + categories: ['blog'], + example: '/8264/list/751', + parameters: { id: '列表 id,见下表,默认为 751,即热门推荐' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '列表', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const { id = '751' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; @@ -82,7 +101,7 @@ export default async (ctx) => { const description = $('meta[name="description"]').prop('content').trim(); const icon = new URL('favicon', rootUrl).href; - ctx.set('data', { + return { item: items, title: `${$('span.country, h2').text()} - ${description.split(',').pop()}`, link: currentUrl, @@ -92,5 +111,5 @@ export default async (ctx) => { logo: icon, subtitle: $('meta[name="keywords"]').prop('content').trim(), author: $('meta[name="author"]').prop('content'), - }); -}; + }; +} diff --git a/lib/routes/8kcos/cat.ts b/lib/routes/8kcos/cat.ts index 32607dac0..733fc49c0 100644 --- a/lib/routes/8kcos/cat.ts +++ b/lib/routes/8kcos/cat.ts @@ -1,16 +1,28 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { SUB_NAME_PREFIX, SUB_URL } from './const'; import loadArticle from './article'; -export default async (ctx) => { +export const route: Route = { + path: '/cat/:cat{.+}?', + radar: { + source: ['8kcosplay.com/'], + target: '', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const limit = Number.parseInt(ctx.req.query('limit')); const { cat = '8kasianidol' } = ctx.req.param(); const url = `${SUB_URL}category/${cat}/`; const resp = await got(url); const $ = load(resp.body); const itemRaw = $('li.item').toArray(); - ctx.set('data', { + return { title: `${SUB_NAME_PREFIX}-${$('span[property=name]:not(.hide)').text()}`, link: url, item: await Promise.all( @@ -19,5 +31,5 @@ export default async (ctx) => { return cache.tryGet(href, () => loadArticle(href)); }) ), - }); -}; + }; +} diff --git a/lib/routes/8kcos/latest.ts b/lib/routes/8kcos/latest.ts index 096114346..799ebfa77 100644 --- a/lib/routes/8kcos/latest.ts +++ b/lib/routes/8kcos/latest.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -5,11 +6,33 @@ import { SUB_NAME_PREFIX, SUB_URL } from './const'; import loadArticle from './article'; const url = SUB_URL; -export default async (ctx) => { +export const route: Route = { + path: '/', + categories: ['anime'], + example: '/8kcos/', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['8kcosplay.com/'], + target: '', + }, + name: '最新', + maintainers: ['KotoriK'], + handler, +}; + +async function handler(ctx) { const limit = Number.parseInt(ctx.req.query('limit')); const response = await got(url); const itemRaw = load(response.body)('ul.post-loop li.item').toArray(); - ctx.set('data', { + return { title: `${SUB_NAME_PREFIX}-最新`, link: url, item: @@ -20,5 +43,5 @@ export default async (ctx) => { return cache.tryGet(href, () => loadArticle(href)); }) )), - }); -}; + }; +} diff --git a/lib/routes/8kcos/tag.ts b/lib/routes/8kcos/tag.ts index 7f7a2e2ce..176a2f569 100644 --- a/lib/routes/8kcos/tag.ts +++ b/lib/routes/8kcos/tag.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { SUB_NAME_PREFIX, SUB_URL } from './const'; import loadArticle from './article'; -export default async (ctx) => { +export const route: Route = { + path: '/tag/:tag', + categories: ['anime'], + example: '/8kcos/tag/cosplay', + parameters: { tag: '标签名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['8kcosplay.com/tag/:tag'], + }, + name: '标签', + maintainers: ['KotoriK'], + handler, +}; + +async function handler(ctx) { const limit = Number.parseInt(ctx.req.query('limit')); const tag = ctx.req.param('tag'); const url = `${SUB_URL}tag/${tag}/`; @@ -11,7 +33,7 @@ export default async (ctx) => { const $ = load(resp.body); const itemRaw = $('li.item').toArray(); - ctx.set('data', { + return { title: `${SUB_NAME_PREFIX}-${$('span[property=name]:not(.hide)').text()}`, link: url, item: await Promise.all( @@ -20,5 +42,5 @@ export default async (ctx) => { return cache.tryGet(href, () => loadArticle(href)); }) ), - }); -}; + }; +} diff --git a/lib/routes/8world/index.ts b/lib/routes/8world/index.ts index 69e5191b0..603050800 100644 --- a/lib/routes/8world/index.ts +++ b/lib/routes/8world/index.ts @@ -1,10 +1,18 @@ +import { Route } from '@/types'; import { getSubPath } from '@/utils/common-utils'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const path = getSubPath(ctx) === '/' ? '/realtime' : getSubPath(ctx); const rootUrl = 'https://www.8world.com'; @@ -51,9 +59,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/91porn/author.ts b/lib/routes/91porn/author.ts index bfb2ea58f..571e95d73 100644 --- a/lib/routes/91porn/author.ts +++ b/lib/routes/91porn/author.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -9,7 +10,29 @@ import { art } from '@/utils/render'; import * as path from 'node:path'; import { domainValidation } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/author/:uid/:lang?', + categories: ['picture'], + example: '/91porn/author/2d6d2iWm4vVCwqujAZbSrKt2QJCbbaObv9HQ21Zo8wGJWudWBg', + parameters: { uid: 'Author ID, can be found in URL', lang: 'Language, see above, `en_US` by default ' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['91porn.com/index.php'], + target: '', + }, + name: 'Author', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { domain = '91porn.com' } = ctx.req.query(); const { uid, lang = 'en_US' } = ctx.req.param(); const siteUrl = `https://${domain}/uvideos.php?UID=${uid}&type=public`; @@ -56,9 +79,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('.login_register_header').text()} - 91porn`, link: siteUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/91porn/index.ts b/lib/routes/91porn/index.ts index 51cd9fe9d..d1fd47bc1 100644 --- a/lib/routes/91porn/index.ts +++ b/lib/routes/91porn/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -9,7 +10,29 @@ import { art } from '@/utils/render'; import * as path from 'node:path'; import { domainValidation } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/:lang?', + categories: ['picture'], + example: '/91porn', + parameters: { lang: 'Language, see below, `en_US` by default ' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['91porn.com/index.php'], + target: '', + }, + name: 'Hot Video Today', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { domain = '91porn.com' } = ctx.req.query(); const siteUrl = `https://${domain}/index.php`; const { lang = 'en_US' } = ctx.req.param(); @@ -56,9 +79,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('.login_register_header').text()} - 91porn`, link: siteUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/95mm/category.ts b/lib/routes/95mm/category.ts index 5c8940d84..1cd61df25 100644 --- a/lib/routes/95mm/category.ts +++ b/lib/routes/95mm/category.ts @@ -1,6 +1,28 @@ +import { Route } from '@/types'; import { rootUrl, ProcessItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/category/:category', + categories: ['anime'], + example: '/95mm/category/1', + parameters: { category: '集合,见下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['95mm.org/'], + }, + name: '集合', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const categories = { 1: '清纯唯美', 2: '摄影私房', @@ -17,4 +39,4 @@ export default async (ctx) => { const currentUrl = `${rootUrl}/category-${category}/list-1/index.html?page=1`; ctx.set('data', await ProcessItems(ctx, categories[category], currentUrl)); -}; +} diff --git a/lib/routes/95mm/tab.ts b/lib/routes/95mm/tab.ts index 44fab7df1..03f2314f6 100644 --- a/lib/routes/95mm/tab.ts +++ b/lib/routes/95mm/tab.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import { rootUrl, ProcessItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/tab/:tab?', + categories: ['anime'], + example: '/95mm/tab/热门', + parameters: { tab: '分类,见下表,默认为最新' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['95mm.org/'], + }, + name: '分类', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const tab = ctx.req.param('tab') ?? '最新'; const currentUrl = `${rootUrl}/home-ajax/index.html?tabcid=${tab}&page=1`; ctx.set('data', await ProcessItems(ctx, tab, currentUrl)); -}; +} diff --git a/lib/routes/95mm/tag.ts b/lib/routes/95mm/tag.ts index 77491f35c..508ddea9a 100644 --- a/lib/routes/95mm/tag.ts +++ b/lib/routes/95mm/tag.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import { rootUrl, ProcessItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/tag/:tag', + categories: ['anime'], + example: '/95mm/tag/黑丝', + parameters: { tag: '标签,可在对应标签页中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['95mm.org/'], + }, + name: '标签', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const tag = ctx.req.param('tag'); const currentUrl = `${rootUrl}/tag-${tag}/page-1/index.html`; ctx.set('data', await ProcessItems(ctx, tag, currentUrl)); -}; +} diff --git a/lib/routes/9to5/subsite.ts b/lib/routes/9to5/subsite.ts index 793679bfd..45f0d6290 100644 --- a/lib/routes/9to5/subsite.ts +++ b/lib/routes/9to5/subsite.ts @@ -1,9 +1,17 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import parser from '@/utils/rss-parser'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/:subsite/:tag?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { let title = '9To5', link, description; @@ -62,10 +70,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title, link, description, item: items, - }); -}; + }; +} diff --git a/lib/routes/a9vg/a9vg.ts b/lib/routes/a9vg/a9vg.ts index 6dddbaa52..e7ce3a71b 100644 --- a/lib/routes/a9vg/a9vg.ts +++ b/lib/routes/a9vg/a9vg.ts @@ -1,9 +1,21 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import * as cheerio from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['a9vg.com/list/news', 'a9vg.com/'], + target: '', + }, + name: 'Unknown', + maintainers: ['monnerHenster'], + handler, +}; + +async function handler() { const baseUrl = 'http://www.a9vg.com'; const link = `${baseUrl}/list/news`; const { data } = await got(link); @@ -21,10 +33,10 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: 'A9VG 电玩部落', link, description: $('meta[name="description"]').attr('content'), item: list, - }); -}; + }; +} diff --git a/lib/routes/aamacau/index.ts b/lib/routes/aamacau/index.ts index 805945303..95d4a1c82 100644 --- a/lib/routes/aamacau/index.ts +++ b/lib/routes/aamacau/index.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:category?/:id?', + categories: ['traditional-media'], + example: '/aamacau', + parameters: { category: '分类,见下表,默认为即時報道', id: 'id,可在对应页面 URL 中找到,默认为空' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['aamacau.com/'], + }, + name: '话题', + maintainers: [], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'topics'; const id = ctx.req.param('id') ?? ''; @@ -49,9 +71,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/abc/index.ts b/lib/routes/abc/index.ts index 57df64f9a..5389a8b0a 100644 --- a/lib/routes/abc/index.ts +++ b/lib/routes/abc/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -8,7 +9,18 @@ import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/:category{.+}?', + radar: { + source: ['abc.net.au/:category*'], + target: '/:category', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { category = 'news/justin' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; @@ -149,7 +161,7 @@ export default async (ctx) => { const icon = new URL($('link[rel="apple-touch-icon"]').prop('href'), rootUrl).href; - ctx.set('data', { + return { item: items, title: $('title').first().text(), link: currentUrl, @@ -161,5 +173,5 @@ export default async (ctx) => { subtitle: $('meta[property="og:title"]').prop('content'), author: $('meta[name="generator"]').prop('content'), allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/abmedia/category.ts b/lib/routes/abmedia/category.ts index 31623f5dd..d5a7a1a5c 100644 --- a/lib/routes/abmedia/category.ts +++ b/lib/routes/abmedia/category.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; @@ -7,7 +8,29 @@ const postsAPIUrl = `${rootUrl}/wp-json/wp/v2/posts`; const getCategoryId = (category) => got.get(`${cateAPIUrl}?slug=${category}`).then((res) => res.data[0].id); -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + categories: ['traditional-media'], + example: '/abmedia/technology-development', + parameters: { category: '类别,默认为产品技术' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.abmedia.io/category/:catehory'], + target: '/:category', + }, + name: '类别', + maintainers: [], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'technology-development'; const limit = ctx.req.param('limit') ?? 25; const categoryId = await getCategoryId(category); @@ -22,9 +45,9 @@ export default async (ctx) => { pubDate: parseDate(item.date), })); - ctx.set('data', { + return { title: `abmedia - ${category}`, link: `${rootUrl}/category/${category}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/abmedia/index.ts b/lib/routes/abmedia/index.ts index 1bb71e38e..493aac221 100644 --- a/lib/routes/abmedia/index.ts +++ b/lib/routes/abmedia/index.ts @@ -1,10 +1,32 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; const rootUrl = 'https://www.abmedia.io'; const postsAPIUrl = `${rootUrl}/wp-json/wp/v2/posts`; -export default async (ctx) => { +export const route: Route = { + path: '/index', + categories: ['traditional-media'], + example: '/abmedia/index', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.abmedia.io/'], + }, + name: '首页最新新闻', + maintainers: [], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.param('limit') ?? 10; const url = `${postsAPIUrl}?per_page=${limit}`; @@ -18,9 +40,9 @@ export default async (ctx) => { pubDate: parseDate(item.date), })); - ctx.set('data', { + return { title: 'ABMedia - 最新消息', link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/abskoop/index.ts b/lib/routes/abskoop/index.ts index abc26382c..bfb620c60 100644 --- a/lib/routes/abskoop/index.ts +++ b/lib/routes/abskoop/index.ts @@ -1,9 +1,21 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['ahhhhfs.com/'], + target: '', + }, + name: 'Unknown', + maintainers: ['zhenhappy'], + handler, +}; + +async function handler(ctx) { const response = await got({ method: 'get', url: 'https://www.ahhhhfs.com/wp-json/wp/v2/posts', @@ -38,11 +50,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'ahhhhfs-A姐分享', link: 'https://www.ahhhhfs.com', description: 'A姐分享,分享各种网络云盘资源、BT种子、高清电影电视剧和羊毛福利,收集各种有趣实用的软件和APP的下载、安装、使用方法,发现一些稀奇古怪的的网站,折腾一些有趣实用的教程,关注谷歌苹果等互联网最新的资讯动态,探索新领域,发现新美好,分享小快乐。', item: items, - }); -}; + }; +} diff --git a/lib/routes/abskoop/nsfw.ts b/lib/routes/abskoop/nsfw.ts index cd31412a3..8222c8d4d 100644 --- a/lib/routes/abskoop/nsfw.ts +++ b/lib/routes/abskoop/nsfw.ts @@ -1,7 +1,19 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/nsfw', + radar: { + source: ['ahhhhfs.com/'], + target: '', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const response = await got({ method: 'get', url: 'https://nsfw.abskoop.com/wp-json/wp/v2/posts', @@ -21,11 +33,11 @@ export default async (ctx) => { category: [...new Set(item._embedded['wp:term'].flatMap((i) => i.map((j) => j.name)))], })); - ctx.set('data', { + return { title: 'ahhhhfs-A姐分享NSFW', link: 'https://nsfw.ahhhhfs.com/articles-archive', description: 'A姐分享NSFW,分享各种网络云盘资源、BT种子、磁力链接、高清电影电视剧和羊毛福利,收集各种有趣实用的软件和APP的下载、安装、使用方法,发现一些稀奇古怪的的网站,折腾一些有趣实用的教程,关注谷歌苹果等互联网最新的资讯动态,探索新领域,发现新美好,分享小快乐。', item: list, - }); -}; + }; +} diff --git a/lib/routes/acfun/article.ts b/lib/routes/acfun/article.ts index 1d1c8d8b3..34066c108 100644 --- a/lib/routes/acfun/article.ts +++ b/lib/routes/acfun/article.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -33,7 +34,25 @@ const categoryMap = { const sortTypeEnum = new Set(['createTime', 'lastCommentTime', 'hotScore']); const timeRangeEnum = new Set(['all', 'oneDay', 'threeDay', 'oneWeek', 'oneMonth']); -export default async (ctx) => { +export const route: Route = { + path: '/article/:categoryId/:sortType?/:timeRange?', + categories: ['program-update'], + example: '/acfun/article/110', + parameters: { categoryId: '分区 ID,见下表', sortType: '排序,见下表,默认为 `createTime`', timeRange: '时间范围,见下表,仅在排序是 `hotScore` 有效,默认为 `all`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '文章', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { categoryId, sortType = 'createTime', timeRange = 'all' } = ctx.req.param(); if (!categoryMap[categoryId]) { throw new Error(`Invalid category Id: ${categoryId}`); @@ -93,9 +112,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: categoryMap[categoryId].title, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/acfun/bangumi.ts b/lib/routes/acfun/bangumi.ts index cf0a9daae..f4b20f388 100644 --- a/lib/routes/acfun/bangumi.ts +++ b/lib/routes/acfun/bangumi.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/bangumi/:id', + categories: ['program-update'], + example: '/acfun/bangumi/5022158', + parameters: { id: '番剧 id' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '番剧', + maintainers: ['xyqfer'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const url = `https://www.acfun.cn/bangumi/aa${id}`; @@ -13,7 +32,7 @@ export default async (ctx) => { const bangumiData = JSON.parse(bangumiPage.data.match(/window.bangumiData = (.*?);\n/)[1]); const bangumiList = JSON.parse(bangumiPage.data.match(/window.bangumiList = (.*?);\n/)[1]); - ctx.set('data', { + return { title: bangumiData.bangumiTitle, link: url, description: bangumiData.bangumiIntro, @@ -24,5 +43,5 @@ export default async (ctx) => { link: `http://www.acfun.cn/bangumi/aa${id}_36188_${item.itemId}`, pubDate: parseDate(item.updateTime, 'x'), })), - }); -}; + }; +} diff --git a/lib/routes/acfun/video.ts b/lib/routes/acfun/video.ts index f4b7cf4f5..4edb1a353 100644 --- a/lib/routes/acfun/video.ts +++ b/lib/routes/acfun/video.ts @@ -1,8 +1,20 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/user/video/:uid', + radar: { + source: ['www.acfun.cn/u/:id'], + target: '/user/video/:id', + }, + name: 'Unknown', + maintainers: ['wdssmq'], + handler, +}; + +async function handler(ctx) { const uid = ctx.req.param('uid'); const url = `https://www.acfun.cn/u/${uid}`; const host = 'https://www.acfun.cn'; @@ -21,7 +33,7 @@ export default async (ctx) => { .text() .match(/.user-photo{\n\s*background:url\((.*)\) 0% 0% \/ 100% no-repeat;/)[1]; - ctx.set('data', { + return { title, link: url, description, @@ -41,5 +53,5 @@ export default async (ctx) => { pubDate: parseDate(itemDate, 'YYYY/MM/DD'), }; }), - }); -}; + }; +} diff --git a/lib/routes/acg17/post.ts b/lib/routes/acg17/post.ts index 8f54088da..cd7baa4de 100644 --- a/lib/routes/acg17/post.ts +++ b/lib/routes/acg17/post.ts @@ -1,12 +1,34 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; const host = 'http://acg17.com'; -export default async (ctx) => { +export const route: Route = { + path: '/post/all', + categories: ['program-update'], + example: '/acg17/post/all', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['acg17.com/post'], + }, + name: '全部文章', + maintainers: ['SunBK201'], + handler, +}; + +async function handler() { const response = await got(`${host}/wp-json/wp/v2/posts?per_page=30`); const list = response.data; - ctx.set('data', { + return { title: `ACG17 - 全部文章`, link: `${host}/blog`, description: 'ACG17 - 全部文章', @@ -16,5 +38,5 @@ export default async (ctx) => { pubDate: parseDate(item.date_gmt), description: item.content.rendered, })), - }); -}; + }; +} diff --git a/lib/routes/acpaa/index.ts b/lib/routes/acpaa/index.ts index eec9d8a66..37e141e49 100644 --- a/lib/routes/acpaa/index.ts +++ b/lib/routes/acpaa/index.ts @@ -1,10 +1,29 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:id?/:name?', + categories: ['other'], + example: '/acpaa', + parameters: { id: '标签 id,默认为 1,可在对应标签页 URL 中找到', name: '标签名称,默认为重要通知,可在对应标签页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '标签', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const { id = '1', name = '重要通知' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; @@ -46,7 +65,7 @@ export default async (ctx) => { const author = $('title').text().replaceAll('-', ''); const subtitle = $('span.myTitle').text().trim(); - ctx.set('data', { + return { item: items, title: `${author} - ${subtitle}`, link: currentUrl, @@ -54,5 +73,5 @@ export default async (ctx) => { language: 'zh', subtitle, author, - }); -}; + }; +} diff --git a/lib/routes/acs/journal.ts b/lib/routes/acs/journal.ts index 2d26b0370..e52f06cc5 100644 --- a/lib/routes/acs/journal.ts +++ b/lib/routes/acs/journal.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -9,7 +10,17 @@ import * as path from 'node:path'; import { config } from '@/config'; import puppeteer from '@/utils/puppeteer'; -export default async (ctx) => { +export const route: Route = { + path: '/journal/:id', + radar: { + source: ['pubs.acs.org/journal/:id', 'pubs.acs.org/'], + }, + name: 'Unknown', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? ''; const rootUrl = 'https://pubs.acs.org'; @@ -70,9 +81,9 @@ export default async (ctx) => { await browser.close(); - ctx.set('data', { + return { title, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/aeaweb/index.ts b/lib/routes/aeaweb/index.ts index c1ea0379d..bac2de5d2 100644 --- a/lib/routes/aeaweb/index.ts +++ b/lib/routes/aeaweb/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -8,7 +9,28 @@ import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/:id', + categories: ['finance'], + example: '/aeaweb/aer', + parameters: { id: 'Journal id, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: true, + }, + radar: { + source: ['aeaweb.org/journals/:id', 'aeaweb.org/'], + }, + name: 'Journal', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { let id = ctx.req.param('id'); const rootUrl = 'https://www.aeaweb.org'; @@ -76,10 +98,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title, description, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/aeon/category.ts b/lib/routes/aeon/category.ts index 913647ec5..2b4ec6613 100644 --- a/lib/routes/aeon/category.ts +++ b/lib/routes/aeon/category.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import { load } from 'cheerio'; import got from '@/utils/got'; import { getData } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/category/:category', + categories: ['traditional-media'], + example: '/aeon/category/philosophy', + parameters: { category: 'Category' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['aeon.aeon.co/:category'], + }, + name: 'Categories', + maintainers: ['emdoe'], + handler, +}; + +async function handler(ctx) { const url = `https://aeon.co/${ctx.req.param('category')}`; const { data: response } = await got(url); const $ = load(response); @@ -18,10 +40,10 @@ export default async (ctx) => { const items = await getData(ctx, list); - ctx.set('data', { + return { title: `AEON | ${data.props.pageProps.section.title}`, link: url, description: data.props.pageProps.section.metaDescription, item: items, - }); -}; + }; +} diff --git a/lib/routes/aeon/type.ts b/lib/routes/aeon/type.ts index b2c575901..8ad34aec7 100644 --- a/lib/routes/aeon/type.ts +++ b/lib/routes/aeon/type.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import { load } from 'cheerio'; import got from '@/utils/got'; import { getData } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/:type', + categories: ['traditional-media'], + example: '/aeon/essays', + parameters: { type: 'Type' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['aeon.aeon.co/:type'], + }, + name: 'Types', + maintainers: ['emdoe'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); const binaryType = type === 'videos' ? 'videos' : 'essays'; const capitalizedType = type.charAt(0).toUpperCase() + type.slice(1); @@ -21,9 +43,9 @@ export default async (ctx) => { const items = await getData(ctx, list); - ctx.set('data', { + return { title: `AEON | ${capitalizedType}`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/agefans/detail.ts b/lib/routes/agefans/detail.ts index 0fb4d508e..482e3b2c9 100644 --- a/lib/routes/agefans/detail.ts +++ b/lib/routes/agefans/detail.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { rootUrl } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/detail/:id', + categories: ['program-update'], + example: '/agefans/detail/20200035', + parameters: { id: '番剧 id,对应详情 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['agemys.org/detail/:id'], + }, + name: '番剧详情', + maintainers: ['s2marine'], + handler, +}; + +async function handler(ctx) { const response = await got(`${rootUrl}/detail/${ctx.req.param('id')}`); const $ = load(response.data); @@ -22,10 +44,10 @@ export default async (ctx) => { }) .reverse(); - ctx.set('data', { + return { title: `AGE动漫 - ${ldJson.name}`, link: `${rootUrl}/detail/${ctx.req.param('id')}`, description: ldJson.description, item: items, - }); -}; + }; +} diff --git a/lib/routes/agefans/update.ts b/lib/routes/agefans/update.ts index 2099f4e39..42d869eeb 100644 --- a/lib/routes/agefans/update.ts +++ b/lib/routes/agefans/update.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { rootUrl } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/update', + categories: ['program-update'], + example: '/agefans/update', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['agemys.org/update', 'agemys.org/'], + }, + name: '最近更新', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const currentUrl = `${rootUrl}/update`; const response = await got(currentUrl); @@ -42,9 +64,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/agirls/index.ts b/lib/routes/agirls/index.ts index 6447ed91e..1fb9bba20 100644 --- a/lib/routes/agirls/index.ts +++ b/lib/routes/agirls/index.ts @@ -1,9 +1,32 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { baseUrl, parseArticle } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + categories: ['traditional-media'], + example: '/agirls/app', + parameters: { category: '分类,默认为最新文章,可在对应主题页的 URL 中找到,下表仅列出部分' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['agirls.aotter.net/posts/:category'], + target: '/:category', + }, + name: '分类', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { category = '' } = ctx.req.param(); const link = `${baseUrl}/posts${category ? `/${category}` : ''}`; const response = await got(link); @@ -22,11 +45,11 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => cache.tryGet(item.link, () => parseArticle(item)))); - ctx.set('data', { + return { title: $('head title').text().trim(), link, description: $('head meta[name=description]').attr('content'), item: items, language: $('html').attr('lang'), - }); -}; + }; +} diff --git a/lib/routes/agirls/topic-list.ts b/lib/routes/agirls/topic-list.ts index 7d35b54a7..ee014d677 100644 --- a/lib/routes/agirls/topic-list.ts +++ b/lib/routes/agirls/topic-list.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { baseUrl } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/topic_list', + categories: ['traditional-media'], + example: '/agirls/topic_list', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['agirls.aotter.net/', 'agirls.aotter.net/topic'], + }, + name: '当前精选主题列表', + maintainers: ['TonyRL'], + handler, +}; + +async function handler() { const category = 'topic'; const link = `${baseUrl}/${category}`; @@ -21,11 +43,11 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('head title').text().trim(), link, description: $('head meta[name=description]').attr('content'), item: items, language: $('html').attr('lang'), - }); -}; + }; +} diff --git a/lib/routes/agirls/topic.ts b/lib/routes/agirls/topic.ts index 46d71e8ef..563808bb2 100644 --- a/lib/routes/agirls/topic.ts +++ b/lib/routes/agirls/topic.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { baseUrl, parseArticle } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/topic/:topic', + categories: ['traditional-media'], + example: '/agirls/topic/AppleWatch', + parameters: { topic: '精选主题,可通过下方精选主题列表获得' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['agirls.aotter.net/topic/:topic'], + }, + name: '精选主题', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const topic = ctx.req.param('topic'); const link = `${baseUrl}/topic/${topic}`; const response = await got(link); @@ -22,11 +44,11 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => cache.tryGet(item.link, () => parseArticle(item)))); - ctx.set('data', { + return { title: $('head title').text().trim(), link, description: ldJson['@graph'][0].description, item: items, language: $('html').attr('lang'), - }); -}; + }; +} diff --git a/lib/routes/agora0/index.ts b/lib/routes/agora0/index.ts index 5ca24ae56..d039ebf9a 100644 --- a/lib/routes/agora0/index.ts +++ b/lib/routes/agora0/index.ts @@ -1,9 +1,32 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + categories: ['traditional-media'], + example: '/agora0/initium', + parameters: { category: '分类,见下表,默认为 initium,即端传媒' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['agora0.gitlab.io/blog/:category', 'agora0.gitlab.io/'], + target: '/:category', + }, + name: '零博客', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'initium'; const rootUrl = 'https://agora0.gitlab.io'; @@ -47,9 +70,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/agora0/pen0.ts b/lib/routes/agora0/pen0.ts index 3f0ce2e94..438deab44 100644 --- a/lib/routes/agora0/pen0.ts +++ b/lib/routes/agora0/pen0.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/pen0', + categories: ['traditional-media'], + example: '/agora0/pen0', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['agorahub.github.io/pen0'], + }, + name: '共和報', + maintainers: ['TonyRL'], + handler, +}; + +async function handler() { const baseUrl = 'https://agorahub.github.io'; const response = await got(`${baseUrl}/pen0/`); const $ = load(response.data); @@ -34,11 +56,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), description: $('head meta[name="description"]').attr('content'), link: response.url, image: $('link[rel="apple-touch-icon"]').attr('href'), item: items, - }); -}; + }; +} diff --git a/lib/routes/ahjzu/news.ts b/lib/routes/ahjzu/news.ts index f04646f89..472aa2af5 100644 --- a/lib/routes/ahjzu/news.ts +++ b/lib/routes/ahjzu/news.ts @@ -1,10 +1,32 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/news', + categories: ['forecast'], + example: '/ahjzu/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['news.ahjzu.edu.cn/20/list.htm'], + }, + name: '通知公告', + maintainers: ['Yuk-0v0'], + handler, +}; + +async function handler() { const rootUrl = 'https://www.ahjzu.edu.cn'; const currentUrl = 'https://www.ahjzu.edu.cn/20/list.htm'; @@ -49,10 +71,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '安建大-通知公告', description: '安徽建筑大学-通知公告', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/aicaijing/index.ts b/lib/routes/aicaijing/index.ts index c437003e2..6c7e7519d 100644 --- a/lib/routes/aicaijing/index.ts +++ b/lib/routes/aicaijing/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -6,7 +7,14 @@ import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/:category?/:id?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'latest'; const id = ctx.req.param('id') ?? 14; @@ -68,9 +76,9 @@ export default async (ctx) => { }), })); - ctx.set('data', { + return { title: `AI 财经社 - ${categories[category].title}`, link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/aiea/index.ts b/lib/routes/aiea/index.ts index 3a2c15779..61c5820e5 100644 --- a/lib/routes/aiea/index.ts +++ b/lib/routes/aiea/index.ts @@ -1,6 +1,25 @@ +import { Route } from '@/types'; import buildData from '@/utils/common-config'; -export default async (ctx) => { +export const route: Route = { + path: '/seminars/:period', + categories: ['journal'], + example: '/aiea/seminars/upcoming', + parameters: { period: 'Time frame' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Seminar Series', + maintainers: ['zxx-457'], + handler, +}; + +async function handler(ctx) { const link = 'http://www.aiea.org/0504'; const period = ctx.req.param('period') ?? ''; @@ -39,4 +58,4 @@ export default async (ctx) => { }, }) ); -}; +} diff --git a/lib/routes/aijishu/index.ts b/lib/routes/aijishu/index.ts index bd0e989f6..205a1a869 100644 --- a/lib/routes/aijishu/index.ts +++ b/lib/routes/aijishu/index.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import utils from './utils'; import got from '@/utils/got'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/:type/:name?', + categories: ['design'], + example: '/aijishu/channel/ai', + parameters: { type: '文章类型,可以取值如下', name: '名字,取自URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '频道、专栏、用户', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { type, name = 'newest' } = ctx.req.param(); const u = name === 'newest' ? `https://aijishu.com/` : `https://aijishu.com/${type}/${name}`; const html = await got(u); @@ -18,9 +37,9 @@ export default async (ctx) => { const items = await Promise.all(list.filter((item) => item?.url?.startsWith('/a/') || item?.object?.url.startsWith('/a/')).map((item) => utils.parseArticle(item))); - ctx.set('data', { + return { title: title.split(' - ').slice(0, 2).join(' - '), link: u, item: items, - }); -}; + }; +} diff --git a/lib/routes/ainvest/article.ts b/lib/routes/ainvest/article.ts index 9c13fc101..d68164de2 100644 --- a/lib/routes/ainvest/article.ts +++ b/lib/routes/ainvest/article.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { getHeaders, randomString, encryptAES, decryptAES } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/article', + categories: ['other'], + example: '/ainvest/article', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ainvest.com/news'], + }, + name: 'Latest Article', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const key = randomString(16); const { data: response } = await got.post('https://api.ainvest.com/gw/socialcenter/v1/edu/article/listArticle', { @@ -33,10 +55,10 @@ export default async (ctx) => { category: [item.nickName, ...item.tags.map((tag) => tag.code)], })); - ctx.set('data', { + return { title: 'AInvest - Latest Articles', link: 'https://www.ainvest.com/news', language: 'en', item: items, - }); -}; + }; +} diff --git a/lib/routes/ainvest/news.ts b/lib/routes/ainvest/news.ts index 41bbcbd44..ae08597ea 100644 --- a/lib/routes/ainvest/news.ts +++ b/lib/routes/ainvest/news.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { getHeaders, randomString, decryptAES } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/news', + categories: ['other'], + example: '/ainvest/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ainvest.com/news'], + }, + name: 'Latest News', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const key = randomString(16); const { data: response } = await got('https://api.ainvest.com/gw/news_f10/v1/newsFlash/getNewsData', { @@ -30,10 +52,10 @@ export default async (ctx) => { comments: item.commentCount, })); - ctx.set('data', { + return { title: 'AInvest - Latest News', link: 'https://www.ainvest.com/news', language: 'en', item: items, - }); -}; + }; +} diff --git a/lib/routes/aip/journal-pupp.ts b/lib/routes/aip/journal-pupp.ts index b9f578d45..aca60597b 100644 --- a/lib/routes/aip/journal-pupp.ts +++ b/lib/routes/aip/journal-pupp.ts @@ -51,10 +51,10 @@ export default async (ctx) => { browser.close(); - ctx.set('data', { + return { title: jrnlName, link: jrnlUrl, item: list, allowEmpty: true, - }); + }; }; diff --git a/lib/routes/aip/journal.ts b/lib/routes/aip/journal.ts index 04e1d2598..a45633082 100644 --- a/lib/routes/aip/journal.ts +++ b/lib/routes/aip/journal.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { renderDesc } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/:pub/:jrn', + categories: ['finance'], + example: '/aip/aapt/ajp', + parameters: { pub: 'Publisher id', jrn: 'Journal id' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: true, + }, + radar: { + source: ['pubs.aip.org/:pub/:jrn'], + }, + name: 'Journal', + maintainers: ['Derekmini', 'auto-bot-ty'], + handler, +}; + +async function handler(ctx) { const pub = ctx.req.param('pub'); const jrn = ctx.req.param('jrn'); const host = `https://pubs.aip.org`; @@ -46,10 +68,10 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title: jrnlName, link: jrnlUrl, item: list, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/airchina/index.ts b/lib/routes/airchina/index.ts index a019b490c..4ab4e4d19 100644 --- a/lib/routes/airchina/index.ts +++ b/lib/routes/airchina/index.ts @@ -1,10 +1,32 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import buildData from '@/utils/common-config'; const baseUrl = 'https://www.airchina.com.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/announcement', + categories: ['shopping'], + example: '/airchina/announcement', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.airchina.com.cn/'], + }, + name: '服务公告', + maintainers: ['LandonLi'], + handler, +}; + +async function handler(ctx) { const link = `${baseUrl}/cn/info/new-service/service_announcement.shtml`; const data = await buildData({ link, @@ -36,4 +58,4 @@ export default async (ctx) => { ); ctx.set('data', data); -}; +} diff --git a/lib/routes/aisixiang/column.ts b/lib/routes/aisixiang/column.ts index 4e799f7dc..9dbaeb2e9 100644 --- a/lib/routes/aisixiang/column.ts +++ b/lib/routes/aisixiang/column.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -6,7 +7,25 @@ import { parseDate } from '@/utils/parse-date'; import { rootUrl, ossUrl, ProcessFeed } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/column/:id', + categories: ['government'], + example: '/aisixiang/column/722', + parameters: { id: '栏目 ID, 可在对应栏目 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '栏目', + maintainers: ['HenryQW', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; @@ -34,7 +53,7 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { item: await ProcessFeed(limit, cache.tryGet, items), title: `爱思想 - ${title}`, link: currentUrl, @@ -42,5 +61,5 @@ export default async (ctx) => { language: 'zh-cn', image: new URL('images/logo.jpg', ossUrl).href, subtitle: title, - }); -}; + }; +} diff --git a/lib/routes/aisixiang/thinktank.ts b/lib/routes/aisixiang/thinktank.ts index c9a5882c1..16b67da82 100644 --- a/lib/routes/aisixiang/thinktank.ts +++ b/lib/routes/aisixiang/thinktank.ts @@ -1,10 +1,29 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { rootUrl, ossUrl, ProcessFeed } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/thinktank/:id/:type?', + categories: ['government'], + example: '/aisixiang/thinktank/WuQine/论文', + parameters: { id: '专栏 ID,一般为作者拼音,可在URL中找到', type: '栏目类型,参考下表,默认为全部' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '思想库(专栏)', + maintainers: ['hoilc', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const { id, type = '' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; @@ -38,7 +57,7 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { item: await ProcessFeed(limit, cache.tryGet, items), title: `爱思想 - ${title}`, link: currentUrl, @@ -46,5 +65,5 @@ export default async (ctx) => { language: 'zh-cn', image: new URL('images/logo_thinktank.jpg', ossUrl).href, subtitle: title, - }); -}; + }; +} diff --git a/lib/routes/aisixiang/toplist.ts b/lib/routes/aisixiang/toplist.ts index 1232d778f..5b5ac1c24 100644 --- a/lib/routes/aisixiang/toplist.ts +++ b/lib/routes/aisixiang/toplist.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -5,7 +6,14 @@ import { parseDate } from '@/utils/parse-date'; import { rootUrl, ossUrl, ProcessFeed } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: ['/ranking/:id?/:period?', '/toplist/:id?/:period?'], + name: 'Unknown', + maintainers: ['HenryQW', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const { id = '1', period = '1' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; @@ -33,12 +41,12 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { item: await ProcessFeed(limit, cache.tryGet, items), title: `爱思想 - ${title}`, link: currentUrl, language: 'zh-cn', image: new URL('images/logo_toplist.jpg', ossUrl).href, subtitle: title, - }); -}; + }; +} diff --git a/lib/routes/aisixiang/zhuanti.ts b/lib/routes/aisixiang/zhuanti.ts index bf992209a..d6444b45b 100644 --- a/lib/routes/aisixiang/zhuanti.ts +++ b/lib/routes/aisixiang/zhuanti.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -6,7 +7,25 @@ import { parseDate } from '@/utils/parse-date'; import { rootUrl, ossUrl, ProcessFeed } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/zhuanti/:id', + categories: ['government'], + example: '/aisixiang/zhuanti/211', + parameters: { id: '专题 ID, 可在对应专题 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '专题', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; @@ -34,7 +53,7 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { item: await ProcessFeed(limit, cache.tryGet, items), title: `爱思想 - ${title}`, link: currentUrl, @@ -42,5 +61,5 @@ export default async (ctx) => { language: 'zh-cn', image: new URL('images/logo_zhuanti.jpg', ossUrl).href, subtitle: title, - }); -}; + }; +} diff --git a/lib/routes/ajmide/index.ts b/lib/routes/ajmide/index.ts index 6ab15dd41..4c4649812 100644 --- a/lib/routes/ajmide/index.ts +++ b/lib/routes/ajmide/index.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:id', + categories: ['picture'], + example: '/ajmide/10603594', + parameters: { id: '播客 id,可以从播客页面 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '播客', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const limit = ctx.req.param('limit') ?? 25; const playListAPI = `https://a.ajmide.com/v3/getBrandContentList.php?brandId=${id}&c=${limit}&i=0`; @@ -19,11 +38,11 @@ export default async (ctx) => { enclosure_type: 'audio/x-m4a', })); - ctx.set('data', { + return { title: data[0].brandName, link: `https://m.ajmide.com/m/brand?id=${id}`, itunes_author: data[0].author_info.nick, image: data[0].brandImgPath, item: items, - }); -}; + }; +} diff --git a/lib/routes/aliresearch/information.ts b/lib/routes/aliresearch/information.ts index 38670d543..ce9470c3c 100644 --- a/lib/routes/aliresearch/information.ts +++ b/lib/routes/aliresearch/information.ts @@ -1,9 +1,32 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/information/:type?', + categories: ['traditional-media'], + example: '/aliresearch/information', + parameters: { type: '类型,见下表,默认为新闻' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['aliresearch.com/cn/information', 'aliresearch.com/'], + target: '/information', + }, + name: '资讯', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') ?? '新闻'; const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 50; @@ -50,9 +73,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `阿里研究院 - ${type}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/alistapart/index.ts b/lib/routes/alistapart/index.ts index d9b6a6ef3..d1e97d0c4 100644 --- a/lib/routes/alistapart/index.ts +++ b/lib/routes/alistapart/index.ts @@ -1,13 +1,25 @@ +import { Route } from '@/types'; import { getData, getList } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['alistapart.com/articles/'], + target: '', + }, + name: 'Unknown', + maintainers: ['Rjnishant530'], + handler, +}; + +async function handler() { const baseUrl = 'https://alistapart.com'; const route = '/wp-json/wp/v2/article?_embed'; const data = await getData(`${baseUrl}${route}`); const items = await getList(data); - ctx.set('data', { + return { title: 'A List Apart', link: `${baseUrl}/articles`, item: items, @@ -15,5 +27,5 @@ export default async (ctx) => { logo: 'https://i0.wp.com/alistapart.com/wp-content/uploads/2019/03/cropped-icon_navigation-laurel-512.jpg?fit=192,192&ssl=1', icon: 'https://i0.wp.com/alistapart.com/wp-content/uploads/2019/03/cropped-icon_navigation-laurel-512.jpg?fit=32,32&ssl=1', language: 'en-us', - }); -}; + }; +} diff --git a/lib/routes/alistapart/topic.ts b/lib/routes/alistapart/topic.ts index 58fc2ccdf..dec87a84a 100644 --- a/lib/routes/alistapart/topic.ts +++ b/lib/routes/alistapart/topic.ts @@ -1,6 +1,28 @@ +import { Route } from '@/types'; import { getData, getList } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/:topic', + categories: ['design'], + example: '/alistapart/application-development', + parameters: { topic: 'Any Topic or from the table below. Defaults to All Articles' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['alistapart.com/blog/topic/:topic'], + }, + name: 'Topics', + maintainers: ['Rjnishant530'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://alistapart.com'; const searchRoute = '/wp-json/wp/v2/categories?slug='; const articleRoute = '/wp-json/wp/v2/article?categories='; @@ -9,7 +31,7 @@ export default async (ctx) => { const data = await getData(`${baseUrl}${articleRoute}${id}&_embed`); const items = await getList(data); - ctx.set('data', { + return { title: 'A List Apart', link: `${baseUrl}/blog/topic/${topic}`, item: items, @@ -17,5 +39,5 @@ export default async (ctx) => { logo: 'https://i0.wp.com/alistapart.com/wp-content/uploads/2019/03/cropped-icon_navigation-laurel-512.jpg?fit=192,192&ssl=1', icon: 'https://i0.wp.com/alistapart.com/wp-content/uploads/2019/03/cropped-icon_navigation-laurel-512.jpg?fit=32,32&ssl=1', language: 'en-us', - }); -}; + }; +} diff --git a/lib/routes/aliyun/database-month.ts b/lib/routes/aliyun/database-month.ts index bc407336e..4471cadf5 100644 --- a/lib/routes/aliyun/database-month.ts +++ b/lib/routes/aliyun/database-month.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/database_month', + categories: ['design'], + example: '/aliyun/database_month', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['mysql.taobao.org/monthly', 'mysql.taobao.org/'], + }, + name: '数据库内核月报', + maintainers: ['junbaor'], + handler, +}; + +async function handler() { const url = 'http://mysql.taobao.org/monthly/'; const response = await got({ method: 'get', url }); const $ = load(response.data); @@ -33,9 +55,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: $('title').text(), link: url, item: result.reverse(), - }); -}; + }; +} diff --git a/lib/routes/aliyun/developer/group.ts b/lib/routes/aliyun/developer/group.ts index 74cb5a150..ab0cf0360 100644 --- a/lib/routes/aliyun/developer/group.ts +++ b/lib/routes/aliyun/developer/group.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/developer/group/:type', + categories: ['design'], + example: '/aliyun/developer/group/alitech', + parameters: { type: '对应技术领域分类' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['developer.aliyun.com/group/:type'], + }, + name: '开发者社区 - 主题', + maintainers: ['umm233'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); const link = `https://developer.aliyun.com/group/${type}`; @@ -26,7 +48,7 @@ export default async (ctx) => { const desc = $('div[class="header-information"]').find('span').last().text().trim(); const list = $('ul[class^="content-tab-list"] > li'); - ctx.set('data', { + return { title: `阿里云开发者社区-${title}`, link, description: desc, @@ -45,5 +67,5 @@ export default async (ctx) => { }; }) .get(), - }); -}; + }; +} diff --git a/lib/routes/aliyun/notice.ts b/lib/routes/aliyun/notice.ts index 73a8fdaa6..976bb8be4 100644 --- a/lib/routes/aliyun/notice.ts +++ b/lib/routes/aliyun/notice.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -16,7 +17,25 @@ const typeMap = { * * @param ctx {import('koa').Context} */ -export default async (ctx) => { +export const route: Route = { + path: '/notice/:type?', + categories: ['design'], + example: '/aliyun/notice', + parameters: { type: 'N' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '公告', + maintainers: ['muzea'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); const url = `https://help.aliyun.com/noticelist/${typeMap[type] || typeMap[0]}.html`; const response = await got({ method: 'get', url }); @@ -49,9 +68,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text().trim(), link: url, item: result, - }); -}; + }; +} diff --git a/lib/routes/aljazeera/index.ts b/lib/routes/aljazeera/index.ts index f150cc1a7..1bf0fa10e 100644 --- a/lib/routes/aljazeera/index.ts +++ b/lib/routes/aljazeera/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -24,7 +25,14 @@ const languages = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const params = getSubPath(ctx) === '/' ? ['arabic'] : getSubPath(ctx).replace(/^\//, '').split('/'); if (!Object.hasOwn(languages, params[0])) { @@ -84,9 +92,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').first().text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/ally/rail.ts b/lib/routes/ally/rail.ts index d1328889d..813b98608 100644 --- a/lib/routes/ally/rail.ts +++ b/lib/routes/ally/rail.ts @@ -1,10 +1,32 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { load } from 'cheerio'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -export default async (ctx) => { +export const route: Route = { + path: '/rail/:category?/:topic?', + categories: ['traditional-media'], + example: '/ally/rail/hyzix/chengguijiaotong/', + parameters: { category: '分类,可在 URL 中找到;略去则抓取首页', topic: '话题,可在 URL 中找到;并非所有页面均有此字段' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['rail.ally.net.cn/', 'rail.ally.net.cn/html/:category?/:topic?'], + }, + name: '世界轨道交通资讯网', + maintainers: ['Rongronggg9'], + handler, +}; + +async function handler(ctx) { // http://rail.ally.net.cn/sitemap.html const { category, topic } = ctx.req.param(); const rootUrl = 'http://rail.ally.net.cn'; @@ -100,10 +122,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `世界轨道交通资讯网 - ${title}`, link: pageUrl, item: items, description: $('head > meta[name="description"]').attr('content'), - }); -}; + }; +} diff --git a/lib/routes/alternativeto/platform.ts b/lib/routes/alternativeto/platform.ts index ee7086c60..63ca69c62 100644 --- a/lib/routes/alternativeto/platform.ts +++ b/lib/routes/alternativeto/platform.ts @@ -1,8 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { baseURL, puppeteerGet } from './utils'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/platform/:name/:routeParams?', + categories: ['design'], + example: '/alternativeto/platform/firefox', + parameters: { name: 'Platform name', routeParams: 'Filters of software type' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.alternativeto.net/platform/:name'], + target: '/platform/:name', + }, + name: 'Platform Software', + maintainers: ['JimenezLi'], + handler, +}; + +async function handler(ctx) { const name = ctx.req.param('name'); const query = new URLSearchParams(ctx.req.param('routeParams')); const link = `https://alternativeto.net/platform/${name}/?${query.toString()}`; @@ -11,7 +34,7 @@ export default async (ctx) => { const html = await puppeteerGet(link, cache); const $ = load(html); - ctx.set('data', { + return { title: $('.Heading_h1___Cf5Y').text().trim(), description: $('.intro-text').text().trim(), link, @@ -29,5 +52,5 @@ export default async (ctx) => { description, }; }), - }); -}; + }; +} diff --git a/lib/routes/alternativeto/software.ts b/lib/routes/alternativeto/software.ts index f3ac2e50c..8e09bceee 100644 --- a/lib/routes/alternativeto/software.ts +++ b/lib/routes/alternativeto/software.ts @@ -1,8 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { baseURL, puppeteerGet } from './utils'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/software/:name/:routeParams?', + categories: ['design'], + example: '/alternativeto/software/cpp', + parameters: { name: 'Software name', routeParams: 'Filters of software type' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.alternativeto.net/software/:name'], + target: '/software/:name', + }, + name: 'Software Alternatives', + maintainers: ['JimenezLi'], + handler, +}; + +async function handler(ctx) { const name = ctx.req.param('name'); const query = new URLSearchParams(ctx.req.param('routeParams')); const link = `https://alternativeto.net/software/${name}/?${query.toString()}`; @@ -11,7 +34,7 @@ export default async (ctx) => { const html = await puppeteerGet(link, cache); const $ = load(html); - ctx.set('data', { + return { title: $('.Heading_h1___Cf5Y').text().trim(), description: $('.intro-text').text().trim(), link, @@ -29,5 +52,5 @@ export default async (ctx) => { description, }; }), - }); -}; + }; +} diff --git a/lib/routes/amazon/awsblogs.ts b/lib/routes/amazon/awsblogs.ts index 6e95d15a0..8fcdc32f0 100644 --- a/lib/routes/amazon/awsblogs.ts +++ b/lib/routes/amazon/awsblogs.ts @@ -1,7 +1,15 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/awsblogs/:locale?', + name: 'Unknown', + maintainers: ['HankChow'], + handler, +}; + +async function handler(ctx) { const locale = ctx.req.param('locale') ?? 'zh_CN'; const response = await got({ @@ -10,7 +18,7 @@ export default async (ctx) => { const items = response.data.items; - ctx.set('data', { + return { title: 'AWS Blog', link: 'https://aws.amazon.com/blogs/', description: 'AWS Blog 更新', @@ -23,5 +31,5 @@ export default async (ctx) => { link: item.item.additionalFields.link, author: item.item.additionalFields.contributors, })), - }); -}; + }; +} diff --git a/lib/routes/amazon/kindle-software-updates.ts b/lib/routes/amazon/kindle-software-updates.ts index 74ed721b4..74a940182 100644 --- a/lib/routes/amazon/kindle-software-updates.ts +++ b/lib/routes/amazon/kindle-software-updates.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -6,7 +7,25 @@ import { art } from '@/utils/render'; import * as path from 'node:path'; import { load } from 'cheerio'; const host = 'https://www.amazon.com'; -export default async (ctx) => { +export const route: Route = { + path: '/kindle/software-updates', + categories: ['university'], + example: '/amazon/kindle/software-updates', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Kindle Software Updates', + maintainers: ['EthanWng97'], + handler, +}; + +async function handler() { const url = host + '/gp/help/customer/display.html'; const nodeIdValue = 'GKMQC26VQQMM8XSW'; const response = await got({ @@ -33,7 +52,7 @@ export default async (ctx) => { return data; }) .get(); - ctx.set('data', { + return { title: 'Kindle E-Reader Software Updates', link: `${url}?nodeId=${nodeIdValue}`, description: 'Kindle E-Reader Software Updates', @@ -47,5 +66,5 @@ export default async (ctx) => { guid: item.title + ' - ' + item.version, link: item.link, })), - }); -}; + }; +} diff --git a/lib/routes/android/platform-tools-releases.ts b/lib/routes/android/platform-tools-releases.ts index 1eaf72d10..cebb924a4 100644 --- a/lib/routes/android/platform-tools-releases.ts +++ b/lib/routes/android/platform-tools-releases.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/platform-tools-releases', + categories: ['university'], + example: '/android/platform-tools-releases', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['developer.android.com/studio/releases/platform-tools', 'developer.android.com/'], + }, + name: 'SDK Platform Tools release notes', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://developer.android.com'; const currentUrl = `${rootUrl}/studio/releases/platform-tools`; @@ -39,9 +61,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/annualreviews/index.ts b/lib/routes/annualreviews/index.ts index 5a078badd..ff5c5d82d 100644 --- a/lib/routes/annualreviews/index.ts +++ b/lib/routes/annualreviews/index.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:id', + categories: ['finance'], + example: '/annualreviews/anchem', + parameters: { id: 'Journal id, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: true, + }, + radar: { + source: ['annualreviews.org/journal/:id', 'annualreviews.org/'], + }, + name: 'Journal', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const rootUrl = 'https://www.annualreviews.org'; @@ -57,7 +79,7 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title') .first() .text() @@ -65,5 +87,5 @@ export default async (ctx) => { description: $('subtitle').first().text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/anquanke/category.ts b/lib/routes/anquanke/category.ts index 73bf77399..964158ef8 100644 --- a/lib/routes/anquanke/category.ts +++ b/lib/routes/anquanke/category.ts @@ -1,10 +1,29 @@ +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'; -export default async (ctx) => { +export const route: Route = { + path: '/:category/:fulltext?', + categories: ['design'], + example: '/anquanke/week', + parameters: { category: '分类订阅', fulltext: '是否获取全文,如需获取全文参数传入 `quanwen` 或 `fulltext`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '分类订阅', + maintainers: ['qwertyuiop6'], + handler, +}; + +async function handler(ctx) { const api = 'https://api.anquanke.com/data/v1/posts?size=10&page=1&category='; const type = ctx.req.param('category'); const fulltext = ctx.req.param('fulltext'); @@ -32,9 +51,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `安全客-${dataArray[0].category_name}`, link: `https://www.anquanke.com/${type === 'week' ? 'week-list' : type}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/anquanke/vul.ts b/lib/routes/anquanke/vul.ts index e77742cd5..f50bb86b7 100644 --- a/lib/routes/anquanke/vul.ts +++ b/lib/routes/anquanke/vul.ts @@ -2,7 +2,7 @@ import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export default async () => { const url = 'https://www.anquanke.com'; const response = await got(`${url}/vul`); @@ -26,9 +26,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: '安全客-漏洞cve报告', link: 'https://www.anquanke.com/vul', item: items, - }); + }; }; diff --git a/lib/routes/apache/apisix/blog.ts b/lib/routes/apache/apisix/blog.ts index 6cf820db5..1ea6b25d8 100644 --- a/lib/routes/apache/apisix/blog.ts +++ b/lib/routes/apache/apisix/blog.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; @@ -22,11 +23,29 @@ async function getArticles() { }); } -export default async (ctx) => { +export const route: Route = { + path: '/apisix/blog', + categories: ['programming'], + example: '/apache/apisix/blog', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'APISIX 博客', + maintainers: ['aneasystone'], + handler, +}; + +async function handler() { const articles = await getArticles(); - ctx.set('data', { + return { title: 'Blog | Apache APISIX', link: 'https://apisix.apache.org/zh/blog/', item: articles, - }); -}; + }; +} diff --git a/lib/routes/apiseven/blog.ts b/lib/routes/apiseven/blog.ts index 4cdbed77a..03b7027b1 100644 --- a/lib/routes/apiseven/blog.ts +++ b/lib/routes/apiseven/blog.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -22,7 +23,25 @@ async function getArticles() { })); } -export default async (ctx) => { +export const route: Route = { + path: '/blog', + categories: ['programming'], + example: '/apiseven/blog', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '博客', + maintainers: ['aneasystone'], + handler, +}; + +async function handler() { const articles = await getArticles(); const items = await Promise.all( articles.map((item) => @@ -41,9 +60,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '博客 | 支流科技', link: 'https://www.apiseven.com/blog', item: items, - }); -}; + }; +} diff --git a/lib/routes/apkpure/versions.ts b/lib/routes/apkpure/versions.ts index f1b40401b..37ab1db90 100644 --- a/lib/routes/apkpure/versions.ts +++ b/lib/routes/apkpure/versions.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import { load } from 'cheerio'; import logger from '@/utils/logger'; import { parseDate } from '@/utils/parse-date'; import puppeteer from '@/utils/puppeteer'; -export default async (ctx) => { +export const route: Route = { + path: '/versions/:pkg/:region?', + categories: ['university'], + example: '/apkpure/versions/jp.co.craftegg.band/jp', + parameters: { pkg: 'Package name', region: 'Region code, `en` by default' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Versions', + maintainers: ['maple3142'], + handler, +}; + +async function handler(ctx) { const { pkg, region = 'en' } = ctx.req.param(); const baseUrl = 'https://apkpure.com'; const link = `${baseUrl}/${region}/${pkg}/versions`; @@ -38,12 +57,12 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('.ver-top-h1').text(), description: $('.ver-top-title p').text(), image: img.href, language: region ?? 'en', link, item: items, - }); -}; + }; +} diff --git a/lib/routes/apnews/topics.ts b/lib/routes/apnews/topics.ts index 81e700607..ac8389785 100644 --- a/lib/routes/apnews/topics.ts +++ b/lib/routes/apnews/topics.ts @@ -1,10 +1,33 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; const HOME_PAGE = 'https://apnews.com'; -export default async (ctx) => { +export const route: Route = { + path: '/topics/:topic?', + categories: ['bbs'], + example: '/apnews/topics/apf-topnews', + parameters: { topic: 'Topic name, can be found in URL. For example: the topic name of AP Top News [https://apnews.com/apf-topnews](https://apnews.com/apf-topnews) is `apf-topnews`, `trending-news` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['apnews.com/hub/:topic'], + target: '/topics/:topic', + }, + name: 'Topics', + maintainers: ['zoenglinghou', 'mjysci', 'TonyRL'], + handler, +}; + +async function handler(ctx) { const { topic = 'trending-news' } = ctx.req.param(); const url = `${HOME_PAGE}/hub/${topic}`; const response = await got(url); @@ -35,11 +58,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), description: $("meta[property='og:description']").text(), link: url, item: items, language: $('html').attr('lang'), - }); -}; + }; +} diff --git a/lib/routes/app-center/release.ts b/lib/routes/app-center/release.ts index d2b42376c..1598174e2 100644 --- a/lib/routes/app-center/release.ts +++ b/lib/routes/app-center/release.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -8,7 +9,28 @@ import { art } from '@/utils/render'; import * as path from 'node:path'; import MarkdownIt from 'markdown-it'; -export default async (ctx) => { +export const route: Route = { + path: '/release/:user/:app/:distribution_group', + categories: ['university'], + example: '/app-center/release/cloudflare/1.1.1.1-windows/beta', + parameters: { user: 'User', app: 'App name', distribution_group: 'Distribution group' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['install.appcenter.ms/users/:user/apps/:app/distribution_groups/:distribution_group', 'install.appcenter.ms/orgs/:user/apps/:app/distribution_groups/:distribution_group'], + }, + name: 'Release', + maintainers: ['Rongronggg9'], + handler, +}; + +async function handler(ctx) { const user = ctx.req.param('user'); const app = ctx.req.param('app'); const distribution_group = ctx.req.param('distribution_group'); @@ -114,11 +136,11 @@ export default async (ctx) => { const icon = items && items[0]._feed_icon; // if it is an empty feed, would not raise an error here const title = items && items[0]._feed_title; - ctx.set('data', { + return { title, link, description: title, image: icon, item: items, - }); -}; + }; +} diff --git a/lib/routes/apple/apps.ts b/lib/routes/apple/apps.ts index 21587c346..5d549a596 100644 --- a/lib/routes/apple/apps.ts +++ b/lib/routes/apple/apps.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; @@ -14,7 +15,29 @@ const platforms = { tvos: 'appletvos', }; -export default async (ctx) => { +export const route: Route = { + path: '/apps/update/:country/:id/:platform?', + categories: ['university'], + example: '/apple/apps/update/us/id408709785', + parameters: { country: 'App Store Country, obtain from the app URL, see below', id: 'App id, obtain from the app URL', platform: 'App Platform, see below, all by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['apps.apple.com/:country/app/:appSlug/:id', 'apps.apple.com/:country/app/:id'], + target: '/apps/update/:country/:id', + }, + name: 'App Update', + maintainers: ['EkkoG', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const { country, id } = ctx.req.param(); let { platform } = ctx.req.param(); @@ -87,7 +110,7 @@ export default async (ctx) => { const icon = new URL('favicon.ico', rootUrl).href; - ctx.set('data', { + return { item: items, title, link: currentUrl, @@ -99,9 +122,9 @@ export default async (ctx) => { subtitle: appName, author: artistName, allowEmpty: true, - }); + }; ctx.set('json', { appData, }); -}; +} diff --git a/lib/routes/apple/exchange-repair.ts b/lib/routes/apple/exchange-repair.ts index 9a3852ddc..7c47aec50 100644 --- a/lib/routes/apple/exchange-repair.ts +++ b/lib/routes/apple/exchange-repair.ts @@ -1,10 +1,33 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; const host = 'https://support.apple.com/'; -export default async (ctx) => { +export const route: Route = { + path: '/exchange_repair/:country?', + categories: ['other'], + example: '/apple/exchange_repair', + parameters: { country: 'country code in apple.com URL (exception: for `United States` please use `us`), default to China `cn`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['support.apple.com/:country/service-programs'], + target: '/exchange_repair/:country', + }, + name: 'Exchange and Repair Extension Programs', + maintainers: ['metowolf', 'HenryQW', 'kt286'], + handler, +}; + +async function handler(ctx) { const country = ctx.req.param('country') ?? ''; const link = new URL(`${country}/service-programs`, host).href; @@ -40,9 +63,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `Apple - ${$('h1.as-center').text()}`, link, item: out, - }); -}; + }; +} diff --git a/lib/routes/appleinsider/index.ts b/lib/routes/appleinsider/index.ts index 394d84001..c9b019fb2 100644 --- a/lib/routes/appleinsider/index.ts +++ b/lib/routes/appleinsider/index.ts @@ -1,9 +1,32 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + categories: ['traditional-media'], + example: '/appleinsider', + parameters: { category: 'Category, see below, News by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['appleinsider.com/:category', 'appleinsider.com/'], + target: '/:category', + }, + name: 'Category', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? ''; const rootUrl = 'https://appleinsider.com'; @@ -52,9 +75,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/appstore/in-app-purchase.ts b/lib/routes/appstore/in-app-purchase.ts index 1a293ae9d..ce5fd2e9a 100644 --- a/lib/routes/appstore/in-app-purchase.ts +++ b/lib/routes/appstore/in-app-purchase.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import * as url from 'node:url'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/iap/:country/:id', + categories: ['university'], + example: '/appstore/iap/us/id953286746', + parameters: { + country: 'App Store Country, obtain from the app URL https://apps.apple.com/us/app/id953286746, in this case, `us`', + id: 'App Store app id, obtain from the app URL https://apps.apple.com/us/app/id953286746, in this case, `id953286746`', + }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'In-App-Purchase Price Drop Alert', + maintainers: ['HenryQW'], + handler, +}; + +async function handler(ctx) { const country = ctx.req.param('country'); const id = ctx.req.param('id'); const link = `https://apps.apple.com/${country}/app/${id}`; @@ -50,9 +72,9 @@ export default async (ctx) => { }); } - ctx.set('data', { + return { title, link, item, - }); -}; + }; +} diff --git a/lib/routes/appstore/price.ts b/lib/routes/appstore/price.ts index 785b5edc7..4876eaf53 100644 --- a/lib/routes/appstore/price.ts +++ b/lib/routes/appstore/price.ts @@ -1,6 +1,32 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import currency from 'currency-symbol-map'; -export default async (ctx) => { +export const route: Route = { + path: '/price/:country/:type/:id', + categories: ['university'], + example: '/appstore/price/us/mac/id1152443474', + parameters: { + country: 'App Store Country, obtain from the app URL https://apps.apple.com/us/app/id1152443474, in this case, `us`', + type: 'App type,either `iOS` or `mac`', + id: 'App Store app id, obtain from the app URL https://apps.apple.com/us/app/id1152443474, in this case, `id1152443474`', + }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['apps.apple.com/'], + }, + name: 'Price Drop', + maintainers: ['HenryQW'], + handler, +}; + +async function handler(ctx) { const country = ctx.req.param('country'); const type = ctx.req.param('type').toLowerCase() === 'mac' ? 'macapps' : 'apps'; const id = ctx.req.param('id').replace('id', ''); @@ -17,10 +43,10 @@ export default async (ctx) => { if (!res.data.results) { const unsupported = '当前 app 未被收录. Price monitor is not available for this app.'; - ctx.set('data', { + return { title: unsupported, item: [{ title: unsupported }], - }); + }; return; } @@ -44,10 +70,10 @@ export default async (ctx) => { }; item.push(single); } - ctx.set('data', { + return { title, link, item, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/appstore/xianmian.ts b/lib/routes/appstore/xianmian.ts index a95cfca21..c63506afe 100644 --- a/lib/routes/appstore/xianmian.ts +++ b/lib/routes/appstore/xianmian.ts @@ -1,12 +1,34 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/xianmian', + categories: ['university'], + example: '/appstore/xianmian', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['app.so/xianmian'], + }, + name: '每日精品限免 / 促销应用(鲜面连线 by AppSo)', + maintainers: ['Andiedie'], + handler, +}; + +async function handler() { const { data: { objects: data }, } = await got.get('https://app.so/api/v5/appso/discount/?platform=web&limit=10'); - ctx.set('data', { + return { title: '每日精品限免 / 促销应用', link: 'http://app.so/xianmian/', description: '鲜面连线 by AppSo:每日精品限免 / 促销应用', @@ -24,5 +46,5 @@ export default async (ctx) => { pubDate: parseDate(item.updated_at * 1000), link: item.app.download_link[0].link, })), - }); -}; + }; +} diff --git a/lib/routes/aqara/community.ts b/lib/routes/aqara/community.ts index 6a235812e..dfeaf2a8a 100644 --- a/lib/routes/aqara/community.ts +++ b/lib/routes/aqara/community.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/community/:id?/:keyword?', + categories: ['other'], + example: '/aqara/community', + parameters: { id: '分类 id,可在对应分类页 URL 中找到,默认为全部', keyword: '关键字,默认为空' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '社区', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? ''; const keyword = ctx.req.param('keyword') ?? ''; const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 100; @@ -23,10 +42,10 @@ export default async (ctx) => { author: item.user.nickname, })); - ctx.set('data', { + return { title: 'Aqara社区', link: currentUrl, item: items, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/aqara/news.ts b/lib/routes/aqara/news.ts index fab74cfdb..f812916c7 100644 --- a/lib/routes/aqara/news.ts +++ b/lib/routes/aqara/news.ts @@ -1,9 +1,17 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/cn/news', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 35; const rootUrl = 'https://www.aqara.cn'; @@ -40,7 +48,7 @@ export default async (ctx) => { const icon = $('link[rel="shortcut icon"]').prop('href').split('?')[0]; - ctx.set('data', { + return { item: items, title: $('title').text(), link: currentUrl, @@ -50,5 +58,5 @@ export default async (ctx) => { icon, logo: icon, author: $('meta[name="author"]').prop('content'), - }); -}; + }; +} diff --git a/lib/routes/aqara/post.ts b/lib/routes/aqara/post.ts index d89d218fb..ca35c1ea3 100644 --- a/lib/routes/aqara/post.ts +++ b/lib/routes/aqara/post.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -8,7 +9,14 @@ import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 50; const rootUrl = 'https://aqara.com'; @@ -81,7 +89,7 @@ export default async (ctx) => { const icon = $('link[rel="apple-touch-icon"]').first().prop('href'); const title = $('meta[property="og:site_name"]').prop('content') ?? 'Aqara'; - ctx.set('data', { + return { item: items, title: `${title}${filterName ? ` - ${filterName}` : ''}`, link: currentUrl, @@ -92,5 +100,5 @@ export default async (ctx) => { logo: icon, subtitle: $('meta[property="og:type"]').prop('content'), author: title, - }); -}; + }; +} diff --git a/lib/routes/aqara/region.ts b/lib/routes/aqara/region.ts index 5ba4f259f..e3c20484d 100644 --- a/lib/routes/aqara/region.ts +++ b/lib/routes/aqara/region.ts @@ -1,4 +1,12 @@ -export default (ctx) => { +import { Route } from '@/types'; +export const route: Route = { + path: '/:region/:type?', + name: 'Unknown', + maintainers: [], + handler, +}; + +function handler(ctx) { const types = { news: 'press-release', blog: 'article', @@ -7,4 +15,4 @@ export default (ctx) => { const { region = 'en', type = 'news' } = ctx.req.param(); const redirectTo = `/aqara/${region}/category/${types[type]}`; ctx.redirect(redirectTo); -}; +} diff --git a/lib/routes/arcteryx/new-arrivals.ts b/lib/routes/arcteryx/new-arrivals.ts index ab922491d..22ab1c73d 100644 --- a/lib/routes/arcteryx/new-arrivals.ts +++ b/lib/routes/arcteryx/new-arrivals.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -6,7 +7,28 @@ import { art } from '@/utils/render'; import * as path from 'node:path'; import { generateRssData } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/new-arrivals/:country/:gender', + categories: ['game'], + example: '/arcteryx/new-arrivals/us/mens', + parameters: { country: 'country', gender: 'gender' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['arcteryx.com/:country/en/c/:gender/new-arrivals'], + }, + name: 'New Arrivals', + maintainers: ['EthanWng97'], + handler, +}; + +async function handler(ctx) { const { country, gender } = ctx.req.param(); const host = `https://arcteryx.com/${country}/en/`; const url = `${host}api/fredhopper/query`; @@ -23,7 +45,7 @@ export default async (ctx) => { }); const items = response.data.universes.universe[1]['items-section'].items.item.map((item, index, arr) => generateRssData(item, index, arr, country)); - ctx.set('data', { + return { title: `Arcteryx - New Arrivals(${country.toUpperCase()}) - ${gender.toUpperCase()}`, link: pageUrl, description: `Arcteryx - New Arrivals(${country.toUpperCase()}) - ${gender.toUpperCase()}`, @@ -34,5 +56,5 @@ export default async (ctx) => { item, }), })), - }); -}; + }; +} diff --git a/lib/routes/arcteryx/outlet.ts b/lib/routes/arcteryx/outlet.ts index d7e1db5d3..1f1359827 100644 --- a/lib/routes/arcteryx/outlet.ts +++ b/lib/routes/arcteryx/outlet.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -6,7 +7,28 @@ import { art } from '@/utils/render'; import * as path from 'node:path'; import { generateRssData } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/outlet/:country/:gender', + categories: ['game'], + example: '/arcteryx/outlet/us/mens', + parameters: { country: 'country', gender: 'gender' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['outlet.arcteryx.com/:country/en/c/:gender'], + }, + name: 'Outlet', + maintainers: ['EthanWng97'], + handler, +}; + +async function handler(ctx) { const { country, gender } = ctx.req.param(); const host = `https://outlet.arcteryx.com/${country}/en/`; const url = `${host}api/fredhopper/query`; @@ -25,7 +47,7 @@ export default async (ctx) => { }); const items = response.data.universes.universe[1]['items-section'].items.item.map((item, index, arr) => generateRssData(item, index, arr, country)); - ctx.set('data', { + return { title: `Arcteryx - Outlet(${country.toUpperCase()}) - ${gender.toUpperCase()}`, link: pageUrl, description: `Arcteryx - Outlet(${country.toUpperCase()}) - ${gender.toUpperCase()}`, @@ -36,5 +58,5 @@ export default async (ctx) => { item, }), })), - }); -}; + }; +} diff --git a/lib/routes/arcteryx/regear-new-arrivals.ts b/lib/routes/arcteryx/regear-new-arrivals.ts index d58f41f86..bd42078ae 100644 --- a/lib/routes/arcteryx/regear-new-arrivals.ts +++ b/lib/routes/arcteryx/regear-new-arrivals.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -10,7 +11,28 @@ const host = 'https://www.regear.arcteryx.com'; function getUSDPrice(number) { return (number / 100).toLocaleString('en-US', { style: 'currency', currency: 'USD' }); } -export default async (ctx) => { +export const route: Route = { + path: '/regear/new-arrivals', + categories: ['game'], + example: '/arcteryx/regear/new-arrivals', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['regear.arcteryx.com/shop/new-arrivals', 'regear.arcteryx.com/'], + }, + name: 'Regear New Arrivals', + maintainers: ['EthanWng97'], + handler, +}; + +async function handler() { const url = `${host}/shop/new-arrivals`; const response = await got({ method: 'get', @@ -39,7 +61,7 @@ export default async (ctx) => { return data; }); - ctx.set('data', { + return { title: 'Arcteryx - Regear - New Arrivals', link: url, description: 'Arcteryx - Regear - New Arrivals', @@ -48,5 +70,5 @@ export default async (ctx) => { link: item.link, description: item.description, })), - }); -}; + }; +} diff --git a/lib/routes/arknights/announce.ts b/lib/routes/arknights/announce.ts index 286675c2b..b7eff85a8 100644 --- a/lib/routes/arknights/announce.ts +++ b/lib/routes/arknights/announce.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/announce/:platform?/:group?', + categories: ['reading'], + example: '/arknights/announce', + parameters: { platform: '平台,默认为 Android', group: '分组,默认为 ALL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '游戏内公告', + maintainers: ['swwind'], + handler, +}; + +async function handler(ctx) { const { platform = 'Android', group = 'ALL' } = ctx.req.param(); let { @@ -48,9 +67,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `《明日方舟》${group === 'SYSTEM' ? '系统' : group === 'ACTIVITY' ? '活动' : '全部'}公告`, link: 'https://ak.hypergryph.com/', item: announceList, - }); -}; + }; +} diff --git a/lib/routes/arknights/japan.ts b/lib/routes/arknights/japan.ts index 967807b85..9e2b32412 100644 --- a/lib/routes/arknights/japan.ts +++ b/lib/routes/arknights/japan.ts @@ -1,7 +1,29 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/japan', + categories: ['reading'], + example: '/arknights/japan', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ak.arknights.jp/news', 'ak.arknights.jp/'], + }, + name: 'アークナイツ (日服新闻)', + maintainers: ['ofyark'], + handler, +}; + +async function handler() { const response = await got({ method: 'get', url: 'https://www.arknights.jp:10014/news?lang=ja&limit=9&page=1', @@ -15,11 +37,11 @@ export default async (ctx) => { link: `https://www.arknights.jp/news/${item.id}`, })); - ctx.set('data', { + return { title: 'アークナイツ', link: 'https://www.arknights.jp/news', description: 'アークナイツ ニュース', language: 'ja', item: newsList, - }); -}; + }; +} diff --git a/lib/routes/arknights/news.ts b/lib/routes/arknights/news.ts index 598a58cf8..d109c271a 100644 --- a/lib/routes/arknights/news.ts +++ b/lib/routes/arknights/news.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/news', + categories: ['reading'], + example: '/arknights/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ak-conf.hypergryph.com/*'], + }, + name: '游戏公告与新闻', + maintainers: ['Astrian'], + handler, +}; + +async function handler() { const response = await got({ method: 'get', url: 'https://ak.hypergryph.com/news.html', @@ -35,9 +57,9 @@ export default async (ctx) => { .get() ); - ctx.set('data', { + return { title: '《明日方舟》游戏公告与新闻', link: 'https://ak.hypergryph.com/news.html', item: newslist, - }); -}; + }; +} diff --git a/lib/routes/artstation/user.ts b/lib/routes/artstation/user.ts index 306ddfd4e..a60a898a2 100644 --- a/lib/routes/artstation/user.ts +++ b/lib/routes/artstation/user.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -8,7 +9,28 @@ import * as path from 'node:path'; import { art } from '@/utils/render'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/:handle', + categories: ['anime'], + example: '/artstation/wlop', + parameters: { handle: 'Artist handle, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.artstation.com/:handle'], + }, + name: 'Artist Profolio', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const handle = ctx.req.param('handle'); const headers = { @@ -90,7 +112,7 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${userData.full_name} - ArtStation`, description: userData.headline, link: userData.permalink, @@ -98,5 +120,5 @@ export default async (ctx) => { icon: userData.large_avatar_url, image: userData.default_cover_url, item: items, - }); -}; + }; +} diff --git a/lib/routes/asiantolick/index.ts b/lib/routes/asiantolick/index.ts index 18646552c..1543fe982 100644 --- a/lib/routes/asiantolick/index.ts +++ b/lib/routes/asiantolick/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -8,7 +9,18 @@ import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/:category{.+}?', + radar: { + source: ['asiantolick.com/'], + target: '', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category'); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 24; @@ -102,7 +114,7 @@ export default async (ctx) => { const title = $('title').text().split(/-/)[0].trim(); const icon = $('link[rel="icon"]').first().prop('href'); - ctx.set('data', { + return { item: items, title: title === 'Asian To Lick' ? title : `Asian To Lick - ${title}`, link: currentUrl, @@ -113,5 +125,5 @@ export default async (ctx) => { logo: icon, subtitle: title, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/asus/bios.ts b/lib/routes/asus/bios.ts index 8a7aef44c..c2b7bac46 100644 --- a/lib/routes/asus/bios.ts +++ b/lib/routes/asus/bios.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -16,7 +17,28 @@ const getProductID = async (model) => { }; }; -export default async (ctx) => { +export const route: Route = { + path: '/bios/:model', + categories: ['university'], + example: '/asus/bios/RT-AX88U', + parameters: { model: 'Model, can be found in product page' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['asus.com.cn/'], + }, + name: 'BIOS', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const model = ctx.req.param('model'); const { productID, url } = await getProductID(model); const biosAPI = `https://www.asus.com.cn/support/api/product.asmx/GetPDBIOS?website=cn&model=${model}&pdid=${productID}&sitelang=cn`; @@ -34,9 +56,9 @@ export default async (ctx) => { link: url, })); - ctx.set('data', { + return { title: `${model} BIOS`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/asus/gpu-tweak.ts b/lib/routes/asus/gpu-tweak.ts index cb3d3d261..06fde000b 100644 --- a/lib/routes/asus/gpu-tweak.ts +++ b/lib/routes/asus/gpu-tweak.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; const pageUrl = 'https://www.asus.com/campaign/GPU-Tweak-III/tw/index.php'; -export default async (ctx) => { +export const route: Route = { + path: '/gpu-tweak', + categories: ['university'], + example: '/asus/gpu-tweak', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['asus.com/campaign/GPU-Tweak-III/*', 'asus.com/'], + }, + name: 'GPU Tweak', + maintainers: ['TonyRL'], + handler, +}; + +async function handler() { const response = await got(pageUrl); const $ = load(response.data); @@ -20,12 +42,12 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('head title').text(), description: $('meta[name=description]').attr('content'), image: new URL($('head link[rel="shortcut icon"]').attr('href'), pageUrl).href, link: pageUrl, item: items, language: $('html').attr('lang'), - }); -}; + }; +} diff --git a/lib/routes/atcoder/contest.ts b/lib/routes/atcoder/contest.ts index 03247a4c9..39e36e6c3 100644 --- a/lib/routes/atcoder/contest.ts +++ b/lib/routes/atcoder/contest.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/contest/:language?/:rated?/:category?/:keyword?', + categories: ['design'], + example: '/atcoder/contest', + parameters: { language: 'Language, `jp` as Japanese or `en` as English, English by default', rated: 'Rated Range, see below, all by default', category: 'Category, see below, all by default', keyword: 'Keyword' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Contests Archive', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const status = ['action', 'upcoming', 'recent']; const language = ctx.req.param('language') ?? 'en'; @@ -56,10 +75,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: String(isStatus ? `${$(`#contest-table-${rated} h3`).text()} - AtCoder` : $('title').text()), link: currentUrl, item: items, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/atcoder/post.ts b/lib/routes/atcoder/post.ts index c735a53ff..e9a124083 100644 --- a/lib/routes/atcoder/post.ts +++ b/lib/routes/atcoder/post.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/post/:language?/:keyword?', + categories: ['design'], + example: '/atcoder/post', + parameters: { language: 'Language, `jp` as Japanese or `en` as English, English by default', keyword: 'Keyword' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Posts', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const language = ctx.req.param('language') ?? 'en'; const keyword = ctx.req.param('keyword') ?? ''; @@ -30,9 +49,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `${keyword ? `[${keyword}] - ` : ''}${$('title').text()}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/auto-stats/index.ts b/lib/routes/auto-stats/index.ts index d94907eac..22f8dd7ed 100644 --- a/lib/routes/auto-stats/index.ts +++ b/lib/routes/auto-stats/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -5,7 +6,25 @@ import iconv from 'iconv-lite'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + categories: ['other'], + example: '/auto-stats', + parameters: { category: '分类,见下表,默认为信息快递' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '分类', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const { category = 'xxkd' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; @@ -53,7 +72,7 @@ export default async (ctx) => { const subtitle = $('title').text().split(/——/).pop(); const image = new URL('images/logo.jpg', rootUrl).href; - ctx.set('data', { + return { item: items, title: $('title').text(), link: currentUrl, @@ -62,5 +81,5 @@ export default async (ctx) => { image, subtitle, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/baai/comments.ts b/lib/routes/baai/comments.ts index a364feb77..6ddbe6b4d 100644 --- a/lib/routes/baai/comments.ts +++ b/lib/routes/baai/comments.ts @@ -1,10 +1,32 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; import { baseUrl, apiHost } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/hub/comments', + categories: ['design'], + example: '/baai/hub/comments', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['hub.baai.ac.cn/comments', 'hub.baai.ac.cn/'], + }, + name: '智源社区 - 评论', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const responses = await got.all( Array.from( { @@ -26,9 +48,9 @@ export default async (ctx) => { guid: `baai:hub:comments:${item.id}`, })); - ctx.set('data', { + return { title: '评论 - 智源社区', link: `${baseUrl}/comments`, item: items, - }); -}; + }; +} diff --git a/lib/routes/baai/events.ts b/lib/routes/baai/events.ts index 8d09b7c99..c1638b2bb 100644 --- a/lib/routes/baai/events.ts +++ b/lib/routes/baai/events.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { baseUrl, apiHost, parseEventDetail, parseItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/hub/events', + categories: ['design'], + example: '/baai/hub/events', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['hub.baai.ac.cn/events', 'hub.baai.ac.cn/'], + }, + name: '智源社区 - 活动', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const responses = await got.all( Array.from( { @@ -25,9 +47,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '活动 - 智源社区', link: `${baseUrl}/events`, item: items, - }); -}; + }; +} diff --git a/lib/routes/baai/hub.ts b/lib/routes/baai/hub.ts index 2f5789edc..6906534ec 100644 --- a/lib/routes/baai/hub.ts +++ b/lib/routes/baai/hub.ts @@ -1,10 +1,18 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { baseUrl, apiHost, parseEventDetail, parseItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: ['/hub/:tagId/:sort?/:range?', '/hub/:tagId/:sort?', '/hub/:sort?'], + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { tagId, sort = 'new', range } = ctx.req.param(); let title, description, brief, iconUrl; @@ -50,11 +58,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${title ? `${title} - ` : ''}${description ? `${description} - ` : ''}智源社区`, description: brief, link: `${baseUrl}/?${tagId ? `tag_id=${tagId}&` : ''}sort=${sort}${range ? `&time_range=${range}` : ''}`, image: iconUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/backlinko/blog.ts b/lib/routes/backlinko/blog.ts index ef5edfbde..a9823b734 100644 --- a/lib/routes/backlinko/blog.ts +++ b/lib/routes/backlinko/blog.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/blog', + categories: ['programming'], + example: '/backlinko/blog', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['backlinko.com/blog', 'backlinko.com/'], + }, + name: 'Blog', + maintainers: ['TonyRL'], + handler, +}; + +async function handler() { const baseUrl = 'https://backlinko.com'; const { data: response, url: link } = await got(`${baseUrl}/blog`); @@ -35,11 +57,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: pageProps.page.seo.title, description: pageProps.page.seo.metaDesc, link, language: 'en', item: items, - }); -}; + }; +} diff --git a/lib/routes/bad/index.ts b/lib/routes/bad/index.ts index 9455156ce..9df29dcd7 100644 --- a/lib/routes/bad/index.ts +++ b/lib/routes/bad/index.ts @@ -1,10 +1,18 @@ +import { Route } from '@/types'; import { getSubPath } from '@/utils/common-utils'; import got from '@/utils/got'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const rootUrl = 'https://bad.news'; const currentUrl = `${rootUrl}${getSubPath(ctx) === '/' ? '' : getSubPath(ctx)}`; @@ -51,9 +59,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `Bad.news - ${$('.active').text()}${$('.selected').text()}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/baidu/gushitong/index.ts b/lib/routes/baidu/gushitong/index.ts index 00a3acd9b..6f2329ffa 100644 --- a/lib/routes/baidu/gushitong/index.ts +++ b/lib/routes/baidu/gushitong/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -10,7 +11,28 @@ const STATUS_MAP = { down: '下跌', }; -export default async (ctx) => { +export const route: Route = { + path: '/gushitong/index', + categories: ['other'], + example: '/baidu/gushitong/index', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gushitong.baidu.com/'], + }, + name: '首页指数', + maintainers: ['CaoMeiYouRen'], + handler, +}; + +async function handler() { const response = await got('https://finance.pae.baidu.com/api/indexbanner?market=ab&finClientType=pc'); const item = response.data.Result.map((e) => ({ title: e.name, @@ -21,11 +43,11 @@ export default async (ctx) => { }), link: `https://gushitong.baidu.com/index/${e.market}-${e.code}`, })); - ctx.set('data', { + return { title: '百度股市通', description: '百度股市通,汇聚全球金融市场的股票、基金、外汇、期货等实时行情,7*24小时覆盖专业财经资讯,提供客观、准确、及时、全面的沪深港美上市公司股价、财务、股东、分红等信息,让用户在复杂的金融市场,更简单的获取投资信息。', link: 'https://gushitong.baidu.com/', item, - }); -}; + }; +} diff --git a/lib/routes/baidu/search.ts b/lib/routes/baidu/search.ts index 588f9a5fa..ab54056f3 100644 --- a/lib/routes/baidu/search.ts +++ b/lib/routes/baidu/search.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -9,7 +10,25 @@ import * as path from 'node:path'; const renderDescription = (description, images) => art(path.join(__dirname, './templates/description.art'), { description, images }); import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/search/:keyword', + categories: ['other'], + example: '/baidu/search/rss', + parameters: { keyword: '搜索关键词' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '搜索', + maintainers: ['CaoMeiYouRen'], + handler, +}; + +async function handler(ctx) { const keyword = ctx.req.param('keyword'); const url = `https://www.baidu.com/s?wd=${encodeURIComponent(keyword)}`; const key = `baidu-search:${url}`; @@ -49,10 +68,10 @@ export default async (ctx) => { false ); - ctx.set('data', { + return { title: `${keyword} - 百度搜索`, description: `${keyword} - 百度搜索`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/baidu/tieba/forum.ts b/lib/routes/baidu/tieba/forum.ts index 5baa32120..547cc722e 100644 --- a/lib/routes/baidu/tieba/forum.ts +++ b/lib/routes/baidu/tieba/forum.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -8,7 +9,25 @@ import timezone from '@/utils/timezone'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: ['/tieba/forum/good/:kw/:cid?/:sortBy?', '/tieba/forum/:kw/:sortBy?'], + categories: ['blog'], + example: '/baidu/tieba/forum/good/女图', + parameters: { kw: '吧名', cid: '精品分类,默认为 `0`(全部分类),如果不传 `cid` 则获取全部分类', sortBy: '排序方式:`created`, `replied`。默认为 `created`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '精品帖子', + maintainers: ['u3u'], + handler, +}; + +async function handler(ctx) { // sortBy: created, replied const { kw, cid = '0', sortBy = 'created' } = ctx.req.param(); @@ -58,10 +77,10 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `${kw}吧`, description: load(data)('meta[name="description"]').attr('content'), link: `https://tieba.baidu.com/f?kw=${encodeURIComponent(kw)}`, item: list, - }); -}; + }; +} diff --git a/lib/routes/baidu/tieba/post.ts b/lib/routes/baidu/tieba/post.ts index a9b2ede0c..6726324a5 100644 --- a/lib/routes/baidu/tieba/post.ts +++ b/lib/routes/baidu/tieba/post.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -31,7 +32,28 @@ async function getPost(id, lz = 0, pn = 7e6) { return data; } -export default async (ctx) => { +export const route: Route = { + path: ['/tieba/post/:id', '/tieba/post/lz/:id'], + categories: ['blog'], + example: '/baidu/tieba/post/686961453', + parameters: { id: '帖子 ID' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['tieba.baidu.com/p/:id'], + }, + name: '帖子动态', + maintainers: ['u3u'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const lz = ctx.req.path.includes('lz') ? 1 : 0; const html = await getPost(id, lz); @@ -40,7 +62,7 @@ export default async (ctx) => { // .substr(3); const list = $('.p_postlist > [data-field]:not(:has(.ad_bottom_view))'); - ctx.set('data', { + return { title: lz ? `【只看楼主】${title}` : title, link: `https://tieba.baidu.com/p/${id}?see_lz=${lz}`, description: `${title}的最新回复`, @@ -79,5 +101,5 @@ export default async (ctx) => { }; }) .get(), - }); -}; + }; +} diff --git a/lib/routes/baidu/tieba/search.ts b/lib/routes/baidu/tieba/search.ts index 93367486f..2ba06cbc4 100644 --- a/lib/routes/baidu/tieba/search.ts +++ b/lib/routes/baidu/tieba/search.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -9,7 +10,25 @@ import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/tieba/search/:qw/:routeParams?', + categories: ['blog'], + example: '/baidu/tieba/search/neuro', + parameters: { qw: '搜索关键词', routeParams: '额外参数;请参阅以下说明和表格' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '贴吧搜索', + maintainers: ['JimenezLi'], + handler, +}; + +async function handler(ctx) { const qw = ctx.req.param('qw'); const query = new URLSearchParams(ctx.req.param('routeParams')); query.set('ie', 'utf-8'); @@ -28,7 +47,7 @@ export default async (ctx) => { const $ = load(data); const resultList = $('div.s_post'); - ctx.set('data', { + return { title: `${qw} - ${query.get('kw') || '百度贴'}吧搜索`, link, item: resultList.toArray().map((element) => { @@ -62,5 +81,5 @@ export default async (ctx) => { link, }; }), - }); -}; + }; +} diff --git a/lib/routes/baidu/tieba/user.ts b/lib/routes/baidu/tieba/user.ts index 3df50f256..2fcbe1f44 100644 --- a/lib/routes/baidu/tieba/user.ts +++ b/lib/routes/baidu/tieba/user.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -export default async (ctx) => { +export const route: Route = { + path: '/tieba/user/:uid', + categories: ['blog'], + example: '/baidu/tieba/user/斗鱼游戏君', + parameters: { uid: '用户 ID' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '用户帖子', + maintainers: ['igxlin', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const uid = ctx.req.param('uid'); const response = await got(`https://tieba.baidu.com/home/main?un=${uid}`); @@ -14,7 +33,7 @@ export default async (ctx) => { const list = $('div.n_right.clearfix'); let imgurl; - ctx.set('data', { + return { title: `${name} 的贴吧`, link: `https://tieba.baidu.com/home/main?un=${uid}`, item: @@ -29,5 +48,5 @@ export default async (ctx) => { link: item.find('div.thread_name a').attr('href'), }; }), - }); -}; + }; +} diff --git a/lib/routes/baidu/top.ts b/lib/routes/baidu/top.ts index f9fed2fc2..3b45c78c2 100644 --- a/lib/routes/baidu/top.ts +++ b/lib/routes/baidu/top.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -6,7 +7,25 @@ import { load } from 'cheerio'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/top/:board?', + categories: ['other'], + example: '/baidu/top', + parameters: { board: '榜单,默认为 `realtime`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '热搜榜单', + maintainers: ['xyqfer'], + handler, +}; + +async function handler(ctx) { const { board = 'realtime' } = ctx.req.param(); const link = `https://top.baidu.com/board?tab=${board}`; const { data: response } = await got(link); @@ -28,10 +47,10 @@ export default async (ctx) => { link: item.rawUrl, })); - ctx.set('data', { + return { title: `${data.curBoardName} - 百度热搜`, description: $('meta[name="description"]').attr('content'), link, item: items, - }); -}; + }; +} diff --git a/lib/routes/bandcamp/live.ts b/lib/routes/bandcamp/live.ts index 1179f9e60..b3a5ed063 100644 --- a/lib/routes/bandcamp/live.ts +++ b/lib/routes/bandcamp/live.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/live', + categories: ['picture'], + example: '/bandcamp/live', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bandcamp.com/live_schedule'], + }, + name: 'Upcoming Live Streams', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://bandcamp.com'; const currentUrl = `${rootUrl}/live_schedule`; const response = await got({ @@ -33,9 +55,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/bandcamp/tag.ts b/lib/routes/bandcamp/tag.ts index efbd73d36..caeb1a980 100644 --- a/lib/routes/bandcamp/tag.ts +++ b/lib/routes/bandcamp/tag.ts @@ -1,8 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/tag/:tag?', + categories: ['picture'], + example: '/bandcamp/tag/united-kingdom', + parameters: { tag: 'Tag, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bandcamp.com/tag/:tag'], + target: '/tag/:tag', + }, + name: 'Tag', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const tag = ctx.req.param('tag'); const rootUrl = 'https://bandcamp.com'; @@ -39,9 +62,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/bandcamp/weekly.ts b/lib/routes/bandcamp/weekly.ts index b154c39c4..a71749772 100644 --- a/lib/routes/bandcamp/weekly.ts +++ b/lib/routes/bandcamp/weekly.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -6,7 +7,28 @@ import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/weekly', + categories: ['picture'], + example: '/bandcamp/weekly', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bandcamp.com/'], + }, + name: 'Weekly', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://bandcamp.com'; const apiUrl = `${rootUrl}/api/bcweekly/3/list`; const response = await got({ @@ -24,9 +46,9 @@ export default async (ctx) => { }), })); - ctx.set('data', { + return { title: 'Bandcamp Weekly', link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/bangumi/moe/index.ts b/lib/routes/bangumi/moe/index.ts index 33b5e931d..d3161008f 100644 --- a/lib/routes/bangumi/moe/index.ts +++ b/lib/routes/bangumi/moe/index.ts @@ -1,9 +1,21 @@ +import { Route } from '@/types'; import { getSubPath } from '@/utils/common-utils'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/moe/*', + radar: { + source: ['bangumi.moe/'], + target: '/moe', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const isLatest = getSubPath(ctx) === '/moe'; const rootUrl = 'https://bangumi.moe'; @@ -85,10 +97,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '萌番组 Bangumi Moe', link: isLatest || items.length === 0 ? rootUrl : `${rootUrl}/search/${tag_id.join('+')}`, item: items, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/bangumi/online/online.ts b/lib/routes/bangumi/online/online.ts index 12a98dbcb..3aad2561d 100644 --- a/lib/routes/bangumi/online/online.ts +++ b/lib/routes/bangumi/online/online.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -6,7 +7,28 @@ import { art } from '@/utils/render'; import { parseDate } from '@/utils/parse-date'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/online', + categories: ['program-update'], + example: '/bangumi/online', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bangumi.online/'], + }, + name: 'Unknown', + maintainers: ['devinmugen'], + handler, +}; + +async function handler() { const url = 'https://api.bangumi.online/serve/home'; const response = await got.post(url); @@ -23,9 +45,9 @@ export default async (ctx) => { pubDate: parseDate(item.create_time), })); - ctx.set('data', { + return { title: 'アニメ新番組', link: 'https://bangumi.online', item: items, - }); -}; + }; +} diff --git a/lib/routes/bangumi/tv/calendar/today.ts b/lib/routes/bangumi/tv/calendar/today.ts index fe6863b52..f832c492a 100644 --- a/lib/routes/bangumi/tv/calendar/today.ts +++ b/lib/routes/bangumi/tv/calendar/today.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -6,7 +7,28 @@ import getData from './_base'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/tv/calendar/today', + categories: ['program-update'], + example: '/bangumi/tv/calendar/today', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bgm.tv/calendar'], + }, + name: '放送列表', + maintainers: ['magic-akari'], + handler, +}; + +async function handler() { const [list, data] = await getData(cache.tryGet); const siteMeta = data.siteMeta; @@ -26,7 +48,7 @@ export default async (ctx) => { bgm.image = images[bgm.bgmId]; } - ctx.set('data', { + return { title: 'bangumi 每日放送', link: 'https://bgm.tv/calendar', item: todayBgm.map((bgm) => { @@ -63,5 +85,5 @@ export default async (ctx) => { content: { html }, }; }), - }); -}; + }; +} diff --git a/lib/routes/bangumi/tv/group/reply.ts b/lib/routes/bangumi/tv/group/reply.ts index bf1d8a41a..49055e117 100644 --- a/lib/routes/bangumi/tv/group/reply.ts +++ b/lib/routes/bangumi/tv/group/reply.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -export default async (ctx) => { +export const route: Route = { + path: '/tv/topic/:id', + categories: ['program-update'], + example: '/bangumi/tv/topic/367032', + parameters: { id: '话题 id, 在话题页面地址栏查看' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bgm.tv/group/topic/:id'], + }, + name: '小组话题的新回复', + maintainers: ['ylc395'], + handler, +}; + +async function handler(ctx) { // bangumi.tv未提供获取小组话题的API,因此仍需要通过抓取网页来获取 const topicID = ctx.req.param('id'); const link = `https://bgm.tv/group/topic/${topicID}`; @@ -42,7 +64,7 @@ export default async (ctx) => { link, }; - ctx.set('data', { + return { title: `${title}的最新回复`, link, item: [ @@ -55,5 +77,5 @@ export default async (ctx) => { })), postTopic, ], - }); -}; + }; +} diff --git a/lib/routes/bangumi/tv/group/topic.ts b/lib/routes/bangumi/tv/group/topic.ts index 65ff39817..64dc410fa 100644 --- a/lib/routes/bangumi/tv/group/topic.ts +++ b/lib/routes/bangumi/tv/group/topic.ts @@ -1,10 +1,32 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; const base_url = 'https://bgm.tv'; -export default async (ctx) => { +export const route: Route = { + path: '/tv/group/:id', + categories: ['program-update'], + example: '/bangumi/tv/group/boring', + parameters: { id: '小组 id, 在小组页面地址栏查看' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bgm.tv/group/:id'], + }, + name: '小组话题', + maintainers: ['SettingDust'], + handler, +}; + +async function handler(ctx) { const groupID = ctx.req.param('id'); const link = `${base_url}/group/${groupID}/forum`; const { data: html } = await got(link); @@ -32,9 +54,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title, link, item: items, - }); -}; + }; +} diff --git a/lib/routes/bangumi/tv/other/followrank.ts b/lib/routes/bangumi/tv/other/followrank.ts index a7268fca0..21bc0082b 100644 --- a/lib/routes/bangumi/tv/other/followrank.ts +++ b/lib/routes/bangumi/tv/other/followrank.ts @@ -1,7 +1,29 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/tv/followrank', + categories: ['program-update'], + example: '/bangumi/tv/followrank', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bgm.tv/anime'], + }, + name: '成员关注动画榜', + maintainers: ['honue'], + handler, +}; + +async function handler() { const url = 'https://bgm.tv/anime'; const response = await got({ url, @@ -33,10 +55,10 @@ export default async (ctx) => { })), ]; - ctx.set('data', { + return { title: 'Bangumi 成员关注动画榜', link: url, item: items, description: `Bangumi 首页-成员关注动画榜`, - }); -}; + }; +} diff --git a/lib/routes/bangumi/tv/person/index.ts b/lib/routes/bangumi/tv/person/index.ts index b81da07d6..dd8dfe899 100644 --- a/lib/routes/bangumi/tv/person/index.ts +++ b/lib/routes/bangumi/tv/person/index.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/tv/person/:id', + categories: ['program-update'], + example: '/bangumi/tv/person/32943', + parameters: { id: '人物 id, 在人物页面的地址栏查看' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bgm.tv/person/:id'], + }, + name: '现实人物的新作品', + maintainers: ['ylc395'], + handler, +}; + +async function handler(ctx) { // bangumi.tv未提供获取“人物信息”的API,因此仍需要通过抓取网页来获取 const personID = ctx.req.param('id'); const link = `https://bgm.tv/person/${personID}/works?sort=date`; @@ -22,7 +44,7 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `${personName}参与的作品`, link, item: works.map((c) => { @@ -34,5 +56,5 @@ export default async (ctx) => { pubDate: match ? parseDate(match[1], ['YYYY-MM-DD', 'YYYY-M-D']) : null, }; }), - }); -}; + }; +} diff --git a/lib/routes/bangumi/tv/subject/index.ts b/lib/routes/bangumi/tv/subject/index.ts index 218567c1e..10f828598 100644 --- a/lib/routes/bangumi/tv/subject/index.ts +++ b/lib/routes/bangumi/tv/subject/index.ts @@ -1,9 +1,32 @@ +import { Route } from '@/types'; import getComments from './comments'; import getFromAPI from './offcial-subject-api'; import getEps from './ep'; import { queryToBoolean } from '@/utils/readable-social'; -export default async (ctx) => { +export const route: Route = { + path: '/tv/subject/:id/:type?/:showOriginalName?', + categories: ['program-update'], + example: '/bangumi/tv/subject/328609/ep/true', + parameters: { id: '条目 id, 在条目页面的地址栏查看', type: '条目类型,可选值为 `ep`, `comments`, `blogs`, `topics`,默认为 `ep`', showOriginalName: '显示番剧标题原名,可选值 0/1/false/true,默认为 false' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bgm.tv/subject/:id'], + target: '/tv/subject/:id', + }, + name: '条目的通用路由格式', + maintainers: ['JimenezLi'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const type = ctx.req.param('type') || 'ep'; const showOriginalName = queryToBoolean(ctx.req.param('showOriginalName')); @@ -25,4 +48,4 @@ export default async (ctx) => { throw new Error(`暂不支持对${type}的订阅`); } ctx.set('data', response); -}; +} diff --git a/lib/routes/bangumi/tv/user/blog.ts b/lib/routes/bangumi/tv/user/blog.ts index 412ad9e8e..e116ac11f 100644 --- a/lib/routes/bangumi/tv/user/blog.ts +++ b/lib/routes/bangumi/tv/user/blog.ts @@ -1,10 +1,32 @@ +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'; -export default async (ctx) => { +export const route: Route = { + path: '/tv/user/blog/:id', + categories: ['program-update'], + example: '/bangumi/tv/user/blog/sai', + parameters: { id: '用户 id, 在用户页面地址栏查看' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bgm.tv/user/:id'], + }, + name: '用户日志', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const currentUrl = `https://bgm.tv/user/${ctx.req.param('id')}/blog`; const response = await got({ method: 'get', @@ -36,9 +58,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/bangumi/tv/user/wish.ts b/lib/routes/bangumi/tv/user/wish.ts index 06aaaa45a..85d7fe859 100644 --- a/lib/routes/bangumi/tv/user/wish.ts +++ b/lib/routes/bangumi/tv/user/wish.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/tv/user/wish/:id', + categories: ['program-update'], + example: '/bangumi/tv/user/wish/sai', + parameters: { id: '用户 id, 在用户页面地址栏查看' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bgm.tv/anime/list/:id/wish'], + }, + name: '用户想看', + maintainers: ['honue'], + handler, +}; + +async function handler(ctx) { const userid = ctx.req.param('id'); const url = `https://bgm.tv/anime/list/${userid}/wish`; const response = await got({ @@ -29,10 +51,10 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `${username}想看的动画`, link: url, item: items, description: `${username}想看的动画列表`, - }); -}; + }; +} diff --git a/lib/routes/baozimh/index.ts b/lib/routes/baozimh/index.ts index cbaffcbc6..a1b40f330 100644 --- a/lib/routes/baozimh/index.ts +++ b/lib/routes/baozimh/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -9,7 +10,28 @@ import * as path from 'node:path'; const rootUrl = 'https://www.baozimh.com'; -export default async (ctx) => { +export const route: Route = { + path: '/comic/:name', + categories: ['program-update'], + example: '/baozimh/comic/guowangpaiming-shiricaofu', + parameters: { name: '漫画名称,在漫画链接可以得到(`comic/` 后的那段)' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.baozimh.com/comic/:name'], + }, + name: '订阅漫画', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const name = ctx.req.param('name'); const url = `${rootUrl}/comic/${name}`; @@ -45,10 +67,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `包子漫画-${comicTitle}`, description: $('.comics-detail__desc').text(), link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/barronschina/index.ts b/lib/routes/barronschina/index.ts index 81a6f908c..a0d6b0cca 100644 --- a/lib/routes/barronschina/index.ts +++ b/lib/routes/barronschina/index.ts @@ -1,10 +1,33 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:id?', + categories: ['other'], + example: '/barronschina', + parameters: { id: '栏目 id,默认为快讯' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['barronschina.com.cn/'], + target: '/:category?', + }, + name: '栏目', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? ''; const rootUrl = 'http://www.barronschina.com.cn'; @@ -63,9 +86,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('title').text().split(',')[0], link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/bast/index.ts b/lib/routes/bast/index.ts index 4645460cd..0e92e74be 100644 --- a/lib/routes/bast/index.ts +++ b/lib/routes/bast/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getSubPath } from '@/utils/common-utils'; import cache from '@/utils/cache'; import got from '@/utils/got'; @@ -5,7 +6,14 @@ import { load } from 'cheerio'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const colPath = getSubPath(ctx).replace(/^\//, '') || '32942'; const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 50; @@ -68,9 +76,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/bbc/index.ts b/lib/routes/bbc/index.ts index b7f6ae8ea..fbf3930b2 100644 --- a/lib/routes/bbc/index.ts +++ b/lib/routes/bbc/index.ts @@ -1,10 +1,18 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import parser from '@/utils/rss-parser'; import { load } from 'cheerio'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/:site?/:channel?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { let feed, title, link; // 为了向下兼容,这里 site 对应的是中文网文档中的 lang,英文网文档中的 channel @@ -69,11 +77,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title, link, image: 'https://www.bbc.com/favicon.ico', description: title, item: items, - }); -}; + }; +} diff --git a/lib/routes/bbcnewslabs/news.ts b/lib/routes/bbcnewslabs/news.ts index 9eda00460..a8913a84d 100644 --- a/lib/routes/bbcnewslabs/news.ts +++ b/lib/routes/bbcnewslabs/news.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/news', + categories: ['design'], + example: '/bbcnewslabs/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bbcnewslabs.co.uk/'], + }, + name: 'News', + maintainers: ['elxy'], + handler, +}; + +async function handler() { const rootUrl = 'https://bbcnewslabs.co.uk'; const response = await got({ method: 'get', @@ -23,9 +45,9 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title: 'News - BBC News Labs', link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/bdys/index.ts b/lib/routes/bdys/index.ts index 43fb13f44..38773db25 100644 --- a/lib/routes/bdys/index.ts +++ b/lib/routes/bdys/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -14,7 +15,31 @@ import { config } from '@/config'; // Visit https://www.bdys.me for the list of domains const allowDomains = new Set(['52bdys.com', 'bde4.icu', 'bdys01.com']); -export default async (ctx) => { +export const route: Route = { + path: '/:caty?/:type?/:area?/:year?/:order?', + categories: ['picture'], + example: '/bdys', + parameters: { + caty: '影视类型,见下表,默认为 `all` 即不限', + type: '资源分类,见下表,默认为 `all` 即不限', + area: '制片地区,见下表,默认为 `all` 即不限', + year: '上映时间,此处填写年份不小于2000,默认为 `all` 即不限', + order: '影视排序,见下表,默认为更新时间', + }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '首页', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const caty = ctx.req.param('caty') || 'all'; const type = ctx.req.param('type') || 'all'; const area = ctx.req.param('area') || 'all'; @@ -94,9 +119,9 @@ export default async (ctx) => { items.push(data); } - ctx.set('data', { + return { title: '哔嘀影视', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/behance/user.ts b/lib/routes/behance/user.ts index 2e367b46c..d2afc7359 100644 --- a/lib/routes/behance/user.ts +++ b/lib/routes/behance/user.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:user/:type?', + categories: ['live'], + example: '/behance/mishapetrick', + parameters: { user: 'username', type: 'type, `projects` or `appreciated`, `projects` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'User Works', + maintainers: ['MisteryMonster'], + handler, +}; + +async function handler(ctx) { const user = ctx.req.param('user') ?? ''; const type = ctx.req.param('type') ?? 'projects'; @@ -50,7 +69,7 @@ export default async (ctx) => { return description; }) ); - ctx.set('data', { + return { title: `${data.profile.owner.first_name} ${data.profile.owner.last_name}'s ${type}`, link: data.profile.owner.url, item: list.map((item, index) => { @@ -64,5 +83,5 @@ export default async (ctx) => { pubDate: parseDate(item.published_on * 1000), }; }), - }); -}; + }; +} diff --git a/lib/routes/bellroy/new-releases.ts b/lib/routes/bellroy/new-releases.ts index 3b8e372ae..2ebb733c5 100644 --- a/lib/routes/bellroy/new-releases.ts +++ b/lib/routes/bellroy/new-releases.ts @@ -1,6 +1,28 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/new-releases', + categories: ['game'], + example: '/bellroy/new-releases', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bellroy.com/collection/new-releases', 'bellroy.com/'], + }, + name: 'New Releases', + maintainers: ['EthanWng97'], + handler, +}; + +async function handler() { const host = 'https://bellroy.com'; const url = 'https://production.products.boobook-services.com/products'; const response = await got({ @@ -14,7 +36,7 @@ export default async (ctx) => { }); const data = response.data.products; - ctx.set('data', { + return { title: 'Bellroy - New Releases', link: 'https://bellroy.com/collection/new-releases', description: 'Bellroy - New Releases', @@ -22,5 +44,5 @@ export default async (ctx) => { title: item.attributes.name + ' - ' + item.attributes.dimensions.color, link: host + item.attributes.canonical_uri, })), - }); -}; + }; +} diff --git a/lib/routes/bendibao/news.ts b/lib/routes/bendibao/news.ts index 11608fd66..06c6cc12b 100644 --- a/lib/routes/bendibao/news.ts +++ b/lib/routes/bendibao/news.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -5,7 +6,28 @@ import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; import { isValidHost } from '@/utils/valid-host'; -export default async (ctx) => { +export const route: Route = { + path: '/news/:city', + categories: ['traditional-media'], + example: '/bendibao/news/bj', + parameters: { city: '城市缩写,可在该城市页面的 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bendibao.com/'], + }, + name: '焦点资讯', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const city = ctx.req.param('city'); if (!isValidHost(city)) { throw new Error('Invalid city'); @@ -102,9 +124,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title, link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/bgmlist/onair.ts b/lib/routes/bgmlist/onair.ts index 9c64bfe36..36b03be9d 100644 --- a/lib/routes/bgmlist/onair.ts +++ b/lib/routes/bgmlist/onair.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -6,12 +7,30 @@ import * as path from 'node:path'; import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; -export default async (ctx) => { +export const route: Route = { + path: '/onair/:lang?', + categories: ['program-update'], + example: '/bgmlist/onair/zh-Hans', + parameters: { lang: '语言' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '开播提醒', + maintainers: ['x2cf'], + handler, +}; + +async function handler(ctx) { const lang = ctx.req.param('lang'); const { data: sites } = await got('https://bgmlist.com/api/v1/bangumi/site'); const { data } = await got('https://bgmlist.com/api/v1/bangumi/onair'); - ctx.set('data', { + return { title: '番组放送 开播提醒', link: 'https://bgmlist.com/', item: data.items.map((item) => { @@ -31,5 +50,5 @@ export default async (ctx) => { guid: item.id, }; }), - }); -}; + }; +} diff --git a/lib/routes/bigquant/collections.ts b/lib/routes/bigquant/collections.ts index 304f50c85..4d12cfb96 100644 --- a/lib/routes/bigquant/collections.ts +++ b/lib/routes/bigquant/collections.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import MarkdownIt from 'markdown-it'; @@ -5,7 +6,28 @@ const md = MarkdownIt({ html: true, }); -export default async (ctx) => { +export const route: Route = { + path: '/collections', + categories: ['other'], + example: '/bigquant/collections', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bigquant.com/'], + }, + name: '专题报告', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://bigquant.com'; const currentUrl = `${rootUrl}/wiki/api/documents.list`; @@ -28,9 +50,9 @@ export default async (ctx) => { pubDate: parseDate(item.publishedAt), })); - ctx.set('data', { + return { title: '专题报告 - AI量化知识库 - BigQuant', link: `${rootUrl}/wiki/collections/c6874e5d-7f45-4e90-8cd9-5e43df3b44ef`, item: items, - }); -}; + }; +} diff --git a/lib/routes/bilibili/app.ts b/lib/routes/bilibili/app.ts index 35b388ddd..544eadc85 100644 --- a/lib/routes/bilibili/app.ts +++ b/lib/routes/bilibili/app.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; const config = { @@ -8,7 +9,25 @@ const config = { android_tv_yst: 'TV 版', }; -export default async (ctx) => { +export const route: Route = { + path: '/app/:id?', + categories: ['university'], + example: '/bilibili/app/android', + parameters: { id: '客户端 id,见下表,默认为安卓版' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '更新情报', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') || 'android'; const rootUrl = 'https://app.bilibili.com'; @@ -25,9 +44,9 @@ export default async (ctx) => { description: `

${item.description}
`, })); - ctx.set('data', { + return { title: '快讯 - Odaily星球日报', link: `${rootUrl}/newsflash`, item: items, - }); -}; + }; +} diff --git a/lib/routes/odaily/post.ts b/lib/routes/odaily/post.ts index b42858e28..a2cd62e60 100644 --- a/lib/routes/odaily/post.ts +++ b/lib/routes/odaily/post.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -16,7 +17,28 @@ const titles = { 296: '活动', }; -export default async (ctx) => { +export const route: Route = { + path: '/:id?', + categories: ['traditional-media'], + example: '/odaily', + parameters: { id: 'id,见下表,默认为最新' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['0daily.com/'], + }, + name: '文章', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? '280'; const currentUrl = `${rootUrl}/api/pp/api/app-front/feed-stream?feed_id=${id}&b_id=&per_page=${ctx.req.query('limit') ?? 25}`; @@ -56,9 +78,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${titles[id]} - Odaily星球日报`, link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/odaily/search-news.ts b/lib/routes/odaily/search-news.ts index 0c083d6dc..b9f2489bb 100644 --- a/lib/routes/odaily/search-news.ts +++ b/lib/routes/odaily/search-news.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; import { rootUrl } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/search/news/:keyword', + categories: ['traditional-media'], + example: '/odaily/search/news/RSS3', + parameters: { keyword: '搜索关键字' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['0daily.com/search/:keyword'], + }, + name: '搜索快讯', + maintainers: ['snowraincloud'], + handler, +}; + +async function handler(ctx) { const currentUrl = `${rootUrl}/api/pp/api/search/entity-search?per_page=${ctx.req.query('limit') ?? 25}&keyword=${ctx.req.param('keyword')}&entity_type=newsflash`; const response = await got({ @@ -18,9 +40,9 @@ export default async (ctx) => { description: `${item.description}
`, })); - ctx.set('data', { + return { title: '快讯 - Odaily星球日报', link: `${rootUrl}/search/${ctx.req.param('keyword')}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/odaily/user.ts b/lib/routes/odaily/user.ts index ddd46b318..5e591bdef 100644 --- a/lib/routes/odaily/user.ts +++ b/lib/routes/odaily/user.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -5,7 +6,28 @@ import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; import { rootUrl } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/user/:id', + categories: ['traditional-media'], + example: '/odaily/user/2147486902', + parameters: { id: '用户 id,可在用户页地址栏中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['0daily.com/user/:id', '0daily.com/'], + }, + name: '用户文章', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const currentUrl = `${rootUrl}/service/feed_stream/user/${id}?b_id=10&per_page=${ctx.req.query('limit') ?? 25}`; @@ -46,9 +68,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${author} - Odaily星球日报`, link: `${rootUrl}/user/${id}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/oeeee/app/channel.ts b/lib/routes/oeeee/app/channel.ts index dd4428e59..125ebcafa 100644 --- a/lib/routes/oeeee/app/channel.ts +++ b/lib/routes/oeeee/app/channel.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -9,7 +10,14 @@ import { parseArticle } from '../utils'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/app/channel/:id', + name: 'Unknown', + maintainers: ['TimWu007'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? 50; const currentUrl = `https://api-ndapp.oeeee.com/friends.php?m=Zone&a=SpaceDoclist&uid=${id}&type=doc`; @@ -32,9 +40,9 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => parseArticle(item, cache.tryGet))); - ctx.set('data', { + return { title: `南方都市报客户端 - ${channel}`, link: `https://m.mp.oeeee.com/u/${id}.html`, item: items, - }); -}; + }; +} diff --git a/lib/routes/oeeee/app/reporter.ts b/lib/routes/oeeee/app/reporter.ts index 8e73c16af..52ec3a0bb 100644 --- a/lib/routes/oeeee/app/reporter.ts +++ b/lib/routes/oeeee/app/reporter.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -7,7 +8,25 @@ import { parseArticle } from '../utils'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/app/reporter/:id', + categories: ['bbs'], + example: '/oeeee/app/reporter/249', + parameters: { id: '记者 UID' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '南都客户端(按记者)', + maintainers: ['TimWu007'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? 0; const currentUrl = `https://m.mp.oeeee.com/show.php?m=Doc&a=getAuthorInfo&id=${id}`; @@ -26,9 +45,9 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => parseArticle(item, cache.tryGet))); - ctx.set('data', { + return { title: `南方都市报奥一网 - ${author}`, link: `https://m.mp.oeeee.com/w/${id}.html`, item: items, - }); -}; + }; +} diff --git a/lib/routes/oeeee/web.ts b/lib/routes/oeeee/web.ts index 87c6ae1e2..fe710f76d 100644 --- a/lib/routes/oeeee/web.ts +++ b/lib/routes/oeeee/web.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -9,7 +10,25 @@ import { parseArticle } from './utils'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/web/:channel', + categories: ['bbs'], + example: '/oeeee/web/170', + parameters: { channel: '频道 ID' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '奥一网', + maintainers: ['TimWu007'], + handler, +}; + +async function handler(ctx) { const channel = ctx.req.param('channel') ?? 0; const currentUrl = `https://www.oeeee.com/api/channel.php?m=Js4channelNews&a=newLatest&cid=${channel}`; @@ -32,9 +51,9 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => parseArticle(item, cache.tryGet))); - ctx.set('data', { + return { title: `南方都市报奥一网`, link: `https://www.oeeee.com/api/channel.php?s=/index/index/channel/${channelEname}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/oilchem/index.ts b/lib/routes/oilchem/index.ts index 10b349e60..39b61c3dd 100644 --- a/lib/routes/oilchem/index.ts +++ b/lib/routes/oilchem/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -5,7 +6,25 @@ import routes from './routes'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:type?/:category?/:subCategory?', + categories: ['traditional-media'], + example: '/oilchem/list/140/18263', + parameters: { type: '类别 id,可在对应类别页中找到,默认为首页', category: '分类 id,可在对应分类页中找到', subCategory: '子分类 id,可在对应分类页中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '资讯', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') ?? ''; const category = ctx.req.param('category') ?? ''; const subCategory = ctx.req.param('subCategory') ?? ''; @@ -59,9 +78,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('.hdbox h3').text()} - 隆众资讯`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/oncc/index.ts b/lib/routes/oncc/index.ts index f2176e47b..c1ba7f943 100644 --- a/lib/routes/oncc/index.ts +++ b/lib/routes/oncc/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -38,7 +39,25 @@ const channelMap = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/:language/:channel?', + categories: ['bbs'], + example: '/oncc/zh-hant/news', + parameters: { language: '`zh-hans` 为简体,`zh-hant` 为繁体', channel: '频道,默认为港澳' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '即時新聞', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const language = ctx.req.param('language'); const channel = ctx.req.param('channel') ?? 'news'; const newsUrl = `${rootUrl}/hk/${channel}/index${languageMap[language]}.html`; @@ -79,9 +98,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `東網 - ${channelMap[channel][language]}`, link: newsUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/oncc/money18.ts b/lib/routes/oncc/money18.ts index 91bad30ea..def18d0b5 100644 --- a/lib/routes/oncc/money18.ts +++ b/lib/routes/oncc/money18.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -23,7 +24,25 @@ const sections = { tech: '科技財情', }; -export default async (ctx) => { +export const route: Route = { + path: '/money18/:id?', + categories: ['bbs'], + example: '/oncc/money18/exp', + parameters: { id: '栏目 id,可在对应栏目页 URL 中找到,默认为 exp,即新聞總覽' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Money18', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? 'exp'; const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 30; @@ -111,9 +130,9 @@ export default async (ctx) => { ); } - ctx.set('data', { + return { title: `東網產經 - ${sections[id]}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/onehu/common.ts b/lib/routes/onehu/common.ts index deec272d2..76c109be3 100644 --- a/lib/routes/onehu/common.ts +++ b/lib/routes/onehu/common.ts @@ -1,9 +1,17 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/', + name: 'Unknown', + maintainers: ['ruoshui9527'], + handler, +}; + +async function handler() { const link = 'https://onehu.xyz'; const response = await got(link); const data = response.data; @@ -20,9 +28,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('title').text(), link, item: list, - }); -}; + }; +} diff --git a/lib/routes/onet/news.ts b/lib/routes/onet/news.ts index d522a9368..5db07e69b 100644 --- a/lib/routes/onet/news.ts +++ b/lib/routes/onet/news.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -10,7 +11,28 @@ import { art } from '@/utils/render'; import * as path from 'node:path'; import { parseArticleContent, parseMainImage } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/news', + categories: ['traditional-media'], + example: '/onet/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['wiadomosci.onet.pl/'], + }, + name: 'News', + maintainers: ['Vegann'], + handler, +}; + +async function handler() { const rssUrl = 'https://wiadomosci.onet.pl/.feed'; const feed = await parser.parseURL(rssUrl); const items = await Promise.all( @@ -49,12 +71,12 @@ export default async (ctx) => { }; }) ); - ctx.set('data', { + return { title: feed.title, link: feed.link, description: feed.title, item: items, language: 'pl', image: 'https://ocdn.eu/wiadomosciucs/static/logo2017/onet2017big_dark.png', - }); -}; + }; +} diff --git a/lib/routes/oo-software/changelog.ts b/lib/routes/oo-software/changelog.ts index b3f0ff224..b9669d2ca 100644 --- a/lib/routes/oo-software/changelog.ts +++ b/lib/routes/oo-software/changelog.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/changelog/:id', + categories: ['university'], + example: '/oo-software/changelog/shutup10', + parameters: { id: 'Software id, see below, shutup10 by default, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Changelog', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? 'shutup10'; const rootUrl = 'https://www.oo-software.com'; @@ -32,9 +51,9 @@ export default async (ctx) => { items[0].enclosure_url = $('.banner-inlay').find('a').attr('href'); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/openai/blog.ts b/lib/routes/openai/blog.ts index 39cbc1601..7fb9ef507 100644 --- a/lib/routes/openai/blog.ts +++ b/lib/routes/openai/blog.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { toTitleCase } from '@/utils/common-utils'; import { getApiUrl, parseArticle } from './common'; -export default async (ctx) => { +export const route: Route = { + path: '/blog/:tag?', + categories: ['traditional-media'], + example: '/openai/blog', + parameters: { tag: 'Tag, see below, All by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Blog', + maintainers: ['StevenRCE0', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const tag = ctx.req.param('tag') || ''; const rootUrl = 'https://openai.com'; @@ -36,9 +55,9 @@ export default async (ctx) => { const title = `OpenAI Blog${tag ? ` - ${toTitleCase(tag)}` : ''}`; - ctx.set('data', { + return { title, link: blogOriginUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/openai/chatgpt.ts b/lib/routes/openai/chatgpt.ts index 4a6d6fda2..1ba79d009 100644 --- a/lib/routes/openai/chatgpt.ts +++ b/lib/routes/openai/chatgpt.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import dayjs from 'dayjs'; import got from '@/utils/got'; @@ -7,7 +8,25 @@ import { config } from '@/config'; import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'; dayjs.extend(isSameOrBefore); -export default async (ctx) => { +export const route: Route = { + path: '/chatgpt/release-notes', + categories: ['traditional-media'], + example: '/openai/chatgpt/release-notes', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'ChatGPT - Release Notes', + maintainers: [], + handler, +}; + +async function handler() { const articleUrl = 'https://help.openai.com/en/articles/6825453-chatgpt-release-notes'; const cacheIn = await cache.tryGet( @@ -91,10 +110,10 @@ export default async (ctx) => { false ); - ctx.set('data', { + return { title: cacheIn.feedTitle, description: cacheIn.feedDesc, link: articleUrl, item: cacheIn.items, - }); -}; + }; +} diff --git a/lib/routes/openai/research.ts b/lib/routes/openai/research.ts index 08433fbb8..78f66dc4c 100644 --- a/lib/routes/openai/research.ts +++ b/lib/routes/openai/research.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { getApiUrl, parseArticle } from './common'; -export default async (ctx) => { +export const route: Route = { + path: '/research', + categories: ['traditional-media'], + example: '/openai/research', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Research', + maintainers: ['yuguorui'], + handler, +}; + +async function handler(ctx) { const apiUrl = new URL('/api/v1/research-publications', await getApiUrl()); const researchRootUrl = 'https://openai.com/research'; @@ -24,9 +43,9 @@ export default async (ctx) => { const title = 'OpenAI Research'; - ctx.set('data', { + return { title, link: researchRootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/openwrt/releases.ts b/lib/routes/openwrt/releases.ts index 33fc38e25..c939e9133 100644 --- a/lib/routes/openwrt/releases.ts +++ b/lib/routes/openwrt/releases.ts @@ -1,7 +1,19 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/releases/:brand/:model', + radar: { + source: ['openwrt.org/toh/:band/:model'], + target: '/releases/:model', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const brand = ctx.req.param('brand'); const model = ctx.req.param('model'); const url = `https://openwrt.org/toh/${brand}/${model}`; @@ -9,7 +21,7 @@ export default async (ctx) => { const $ = load(response.data); const table = $('#installation').next(); - ctx.set('data', { + return { title: $('h1').text() + ' - OpenWrt Releases', link: url, description: $('.dw-content div.level1').text(), @@ -21,5 +33,5 @@ export default async (ctx) => { description: `Firmware OpenWrt Install: ${table.find('.firmware_openwrt_install_url').html()}${$('.head-info-desc').text()}
`, - }); -}; + }; +} diff --git a/lib/routes/qq/ac/rank.ts b/lib/routes/qq/ac/rank.ts index 59c2126ba..24a9900c1 100644 --- a/lib/routes/qq/ac/rank.ts +++ b/lib/routes/qq/ac/rank.ts @@ -1,6 +1,28 @@ +import { Route } from '@/types'; import { rootUrl, ProcessItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/ac/rank/:type?/:time?', + categories: ['program-update'], + example: '/qq/ac/rank', + parameters: { type: '分类,见下表,默认为月票榜', time: '时间,`cur` 为当周、`prev` 为上周' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ac.qq.com/Rank/comicRank/type/:type', 'ac.qq.com/'], + }, + name: '排行榜', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const titles = { mt: '月票榜', rise: '飙升榜', @@ -17,4 +39,4 @@ export default async (ctx) => { const currentUrl = `${rootUrl}/Rank/comicRank/type/${type}`; ctx.set('data', await ProcessItems(ctx, currentUrl, time, titles[type])); -}; +} diff --git a/lib/routes/qq/fact/index.ts b/lib/routes/qq/fact/index.ts index e28481077..f186cf28a 100644 --- a/lib/routes/qq/fact/index.ts +++ b/lib/routes/qq/fact/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -17,7 +18,28 @@ const getRequestToken = () => { const baseUrl = 'https://vp.fact.qq.com'; -export default async (ctx) => { +export const route: Route = { + path: '/fact', + categories: ['other'], + example: '/qq/fact', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['vp.fact.qq.com/home', 'vp.fact.qq.com/'], + }, + name: '最新辟谣', + maintainers: ['hoilc'], + handler, +}; + +async function handler() { const { data: response } = await got(`${baseUrl}/api/article/list`, { headers: { Referer: `${baseUrl}/home`, @@ -57,9 +79,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '较真查证平台 - 腾讯新闻', link: `${baseUrl}/home`, item: items, - }); -}; + }; +} diff --git a/lib/routes/qq/kg/reply.ts b/lib/routes/qq/kg/reply.ts index ed29bc9ba..fefa89caa 100644 --- a/lib/routes/qq/kg/reply.ts +++ b/lib/routes/qq/kg/reply.ts @@ -1,12 +1,31 @@ +import { Route } from '@/types'; import cache from './cache'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/kg/reply/:playId', + categories: ['new-media'], + example: '/qq/kg/reply/OhXHMdO1VxLWQOOm', + parameters: { playId: '音频页 ID, 可在对应页面的 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '用户作品评论动态', + maintainers: ['zhangxiang012'], + handler, +}; + +async function handler(ctx) { const playId = ctx.req.param('playId'); const url = `https://node.kg.qq.com/play?s=${playId}`; const play_item = await cache.getPlayInfo(ctx, playId, ''); - ctx.set('data', { + return { title: `${play_item.name} - ${play_item.author} 的评论`, link: url, image: play_item.itunes_item_image, @@ -17,5 +36,5 @@ export default async (ctx) => { link: url, guid: `ksong:${play_item.ksong_mid}:${item.comment_id}`, })), - }); -}; + }; +} diff --git a/lib/routes/qq/kg/user.ts b/lib/routes/qq/kg/user.ts index 3bf402d24..114a2ea34 100644 --- a/lib/routes/qq/kg/user.ts +++ b/lib/routes/qq/kg/user.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { JSDOM } from 'jsdom'; import cache from './cache'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/kg/:userId', + categories: ['new-media'], + example: '/qq/kg/639a9a86272c308e33', + parameters: { userId: '用户 ID, 可在对应页面的 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: true, + supportScihub: false, + }, + name: '用户作品列表', + maintainers: ['zhangxiang012'], + handler, +}; + +async function handler(ctx) { const userId = ctx.req.param('userId'); const url = `https://node.kg.qq.com/personal?uid=${userId}`; const response = await got(url); @@ -37,7 +56,7 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `${author} - 全民K歌`, link: url, description: data.share.content, @@ -46,5 +65,5 @@ export default async (ctx) => { image: authorimg, itunes_author: author, itunes_category: '全民K歌', - }); -}; + }; +} diff --git a/lib/routes/qq88/index.ts b/lib/routes/qq88/index.ts index 1d8a9de9e..425939615 100644 --- a/lib/routes/qq88/index.ts +++ b/lib/routes/qq88/index.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + categories: ['picture'], + example: '/qq88', + parameters: { category: '分类 id,见下表,默认为首页' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '分类', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? ''; const rootUrl = 'https://qq88.info'; @@ -53,9 +72,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('.page-title').text() || '首页'} - 秋爸日字`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/qqorw/index.ts b/lib/routes/qqorw/index.ts index fd0b6de8d..732eefbab 100644 --- a/lib/routes/qqorw/index.ts +++ b/lib/routes/qqorw/index.ts @@ -1,10 +1,32 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + categories: ['traditional-media'], + example: '/qqorw', + parameters: { category: '分类,见下表,默认为首页' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['qqorw.cn/:category', 'qqorw.cn/'], + }, + name: '每日早报', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const { category = '' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 10; @@ -63,7 +85,7 @@ export default async (ctx) => { const icon = new URL('favicon.ico', rootUrl).href; const title = $('header.archive-header h1 a').last().text(); - ctx.set('data', { + return { item: items, title: `${author}${title ? ` - ${title}` : ''}`, link: currentUrl, @@ -74,5 +96,5 @@ export default async (ctx) => { logo: icon, subtitle: $('meta[name="keywords"]').prop('content'), author, - }); -}; + }; +} diff --git a/lib/routes/questmobile/report.ts b/lib/routes/questmobile/report.ts index d1c7fc5b0..f8052c691 100644 --- a/lib/routes/questmobile/report.ts +++ b/lib/routes/questmobile/report.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -28,7 +29,25 @@ const parseTree = (tree, result = []) => { return result; }; -export default async (ctx) => { +export const route: Route = { + path: '/report/:industry?/:label?', + categories: ['traditional-media'], + example: '/questmobile/report', + parameters: { industry: '行业,见下表,默认为 `-1`,即全部行业', label: '标签,见下表,默认为 `-1`,即全部标签' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '行业研究报告', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const { industry, label } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 50; @@ -115,7 +134,7 @@ export default async (ctx) => { const image = $(`img[alt="${author}"]`).prop('src'); const icon = $('link[rel="shortcut icon"]').prop('href'); - ctx.set('data', { + return { item: items, title: `${author}${categories.length === 0 ? '' : ` - ${categories.join(' - ')}`}`, link: currentUrl, @@ -127,5 +146,5 @@ export default async (ctx) => { subtitle: $('meta[name="keywords"]').prop('content'), author, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/quicker/qa.ts b/lib/routes/quicker/qa.ts index 36d8c1059..02163ecaa 100644 --- a/lib/routes/quicker/qa.ts +++ b/lib/routes/quicker/qa.ts @@ -1,10 +1,29 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; import { parseDate, parseRelativeDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/qa/:category?/:state?', + categories: ['design'], + example: '/quicker/qa', + parameters: { category: '分类,见下表,默认为全部', state: '状态,见下表,默认为全部' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '讨论区', + maintainers: ['Cesaryuan', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'all'; const state = ctx.req.param('state') ?? ''; @@ -57,10 +76,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/quicker/share.ts b/lib/routes/quicker/share.ts index d845d5f43..6c09037f8 100644 --- a/lib/routes/quicker/share.ts +++ b/lib/routes/quicker/share.ts @@ -1,10 +1,32 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; import { parseDate, parseRelativeDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/share/:category?', + categories: ['design'], + example: '/quicker/share/Recent', + parameters: { category: '分类,见下表,默认为动作库最新更新' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['getquicker.net/Share/:category', 'getquicker.net/'], + }, + name: '动作分享', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'Recent'; const rootUrl = 'https://getquicker.net'; @@ -53,10 +75,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/quicker/user.ts b/lib/routes/quicker/user.ts index 7ca03a24c..138fffb16 100644 --- a/lib/routes/quicker/user.ts +++ b/lib/routes/quicker/user.ts @@ -1,10 +1,29 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; import { parseDate, parseRelativeDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/user/:category/:id', + categories: ['design'], + example: '/quicker/user/Actions/3-CL', + parameters: { category: '分类,见下表', id: '用户 id,可在对应用户页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '用户更新', + maintainers: ['Cesaryuan', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category'); const id = ctx.req.param('id'); @@ -54,10 +73,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/quicker/versions.ts b/lib/routes/quicker/versions.ts index 05192b024..1793d2ce3 100644 --- a/lib/routes/quicker/versions.ts +++ b/lib/routes/quicker/versions.ts @@ -1,8 +1,16 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: ['/update', '/versions'], + name: 'Unknown', + maintainers: ['Cesaryuan', 'nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://getquicker.net'; const currentUrl = `${rootUrl}/Help/Versions`; @@ -28,9 +36,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/qust/jw.ts b/lib/routes/qust/jw.ts index eb8e7f8eb..2a0f6270a 100644 --- a/lib/routes/qust/jw.ts +++ b/lib/routes/qust/jw.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; const baseUrl = 'https://jw.qust.edu.cn/'; -export default async (ctx) => { +export const route: Route = { + path: '/jw', + categories: ['forecast'], + example: '/qust/jw', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['jw.qust.edu.cn/jwtz.htm', 'jw.qust.edu.cn/'], + }, + name: '教务通知', + maintainers: ['Silent-wqh'], + handler, +}; + +async function handler() { const response = await got({ method: 'get', url: `${baseUrl}jwtz.htm`, @@ -22,9 +44,9 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title: '青岛科技大学 - 教务通知', link: `${baseUrl}jwtz.htm`, item: items, - }); -}; + }; +} diff --git a/lib/routes/qweather/3days.ts b/lib/routes/qweather/3days.ts index 566c8229f..afdc01802 100644 --- a/lib/routes/qweather/3days.ts +++ b/lib/routes/qweather/3days.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -12,7 +13,25 @@ const AIR_QUALITY_API = 'https://devapi.qweather.com/v7/air/5d'; const CIRY_LOOKUP_API = 'https://geoapi.qweather.com/v2/city/lookup'; const author = 'QWeather'; -export default async (ctx) => { +export const route: Route = { + path: '/3days/:location', + categories: ['travel'], + example: '/qweather/3days/广州', + parameters: { location: 'N' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '近三天天气', + maintainers: ['Rein-Ou', 'la3rence'], + handler, +}; + +async function handler(ctx) { if (!config.hefeng.key) { throw new Error('QWeather RSS is disabled due to the lack of relevant config'); } @@ -67,11 +86,11 @@ export default async (ctx) => { author, })); - ctx.set('data', { + return { title: ctx.req.param('location') + '未来三天天气', description: ctx.req.param('location') + '未来三天天气情况,使用和风彩云 API (包括空气质量)', item: items, link: combined.fxLink, author, - }); -}; + }; +} diff --git a/lib/routes/qweather/now.ts b/lib/routes/qweather/now.ts index 1a770f033..2f8cb91bf 100644 --- a/lib/routes/qweather/now.ts +++ b/lib/routes/qweather/now.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -8,7 +9,25 @@ import * as path from 'node:path'; import { parseDate } from '@/utils/parse-date'; import { config } from '@/config'; const rootUrl = 'https://devapi.qweather.com/v7/weather/now?'; -export default async (ctx) => { +export const route: Route = { + path: '/now/:location', + categories: ['travel'], + example: '/qweather/广州', + parameters: { location: 'N' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '实时天气', + maintainers: ['Rein-Ou'], + handler, +}; + +async function handler(ctx) { const id = await cache.tryGet(ctx.req.param('location') + '_id', async () => { const response = await got(`https://geoapi.qweather.com/v2/city/lookup?location=${ctx.req.param('location')}&key=${config.hefeng.key}`); const data = []; @@ -34,7 +53,7 @@ export default async (ctx) => { const time_show = timeObj.toLocaleString(); - ctx.set('data', { + return { title: ctx.req.param('location') + '实时天气', description: ctx.req.param('location') + '实时天气状况', item: data.map((item) => ({ @@ -45,5 +64,5 @@ export default async (ctx) => { link: responseData.fxLink, })), link: responseData.fxLink, - }); -}; + }; +} diff --git a/lib/routes/radio-canada/latest.ts b/lib/routes/radio-canada/latest.ts index accb7d5c0..2f167aa12 100644 --- a/lib/routes/radio-canada/latest.ts +++ b/lib/routes/radio-canada/latest.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/latest/:language?', + categories: ['traditional-media'], + example: '/radio-canada/latest', + parameters: { language: 'Language, see below, English by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ici.radio-canada.ca/rci/:lang', 'ici.radio-canada.ca/'], + }, + name: 'Latest News', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const language = ctx.req.param('language') ?? 'en'; const rootUrl = 'https://ici.radio-canada.ca'; @@ -43,9 +65,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: response.data.meta.title, link: response.data.metric.metrikContent.omniture.url, item: items, - }); -}; + }; +} diff --git a/lib/routes/radio/album.ts b/lib/routes/radio/album.ts index bb0ee6781..d729c81ff 100644 --- a/lib/routes/radio/album.ts +++ b/lib/routes/radio/album.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -7,7 +8,25 @@ import { art } from '@/utils/render'; import * as path from 'node:path'; import CryptoJS from 'crypto-js'; -export default async (ctx) => { +export const route: Route = { + path: '/album/:id', + categories: ['picture'], + example: '/radio/album/15682090498666', + parameters: { id: '专辑 id,可在对应专辑页面的 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: true, + supportScihub: false, + }, + name: '专辑', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const KEY = 'f0fc4c668392f9f9a447e48584c214ee'; const id = ctx.req.param('id'); @@ -97,7 +116,7 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `云听 - ${data.columnName}`, link: currentUrl, item: items, @@ -105,5 +124,5 @@ export default async (ctx) => { itunes_author: data.anchorpersons, description: data.descriptions ?? data.descriptionSimple, itunes_category: data.atypeInfo.map((c) => c.categoryName).join(','), - }); -}; + }; +} diff --git a/lib/routes/radio/index.ts b/lib/routes/radio/index.ts index d155279f8..429453cb5 100644 --- a/lib/routes/radio/index.ts +++ b/lib/routes/radio/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -6,7 +7,25 @@ import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/:id', + categories: ['picture'], + example: '/radio/1552135', + parameters: { id: '专辑 id,可在对应专辑页面的 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: true, + supportScihub: false, + }, + name: '节目', + maintainers: ['kt286', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const size = ctx.req.query('limit') ?? '100'; @@ -47,12 +66,12 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `云听 - ${data.odchannel.name}`, link: currentUrl, item: items, image: data.odchannel.imageUrl[0].url, itunes_author: data.odchannel.commissioningEditorName || data.odchannel.editorName || data.odchannel.source || 'radio.cn', description: data.odchannel.description || data.odchannel.sub_title || '', - }); -}; + }; +} diff --git a/lib/routes/radio/zhibo.ts b/lib/routes/radio/zhibo.ts index e96a2abd3..d6a080a83 100644 --- a/lib/routes/radio/zhibo.ts +++ b/lib/routes/radio/zhibo.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -7,7 +8,25 @@ import { art } from '@/utils/render'; import * as path from 'node:path'; import CryptoJS from 'crypto-js'; -export default async (ctx) => { +export const route: Route = { + path: '/zhibo/:id', + categories: ['picture'], + example: '/radio/zhibo/1395528', + parameters: { id: '直播 id,可在对应点播页面的 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: true, + supportScihub: false, + }, + name: '直播', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const KEY = 'f0fc4c668392f9f9a447e48584c214ee'; const id = ctx.req.param('id'); @@ -65,12 +84,12 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `云听 - ${data[0].name}`, link: currentUrl, item: items, image: iconUrl, itunes_author: 'radio.cn', description: data[0].des, - }); -}; + }; +} diff --git a/lib/routes/rarehistoricalphotos/index.ts b/lib/routes/rarehistoricalphotos/index.ts index 64b0319be..9a7146763 100644 --- a/lib/routes/rarehistoricalphotos/index.ts +++ b/lib/routes/rarehistoricalphotos/index.ts @@ -1,9 +1,21 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; const baseUrl = 'https://rarehistoricalphotos.com'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['rarehistoricalphotos.com/'], + target: '', + }, + name: 'Unknown', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { data } = await got(`${baseUrl}/wp-json/wp/v2/posts`, { searchParams: { per_page: ctx.req.query('limit') ? Number(ctx.req.query('limit')) : undefined, @@ -17,12 +29,12 @@ export default async (ctx) => { pubDate: parseDate(item.date_gmt), })); - ctx.set('data', { + return { title: 'Rare Historical Photos', description: 'And the story behind them...', link: baseUrl, image: 'https://rarehistoricalphotos.com/wp-content/uploads/2022/04/cropped-rarehistoricalphotos-32x32.png', language: 'en-US', item: items, - }); -}; + }; +} diff --git a/lib/routes/rattibha/user.ts b/lib/routes/rattibha/user.ts index 9d3f900c8..1aca93a48 100644 --- a/lib/routes/rattibha/user.ts +++ b/lib/routes/rattibha/user.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/user/:user', + categories: ['new-media'], + example: '/rattibha/user/elonmusk', + parameters: { user: 'Twitter username, without @' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['rattibha.com/:user'], + }, + name: 'User Threads', + maintainers: ['yshalsager'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://rattibha.com'; const { user: twitterUser } = ctx.req.param(); @@ -38,9 +60,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `سلاسل تغريدات ${twitterUser}`, link: `${baseUrl}/${twitterUser}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/rawkuma/manga.ts b/lib/routes/rawkuma/manga.ts index 0f0f1b57b..e82dd6f37 100644 --- a/lib/routes/rawkuma/manga.ts +++ b/lib/routes/rawkuma/manga.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -8,7 +9,28 @@ import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/manga/:id', + categories: ['program-update'], + example: '/rawkuma/manga/tensei-shitara-dai-nana-ouji-dattanode-kimamani-majutsu-o-kiwamemasu', + parameters: { id: 'Manga ID, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['rawkuma.com/manga/:id', 'rawkuma.com/'], + }, + name: 'Manga', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 50; @@ -71,7 +93,7 @@ export default async (ctx) => { .prop('href') .replace(/-\d+x\d+/, ''); - ctx.set('data', { + return { item: items, title: $('title').text(), link: currentUrl, @@ -81,5 +103,5 @@ export default async (ctx) => { logo: icon, subtitle: $('div.wd-full span').text(), author, - }); -}; + }; +} diff --git a/lib/routes/reactnewsletter/reactnewsletter.ts b/lib/routes/reactnewsletter/reactnewsletter.ts index 6b687169a..9121a7efd 100644 --- a/lib/routes/reactnewsletter/reactnewsletter.ts +++ b/lib/routes/reactnewsletter/reactnewsletter.ts @@ -1,9 +1,21 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; const currentURL = 'https://reactnewsletter.com/issues'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['bytes.dev/issues', 'bytes.dev/'], + target: '', + }, + name: 'Unknown', + maintainers: ['meixger'], + handler, +}; + +async function handler() { const resp = await got(currentURL); const $ = load(resp.data); const text = $('script#__NEXT_DATA__').text(); @@ -16,10 +28,10 @@ export default async (ctx) => { link: `/issues/${item.slug}`, })); - ctx.set('data', { + return { title: 'reactnewsletter.dev', description: 'Stay up to date on the latest React news, tutorials, resources, and more. Delivered every Tuesday, for free.', link: currentURL, item: items, - }); -}; + }; +} diff --git a/lib/routes/readhub/daily.ts b/lib/routes/readhub/daily.ts index 9a0ac794a..344ccefde 100644 --- a/lib/routes/readhub/daily.ts +++ b/lib/routes/readhub/daily.ts @@ -1,10 +1,32 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { rootUrl, processItems } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/daily', + categories: ['traditional-media'], + example: '/readhub/daily', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['readhub.cn/daily'], + }, + name: '每日早报', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 11; const currentUrl = new URL('daily', rootUrl).href; @@ -28,7 +50,7 @@ export default async (ctx) => { const image = 'https://readhub-oss.nocode.com/static/readhub.png'; const icon = new URL($('link[rel="apple-touch-icon"]').prop('href'), rootUrl); - ctx.set('data', { + return { item: items, title: `${author} - ${subtitle}`, link: currentUrl, @@ -40,5 +62,5 @@ export default async (ctx) => { subtitle, author, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/readhub/index.ts b/lib/routes/readhub/index.ts index 51e9114d4..f1094f8d0 100644 --- a/lib/routes/readhub/index.ts +++ b/lib/routes/readhub/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -9,7 +10,25 @@ import * as path from 'node:path'; import { rootUrl, apiTopicUrl, art, processItems } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + categories: ['traditional-media'], + example: '/readhub', + parameters: { category: '分类,见下表,默认为热门话题' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '分类', + maintainers: ['WhiteWorld', 'nczitzk', 'Fatpandac'], + handler, +}; + +async function handler(ctx) { const { category = '' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; @@ -50,7 +69,7 @@ export default async (ctx) => { const image = $('link[rel="preload"][as="image"]').prop('href'); const icon = $('meta[property="og:image"]').prop('content'); - ctx.set('data', { + return { item: items, title: `${author} - ${subtitle}`, link: currentUrl, @@ -62,5 +81,5 @@ export default async (ctx) => { subtitle, author, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/remnote/changelog.ts b/lib/routes/remnote/changelog.ts index 8b52d6ff2..571ad3363 100644 --- a/lib/routes/remnote/changelog.ts +++ b/lib/routes/remnote/changelog.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; const FQDN = 'feedback.remnote.com'; const apiGateway = 'https://gateway.hellonext.co'; -export default async (ctx) => { +export const route: Route = { + path: '/changelog', + categories: ['university'], + example: '/remnote/changelog', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['remnote.com/changelog', 'remnote.com/'], + }, + name: 'Changelog', + maintainers: ['TonyRL', 'amakerlife'], + handler, +}; + +async function handler() { const { data } = await got(`${apiGateway}/api/v2/changelogs`, { headers: { 'x-organization': FQDN, @@ -21,12 +43,12 @@ export default async (ctx) => { pubDate: parseDate(item.published_at.timestamp), })); - ctx.set('data', { + return { title: 'Changelog | RemNote', description: 'Vote or request new RemNote features. Subscribe to get updates about new features from RemNote.', link: `https://${FQDN}/changelog`, image: 'https://vault.hnxt.dev/uploads/organization_customization/favicon/3970/88153ff13b4b03492ddfee6e675228c1.png', item: items, language: 'en-US', - }); -}; + }; +} diff --git a/lib/routes/researchgate/publications.ts b/lib/routes/researchgate/publications.ts index aa9e0d6c0..2edf93016 100644 --- a/lib/routes/researchgate/publications.ts +++ b/lib/routes/researchgate/publications.ts @@ -1,9 +1,21 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import puppeteer from '@/utils/puppeteer'; -export default async (ctx) => { +export const route: Route = { + path: '/publications/:id', + radar: { + source: ['researchgate.net/profile/:username'], + target: '/publications/:username', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const rootUrl = 'https://www.researchgate.net'; @@ -64,9 +76,9 @@ export default async (ctx) => { await browser.close(); - ctx.set('data', { + return { title: `${$('meta[property="profile:username"]').attr('content')}'s Publications - ResearchGate`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/reuters/common.ts b/lib/routes/reuters/common.ts index 5ee17716b..7c03c6232 100644 --- a/lib/routes/reuters/common.ts +++ b/lib/routes/reuters/common.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -8,7 +9,28 @@ import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/:category/:topic?', + categories: ['bbs'], + example: '/reuters/world/us', + parameters: { category: 'find it in the URL, or tables below', topic: 'find it in the URL, or tables below' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['reuters.com/:category/:topic?', 'reuters.com/'], + }, + name: 'Category/Topic/Author', + maintainers: ['LyleLee', 'HenryQW', 'proletarius101', 'black-desk', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const MUST_FETCH_BY_TOPICS = new Set(['authors']); const CAN_USE_SOPHI = ['world']; @@ -135,11 +157,11 @@ export default async (ctx) => { ); items = results.filter((r) => r.status === 'fulfilled').map((r) => r.value); - ctx.set('data', { + return { title, description, image: 'https://www.reuters.com/pf/resources/images/reuters/logo-vertical-default-512x512.png?d=116', link: `https://www.reuters.com${section_id}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/reuters/investigates.ts b/lib/routes/reuters/investigates.ts index a4bdfe3bf..a2ead14e4 100644 --- a/lib/routes/reuters/investigates.ts +++ b/lib/routes/reuters/investigates.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/investigates', + categories: ['bbs'], + example: '/reuters/investigates', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Inverstigates', + maintainers: ['LyleLee'], + handler, +}; + +async function handler() { const rootUrl = 'https://www.reuters.com'; const currentUrl = `${rootUrl}/investigates/`; const response = await got(currentUrl); @@ -32,9 +51,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('h1.series-subtitle').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/rfa/index.ts b/lib/routes/rfa/index.ts index e68be85e4..e112f9dad 100644 --- a/lib/routes/rfa/index.ts +++ b/lib/routes/rfa/index.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { load } from 'cheerio'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:language?/:channel?/:subChannel?', + categories: ['bbs'], + example: '/rfa/english', + parameters: { language: 'language, English by default', channel: 'channel', subChannel: 'subchannel, where applicable' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'News', + maintainers: ['zphw'], + handler, +}; + +async function handler(ctx) { let url = 'https://www.rfa.org/' + (ctx.req.param('language') ?? 'english'); if (ctx.req.param('channel')) { @@ -44,9 +63,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'RFA', link: 'https://www.rfa.org/', item: result, - }); -}; + }; +} diff --git a/lib/routes/rfi/news.ts b/lib/routes/rfi/news.ts index 3e1602cde..262c820fd 100644 --- a/lib/routes/rfi/news.ts +++ b/lib/routes/rfi/news.ts @@ -1,9 +1,21 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:path{.+}?', + radar: { + source: ['rfi.fr/*path'], + target: '/:path', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const rootUrl = 'https://www.rfi.fr/'; const path = ctx.req.param('path') ?? 'en'; const currentUrl = `${rootUrl}${path.endsWith('/') ? path : `${path}/`}`; @@ -57,7 +69,7 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), description: $('meta[name="description"]').attr('content'), link: currentUrl, @@ -66,5 +78,5 @@ export default async (ctx) => { logo: new URL($('link[rel="apple-touch-icon"]').attr('href'), currentUrl).href, item: items, language: $('html').attr('lang'), - }); -}; + }; +} diff --git a/lib/routes/right/forum.ts b/lib/routes/right/forum.ts index 760007a32..e89d61f6b 100644 --- a/lib/routes/right/forum.ts +++ b/lib/routes/right/forum.ts @@ -1,10 +1,29 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/forum/:id?', + categories: ['blog'], + example: '/right/forum/31', + parameters: { id: '板块 id,可在板块页 URL 中找到,默认为新手入门及其它(硬件)' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '板块', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? '31'; const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 20; @@ -58,9 +77,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('.xs2 a').text()} - 恩山无线论坛`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/rodong/news.ts b/lib/routes/rodong/news.ts index 520927853..2dc9dfeed 100644 --- a/lib/routes/rodong/news.ts +++ b/lib/routes/rodong/news.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -5,7 +6,29 @@ import { parseDate } from '@/utils/parse-date'; const host = 'http://www.rodong.rep.kp'; -export default async (ctx) => { +export const route: Route = { + path: '/news/:language?', + categories: ['bbs'], + example: '/rodong/news', + parameters: { language: 'Language, see below, `ko` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['rodong.rep.kp/cn/index.php', 'rodong.rep.kp/en/index.php', 'rodong.rep.kp/ko/index.php', 'rodong.rep.kp/cn', 'rodong.rep.kp/en', 'rodong.rep.kp/ko'], + target: '/news', + }, + name: 'News', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { language = 'ko' } = ctx.req.param(); const link = `${host}/${language}/index.php?MkBAMkAxQA==`; const { data: response } = await got(link); @@ -37,10 +60,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), description: $('head meta[name="description"]').attr('content'), link, item: items, - }); -}; + }; +} diff --git a/lib/routes/routledge/book-series.ts b/lib/routes/routledge/book-series.ts index 07811ff4a..20faf1fac 100644 --- a/lib/routes/routledge/book-series.ts +++ b/lib/routes/routledge/book-series.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -8,7 +9,17 @@ import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/:bookName/book-series/:bookId', + radar: { + source: ['routledge.com/:bookName/book-series/:bookId'], + }, + name: 'Unknown', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { bookName, bookId } = ctx.req.param(); const baseUrl = 'https://www.routledge.com'; const pageUrl = `${baseUrl}/${bookName}/book-series/${bookId}`; @@ -73,10 +84,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), description: $('head meta[name="description"]').attr('content'), link: pageUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/rsc/journal.ts b/lib/routes/rsc/journal.ts index c60cad9d2..dc66f8e5b 100644 --- a/lib/routes/rsc/journal.ts +++ b/lib/routes/rsc/journal.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -9,7 +10,25 @@ import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/journal/:id/:category?', + categories: ['finance'], + example: '/rsc/journal/ta', + parameters: { id: 'Journal id, can be found in URL', category: 'Category, see below, All Recent Articles by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Journal', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const { id, category = 'allrecentarticles' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 50; @@ -94,7 +113,7 @@ export default async (ctx) => { const icon = new URL($('link[rel="apple-touch-icon"]').prop('href'), rootUrl).href; - ctx.set('data', { + return { item: items, title: $('meta[name="citation_title"]').prop('content'), link: currentUrl, @@ -106,5 +125,5 @@ export default async (ctx) => { subtitle: $('title').text(), author: $('meta[name="citation_journal_abbrev"]').prop('content'), allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/rsshub/routes.ts b/lib/routes/rsshub/routes.ts index 0b679cf00..03bb4088c 100644 --- a/lib/routes/rsshub/routes.ts +++ b/lib/routes/rsshub/routes.ts @@ -1,7 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/routes/:lang?', + categories: ['university'], + example: '/rsshub/routes/en', + parameters: { lang: 'Language, `zh` means Chinese docs, other values or null means English docs, `en` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['docs.rsshub.app/*'], + target: '/routes', + }, + name: 'New routes', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { const isEnglish = ctx.req.param('lang') !== 'zh'; const lang = isEnglish ? '' : 'zh/'; @@ -44,7 +67,7 @@ export default async (ctx) => { ); const list = all.flatMap(({ page, item, type }) => item.map((item) => ({ page, item, type }))); - ctx.set('data', { + return { title: isEnglish ? 'RSSHub has new routes' : 'RSSHub 有新路由啦', link: 'https://docs.rsshub.app', description: isEnglish ? 'Everything is RSSible' : '万物皆可 RSS', @@ -61,5 +84,5 @@ export default async (ctx) => { guid: item.attr('id'), }; }), - }); -}; + }; +} diff --git a/lib/routes/rsshub/transform/html.ts b/lib/routes/rsshub/transform/html.ts index 0ee1d89c5..e80b79390 100644 --- a/lib/routes/rsshub/transform/html.ts +++ b/lib/routes/rsshub/transform/html.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/transform/html/:url/:routeParams', + categories: ['other'], + example: '/rsshub/transform/html/https%3A%2F%2Fwechat2rss.xlab.app%2Fposts%2Flist%2F/item=div%5Bclass%3D%27post%2Dcontent%27%5D%20p%20a', + parameters: { url: '`encodeURIComponent`ed URL address', routeParams: 'Transformation rules, requires URL encode' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Transformation - HTML', + maintainers: ['ttttmr'], + handler, +}; + +async function handler(ctx) { if (!config.feature.allow_user_supply_unsafe_domain) { throw new Error(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`); } @@ -56,10 +75,10 @@ export default async (ctx) => { }) .filter(Boolean); - ctx.set('data', { + return { title: rssTitle, link: url, description: `Proxy ${url}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/rsshub/transform/json.ts b/lib/routes/rsshub/transform/json.ts index 4d8b82f38..944d34b6f 100644 --- a/lib/routes/rsshub/transform/json.ts +++ b/lib/routes/rsshub/transform/json.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { config } from '@/config'; @@ -14,7 +15,25 @@ function jsonGet(obj, attr) { return obj; } -export default async (ctx) => { +export const route: Route = { + path: '/transform/json/:url/:routeParams', + categories: ['other'], + example: '/rsshub/transform/json/https%3A%2F%2Fapi.github.com%2Frepos%2Fginuerzh%2Fgost%2Freleases/title=Gost%20releases&itemTitle=tag_name&itemLink=html_url&itemDesc=body', + parameters: { url: '`encodeURIComponent`ed URL address', routeParams: 'Transformation rules, requires URL encode' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Transformation - JSON', + maintainers: ['ttttmr'], + handler, +}; + +async function handler(ctx) { if (!config.feature.allow_user_supply_unsafe_domain) { throw new Error(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`); } @@ -49,10 +68,10 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: rssTitle, link: url, description: `Proxy ${url}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/rsshub/transform/sitemap.ts b/lib/routes/rsshub/transform/sitemap.ts index 482454a33..19f5d8d32 100644 --- a/lib/routes/rsshub/transform/sitemap.ts +++ b/lib/routes/rsshub/transform/sitemap.ts @@ -1,8 +1,16 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/transform/sitemap/:url/:routeParams?', + name: 'Unknown', + maintainers: ['flrngel'], + handler, +}; + +async function handler(ctx) { if (!config.feature.allow_user_supply_unsafe_domain) { throw new Error(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`); } @@ -41,10 +49,10 @@ export default async (ctx) => { .filter(Boolean) : []; - ctx.set('data', { + return { title: rssTitle, link: url, description: `Proxy ${url}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/ruancan/category.ts b/lib/routes/ruancan/category.ts index e7b48d198..45b4e0ea7 100644 --- a/lib/routes/ruancan/category.ts +++ b/lib/routes/ruancan/category.ts @@ -1,8 +1,31 @@ +import { Route } from '@/types'; import fetchFeed from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/category/:category?', + categories: ['traditional-media'], + example: '/ruancan/category/news', + parameters: { category: '分类 id,可在对应分类页 URL 中找到,默认为业界' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ruancan.com/cat/:category', 'ruancan.com/'], + target: '/category/:category', + }, + name: '分类', + maintainers: [], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category'); const currentUrl = `/cat/${category}`; ctx.set('data', await fetchFeed(ctx, currentUrl)); -}; +} diff --git a/lib/routes/ruancan/index.ts b/lib/routes/ruancan/index.ts index 1a5a347cf..09d8a533d 100644 --- a/lib/routes/ruancan/index.ts +++ b/lib/routes/ruancan/index.ts @@ -1,7 +1,19 @@ +import { Route } from '@/types'; import fetchFeed from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['ruancan.com/'], + target: '', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const currentUrl = ''; ctx.set('data', await fetchFeed(ctx, currentUrl)); -}; +} diff --git a/lib/routes/ruancan/search.ts b/lib/routes/ruancan/search.ts index 00a871c66..915247dc1 100644 --- a/lib/routes/ruancan/search.ts +++ b/lib/routes/ruancan/search.ts @@ -1,8 +1,31 @@ +import { Route } from '@/types'; import fetchFeed from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/search/:keyword?', + categories: ['traditional-media'], + example: '/ruancan/search/Windows', + parameters: { keyword: '关键字,默认为空' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ruancan.com/'], + target: '', + }, + name: '搜索', + maintainers: [], + handler, +}; + +async function handler(ctx) { const keyword = ctx.req.param('keyword'); const currentUrl = `/?s=${keyword}`; ctx.set('data', await fetchFeed(ctx, currentUrl)); -}; +} diff --git a/lib/routes/ruancan/user.ts b/lib/routes/ruancan/user.ts index 1be09ad8d..33eae74a0 100644 --- a/lib/routes/ruancan/user.ts +++ b/lib/routes/ruancan/user.ts @@ -1,8 +1,19 @@ +import { Route } from '@/types'; import fetchFeed from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/user/:id', + radar: { + source: ['ruancan.com/i/:id', 'ruancan.com/'], + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const currentUrl = `/i/${id}`; ctx.set('data', await fetchFeed(ctx, currentUrl)); -}; +} diff --git a/lib/routes/ruc/hr.ts b/lib/routes/ruc/hr.ts index fa2a5250c..f33f33997 100644 --- a/lib/routes/ruc/hr.ts +++ b/lib/routes/ruc/hr.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/hr/:category?', + categories: ['forecast'], + example: '/ruc/hr', + parameters: { category: '分类,见下方说明,默认为首页通知公告' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['hr.ruc.edu.cn/'], + }, + name: '人事处', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category')?.replace(/-/g, '/') ?? 'tzgg'; const rootUrl = 'http://hr.ruc.edu.cn'; @@ -51,9 +73,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/runtrail/posts.ts b/lib/routes/runtrail/posts.ts index 49f2ac85d..958e7237f 100644 --- a/lib/routes/runtrail/posts.ts +++ b/lib/routes/runtrail/posts.ts @@ -1,7 +1,19 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['runtrail.cn/'], + target: '', + }, + name: 'Unknown', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://runtrail.cn'; const { data: response } = await got(`${baseUrl}/wp-json/wp/v2/posts`, { searchParams: { @@ -26,9 +38,9 @@ export default async (ctx) => { link: item.link, })); - ctx.set('data', { + return { title: '最新文章 - 跑野大爆炸', link: baseUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/rustcc/jobs.ts b/lib/routes/rustcc/jobs.ts index b2d990265..9eb6dfda0 100644 --- a/lib/routes/rustcc/jobs.ts +++ b/lib/routes/rustcc/jobs.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; @@ -5,7 +6,28 @@ import timezone from '@/utils/timezone'; const base_url = 'https://rustcc.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/jobs', + categories: ['design'], + example: '/rustcc/jobs', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['rustcc.cn/'], + }, + name: '招聘', + maintainers: ['CcccFz'], + handler, +}; + +async function handler() { const jobs_url = 'https://rustcc.cn/section?id=fed6b7de-0a74-48eb-8988-1978858c9b35'; const response = await got({ @@ -18,13 +40,13 @@ export default async (ctx) => { const $ = load(response.data); const list = $('.article-list li').get(); - ctx.set('data', { + return { title: 'Rust语言中文社区 | 招聘', link: jobs_url, description: `获取Rust语言中文社区的最新招聘`, item: await Promise.all(list.map((item) => getFeedItem(item))), - }); -}; + }; +} function getFeedItem(item) { const $ = load(item); diff --git a/lib/routes/sakurazaka46/blog.ts b/lib/routes/sakurazaka46/blog.ts index 8bc20073b..2516e4ad5 100644 --- a/lib/routes/sakurazaka46/blog.ts +++ b/lib/routes/sakurazaka46/blog.ts @@ -1,10 +1,29 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/blog/:id?/:page?', + categories: ['traditional-media'], + example: '/sakurazaka46/blog', + parameters: { id: 'Member ID, see below, `all` by default', page: 'Page, `0` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Sakurazaka46 Blog 櫻坂 46 博客', + maintainers: ['victor21813', 'nczitzk', 'akashigakki'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? 'all'; const page = ctx.req.param('page') ?? '0'; @@ -49,9 +68,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('title').text()}${id ? ` - ${$('.name').first().text()}` : ''}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/sakurazaka46/news.ts b/lib/routes/sakurazaka46/news.ts index bb737212e..fd67c58dd 100644 --- a/lib/routes/sakurazaka46/news.ts +++ b/lib/routes/sakurazaka46/news.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/news', + categories: ['traditional-media'], + example: '/sakurazaka46/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['sakurazaka46.com/s/s46/news/list', 'sakurazaka46.com/'], + }, + name: 'Sakurazaka46 News 櫻坂 46 新闻', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const rootUrl = 'https://sakurazaka46.com'; const currentUrl = `${rootUrl}/s/s46/news/list`; @@ -44,9 +66,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/samsung/research/blog.ts b/lib/routes/samsung/research/blog.ts index 9f840498b..543f67e96 100644 --- a/lib/routes/samsung/research/blog.ts +++ b/lib/routes/samsung/research/blog.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/research/blog', + categories: ['traditional-media'], + example: '/samsung/research/blog', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['research.samsung.com/blog', 'research.samsung.com/'], + }, + name: 'Research Blog', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://research.samsung.com'; const currentUrl = `${rootUrl}/blogMain/list.json`; @@ -42,9 +64,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'BLOG | Samsung Research', link: `${rootUrl}/blog`, item: items, - }); -}; + }; +} diff --git a/lib/routes/saraba1st/digest.ts b/lib/routes/saraba1st/digest.ts index 4c6b0edca..367e2f741 100644 --- a/lib/routes/saraba1st/digest.ts +++ b/lib/routes/saraba1st/digest.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -10,7 +11,25 @@ import timezone from '@/utils/timezone'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/digest/:tid', + categories: ['blog'], + example: '/saraba1st/digest/forum-75-1', + parameters: { tid: '论坛 id' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '论坛摘要', + maintainers: ['shinemoon'], + handler, +}; + +async function handler(ctx) { const tid = ctx.req.param('tid'); const cookieString = config.saraba1st.cookie ?? ''; const res = await got('https://bbs.saraba1st.com/2b/' + tid + '.html', { @@ -45,13 +64,13 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `Stage1 论坛 - ${title}`, link: `https://bbs.saraba1st.com/2b/${tid}.html`, // item: await resultItems, item: resultItems, - }); -}; + }; +} async function fetchContent(url) { // Fetch the subpageinof diff --git a/lib/routes/saraba1st/thread.ts b/lib/routes/saraba1st/thread.ts index 722a65337..c1228ed30 100644 --- a/lib/routes/saraba1st/thread.ts +++ b/lib/routes/saraba1st/thread.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import queryString from 'query-string'; @@ -5,7 +6,25 @@ import { config } from '@/config'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -export default async (ctx) => { +export const route: Route = { + path: '/thread/:tid', + categories: ['blog'], + example: '/saraba1st/thread/1842868', + parameters: { tid: '帖子 id' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '帖子', + maintainers: ['zengxs'], + handler, +}; + +async function handler(ctx) { const tid = ctx.req.param('tid'); const cookieString = config.saraba1st.cookie ?? ''; @@ -55,9 +74,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `Stage1 论坛 - ${title}`, link: `https://bbs.saraba1st.com/2b/thread-${tid}-1-1.html`, item: resultItems, - }); -}; + }; +} diff --git a/lib/routes/sass/gs/index.ts b/lib/routes/sass/gs/index.ts index a5def11c9..bd15103cf 100644 --- a/lib/routes/sass/gs/index.ts +++ b/lib/routes/sass/gs/index.ts @@ -1,10 +1,32 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; const host = 'https://gs.sass.org.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/gs/:type', + categories: ['forecast'], + example: '/sass/gs/1793', + parameters: { type: '类别 ID,见下表,其他未列出的栏目参数可以从页面的 URL Path 中找到,例如:硕士统考招生的网址为 `https://gs.sass.org.cn/1793/list.htm`,则类别 ID 为`1793`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gs.sass.org.cn/:type/list.htm'], + }, + name: '研究生院', + maintainers: ['yanbot-team'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); const url = `${host}/${type}/list.htm`; @@ -38,10 +60,10 @@ export default async (ctx) => { }) ); // 处理返回 - ctx.set('data', { + return { title: '上海社会科学院 - 研究生院', link: url, description: '上海社会科学院 - 研究生院', item: items, - }); -}; + }; +} diff --git a/lib/routes/scau/yjs.ts b/lib/routes/scau/yjs.ts index 1c2756e6a..83b622a81 100644 --- a/lib/routes/scau/yjs.ts +++ b/lib/routes/scau/yjs.ts @@ -1,14 +1,36 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/yzb', + categories: ['forecast'], + example: '/scau/yzb', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['yzb.scau.edu.cn/2136/list1.htm', 'yzb.scau.edu.cn/'], + }, + name: '华农研讯', + maintainers: ['shengmaosu'], + handler, +}; + +async function handler() { const link = 'https://yzb.scau.edu.cn/2136/list1.htm'; const response = await got(link); const $ = load(response.data); const list = $('#wp_news_w25 tr'); - ctx.set('data', { + return { title: '华南农业大学研招办', link, description: '华农研讯', @@ -23,5 +45,5 @@ export default async (ctx) => { pubDate: parseDate(item.find('td').eq(3).text(), 'YYYY/MM/DD'), }; }), - }); -}; + }; +} diff --git a/lib/routes/scau/yjsy.ts b/lib/routes/scau/yjsy.ts index 510089aaf..81761c323 100644 --- a/lib/routes/scau/yjsy.ts +++ b/lib/routes/scau/yjsy.ts @@ -1,14 +1,36 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/yjsy', + categories: ['forecast'], + example: '/scau/yjsy', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['yjsy.scau.edu.cn/208/list.htm', 'yjsy.scau.edu.cn/'], + }, + name: '研究生院通知', + maintainers: ['Chunssu'], + handler, +}; + +async function handler() { const link = 'https://yjsy.scau.edu.cn/208/list1.htm'; const response = await got(link); const $ = load(response.data); const list = $('#wp_news_w25 tr td tr'); - ctx.set('data', { + return { title: '华南农业大学研究生院', link, description: '通知公告', @@ -23,5 +45,5 @@ export default async (ctx) => { pubDate: parseDate(item.find('td').eq(2).text(), 'YYYY/MM/DD'), }; }), - }); -}; + }; +} diff --git a/lib/routes/science/blogs.ts b/lib/routes/science/blogs.ts index 285af0714..5def0f097 100644 --- a/lib/routes/science/blogs.ts +++ b/lib/routes/science/blogs.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; @@ -5,7 +6,29 @@ import { baseUrl } from './utils'; import { config } from '@/config'; import puppeteer from '@/utils/puppeteer'; -export default async (ctx) => { +export const route: Route = { + path: '/blogs/:name?', + categories: ['finance'], + example: '/science/blogs/pipeline', + parameters: { name: 'Short name for the blog, get this from the url. Defaults to pipeline' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['science.org/blogs/:name'], + target: '/blogs/:name', + }, + name: 'Blogs', + maintainers: ['TomHodson'], + handler, +}; + +async function handler(ctx) { const { name = 'pipeline' } = ctx.req.param(); const link = `${baseUrl}/blogs/${name}/feed`; @@ -53,12 +76,12 @@ export default async (ctx) => { const name_re = /Keyword search result for Blog Series: (?链接: resolveRelativeUrl(data.content) + generateArticleLink(data.id);
-export default async (ctx) => {
+export const route: Route = {
+ path: '/jwc/notice/:category?',
+ categories: ['forecast'],
+ example: '/scut/jwc/notice/all',
+ parameters: { category: '通知分类,默认为 `all`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '教务处通知公告',
+ maintainers: ['imkero'],
+ handler,
+};
+
+async function handler(ctx) {
const categoryName = ctx.req.param('category') || 'all';
const categoryMeta = categoryMap[categoryName];
@@ -116,9 +135,9 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: '华南理工大学教务处通知公告 - ' + categoryMeta.title,
link: listPageUrl,
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/scut/jwc/school.ts b/lib/routes/scut/jwc/school.ts
index dbf79de21..c10fbd227 100644
--- a/lib/routes/scut/jwc/school.ts
+++ b/lib/routes/scut/jwc/school.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import * as url from 'node:url';
@@ -51,7 +52,25 @@ const generateArticleLink = (id) => ` 链接:(.+?)<\/a><\/h5>\s* (.+?)<\/p>/g)].map((item) => ({
title: item[2],
link: `https://www.sec-wiki.com/weekly/${item[1]}`,
description: item[3],
}));
- ctx.set('data', {
+ return {
title: 'SecWiki-安全维基',
link: 'https://www.sec-wiki.com/',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/secrss/author.ts b/lib/routes/secrss/author.ts
index 02744641b..903c0d61b 100644
--- a/lib/routes/secrss/author.ts
+++ b/lib/routes/secrss/author.ts
@@ -1,9 +1,28 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { load } from 'cheerio';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/author/:author',
+ categories: ['design'],
+ example: '/secrss/author/网络安全威胁和漏洞信息共享平台',
+ parameters: { author: 'N' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '作者',
+ maintainers: ['XinRoom', 'SunBK201'],
+ handler,
+};
+
+async function handler(ctx) {
const api = 'https://www.secrss.com/api/articles/group?author=';
const author = ctx.req.param('author');
const host = 'https://www.secrss.com';
@@ -28,9 +47,9 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: `安全内参-${author}`,
link: 'https://www.secrss.com',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/secrss/category.ts b/lib/routes/secrss/category.ts
index 0b1ccb830..ba1be3524 100644
--- a/lib/routes/secrss/category.ts
+++ b/lib/routes/secrss/category.ts
@@ -1,10 +1,29 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
import { load } from 'cheerio';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/category/:category?',
+ categories: ['design'],
+ example: '/secrss/category/产业趋势',
+ parameters: { category: 'N' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '分类',
+ maintainers: ['XinRoom', 'SunBK201'],
+ handler,
+};
+
+async function handler(ctx) {
const api = 'https://www.secrss.com/api/articles?tag=';
const { category = '' } = ctx.req.param();
const host = 'https://www.secrss.com';
@@ -30,9 +49,9 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: `安全内参-${category}`,
link: 'https://www.secrss.com',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/seekingalpha/index.ts b/lib/routes/seekingalpha/index.ts
index a7fa5f76e..581a2b15c 100644
--- a/lib/routes/seekingalpha/index.ts
+++ b/lib/routes/seekingalpha/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -5,7 +6,29 @@ import { parseDate } from '@/utils/parse-date';
import dayjs from 'dayjs';
const baseUrl = 'https://seekingalpha.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:symbol/:category?',
+ categories: ['other'],
+ example: '/seekingalpha/TSM/transcripts',
+ parameters: { symbol: 'Stock symbol', category: 'Category, see below, `news` by default' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['seekingalpha.com/symbol/:symbol/:category', 'seekingalpha.com/symbol/:symbol/earnings/:category'],
+ target: '/:symbol/:category',
+ },
+ name: 'Summary',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const { category = 'news', symbol } = ctx.req.param();
const pageUrl = `${baseUrl}/symbol/${symbol.toUpperCase()}/${category === 'transcripts' ? `earnings/${category}` : category}`;
@@ -45,7 +68,7 @@ export default async (ctx) => {
)
: [];
- ctx.set('data', {
+ return {
title: response.data.meta.page.title,
description: response.data.meta.page.description,
link: pageUrl,
@@ -53,5 +76,5 @@ export default async (ctx) => {
item: items,
allowEmpty: true,
language: 'en-US',
- });
-};
+ };
+}
diff --git a/lib/routes/sega/pjsekai.ts b/lib/routes/sega/pjsekai.ts
index 4b9cd2066..d05668330 100644
--- a/lib/routes/sega/pjsekai.ts
+++ b/lib/routes/sega/pjsekai.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import * as cheerio from 'cheerio';
import timezone from '@/utils/timezone';
import cache from '@/utils/cache';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/pjsekai/news',
+ categories: ['reading'],
+ example: '/sega/pjsekai/news',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['pjsekai.sega.jp/news/index.html'],
+ },
+ name: '世界计划 多彩舞台 | ProjectSekai | プロセカ',
+ maintainers: ['15x15G'],
+ handler,
+};
+
+async function handler() {
// 从仓库 Sekai-World/sekai-master-db-diff 获取最新公告
const response = await got.get(`https://cdn.jsdelivr.net/gh/Sekai-World/sekai-master-db-diff@master/userInformations.json`);
const posts = response.data || [];
@@ -41,10 +63,10 @@ export default async (ctx) => {
return item;
})
);
- ctx.set('data', {
+ return {
title: 'Project Sekai - News',
link: 'https://pjsekai.sega.jp/',
description: 'プロジェクトセカイ カラフルステージ! feat.初音ミク',
item: list,
- });
-};
+ };
+}
diff --git a/lib/routes/segmentfault/blogs.ts b/lib/routes/segmentfault/blogs.ts
index df4002a94..f6760075f 100644
--- a/lib/routes/segmentfault/blogs.ts
+++ b/lib/routes/segmentfault/blogs.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { host, acw_sc__v2, parseList, parseItems } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/blogs/:tag',
+ categories: ['design'],
+ example: '/segmentfault/blogs/go',
+ parameters: { tag: '标签名称,在 [标签](https://segmentfault.com/tags) 中可以找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['segmentfault.com/t/:tag/blogs'],
+ },
+ name: '博客',
+ maintainers: ['shiluanzzz'],
+ handler,
+};
+
+async function handler(ctx) {
const tag = ctx.req.param('tag');
const apiURL = `${host}/gateway/tag/${tag}/articles?loadMoreType=pagination&initData=true&page=1&sort=newest&pageSize=30`;
const response = await got(apiURL);
@@ -14,9 +36,9 @@ export default async (ctx) => {
const items = await Promise.all(list.map((item) => parseItems(acwScV2Cookie, item, cache.tryGet)));
- ctx.set('data', {
+ return {
title: `segmentfault-Blogs-${tag}`,
link: `${host}/t/${tag}/blogs`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/segmentfault/channel.ts b/lib/routes/segmentfault/channel.ts
index d5a2ca0e9..df613074c 100644
--- a/lib/routes/segmentfault/channel.ts
+++ b/lib/routes/segmentfault/channel.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { host, acw_sc__v2, parseList, parseItems } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/channel/:name',
+ categories: ['design'],
+ example: '/segmentfault/channel/frontend',
+ parameters: { name: '频道名称,在频道 URL 可以找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['segmentfault.com/channel/:name'],
+ },
+ name: '频道',
+ maintainers: ['LogicJake', 'Fatpandac'],
+ handler,
+};
+
+async function handler(ctx) {
const name = ctx.req.param('name');
const link = `${host}/channel/${name}`;
@@ -27,9 +49,9 @@ export default async (ctx) => {
const items = await Promise.all(list.map((item) => parseItems(acwScV2Cookie, item, cache.tryGet)));
- ctx.set('data', {
+ return {
title: `segmentfault - ${channelName}`,
link,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/segmentfault/user.ts b/lib/routes/segmentfault/user.ts
index 114d03aa4..fda28e10b 100644
--- a/lib/routes/segmentfault/user.ts
+++ b/lib/routes/segmentfault/user.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { host, acw_sc__v2, parseList, parseItems } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/user/:name',
+ categories: ['design'],
+ example: '/segmentfault/user/minnanitkong',
+ parameters: { name: '用户 Id,用户详情页 URL 可以找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['segmentfault.com/u/:name'],
+ },
+ name: '用户',
+ maintainers: ['leyuuu', 'Fatpandac'],
+ handler,
+};
+
+async function handler(ctx) {
const name = ctx.req.param('name');
const apiURL = `${host}/gateway/homepage/${name}/timeline?size=20&offset=`;
@@ -16,9 +38,9 @@ export default async (ctx) => {
const items = await Promise.all(list.map((item) => parseItems(acwScV2Cookie, item, cache.tryGet)));
- ctx.set('data', {
+ return {
title: `segmentfault - ${author}`,
link: `${host}/u/${name}`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sehuatang/index.ts b/lib/routes/sehuatang/index.ts
index ee0a265e0..6a480c9bd 100644
--- a/lib/routes/sehuatang/index.ts
+++ b/lib/routes/sehuatang/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -38,7 +39,14 @@ const forumIdMaps = {
hrxazp: '98', // 华人性爱自拍
};
-export default async (ctx) => {
+export const route: Route = {
+ path: ['/bt/:subforumid?', '/picture/:subforumid', '/:subforumid?/:type?', '/:subforumid?', ''],
+ name: 'Unknown',
+ maintainers: ['qiwihui', 'junfengP', 'nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const subformName = ctx.req.param('subforumid') ?? 'gqzwzm';
const subformId = subformName in forumIdMaps ? forumIdMaps[subformName] : subformName;
const type = ctx.req.param('type');
@@ -122,9 +130,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `色花堂 - ${$('#pt > div:nth-child(1) > a:last-child').text()}`,
link,
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/sehuatang/user.ts b/lib/routes/sehuatang/user.ts
index a23e92262..3e06b46e5 100644
--- a/lib/routes/sehuatang/user.ts
+++ b/lib/routes/sehuatang/user.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
// 导入必要的模组
import got from '@/utils/got'; // 自订的 got
@@ -7,7 +8,25 @@ import { config } from '@/config';
const baseUrl = 'https://sehuatang.org/';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/user/:uid',
+ categories: ['picture'],
+ example: '/sehuatang/user/411096',
+ parameters: { uid: '用户 uid, 可在用户主页 URL 中找到' },
+ features: {
+ requireConfig: true,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '作者文章',
+ maintainers: ['JamYiz'],
+ handler,
+};
+
+async function handler(ctx) {
if (!config.sehuatang.cookie) {
throw new Error('Sehuatang RSS is disabled due to the lack of relevant config');
}
@@ -104,10 +123,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
// 在此处输出您的 RSS
title: `${$('.mt').text()}的帖子-色花堂`,
link,
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/sensortower/blog.ts b/lib/routes/sensortower/blog.ts
index 66b2989ed..baf3777ca 100644
--- a/lib/routes/sensortower/blog.ts
+++ b/lib/routes/sensortower/blog.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -8,7 +9,29 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/blog/:language?',
+ categories: ['traditional-media'],
+ example: '/sensortower/blog',
+ parameters: { language: 'Language, see below, English by default' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['sensortower.com/blog', 'sensortower.com/zh-CN/blog', 'sensortower.com/ja/blog', 'sensortower.com/ko/blog', 'sensortower.com/'],
+ target: '/blog',
+ },
+ name: 'Blog',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const language = ctx.req.param('language') ?? '';
const rootUrl = 'https://sensortower.com';
@@ -64,10 +87,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: 'Sensor Tower - Blog',
link: currentUrl,
item: items,
language: language || 'en-US',
- });
-};
+ };
+}
diff --git a/lib/routes/setn/index.ts b/lib/routes/setn/index.ts
index 3c61d0cad..c38973110 100644
--- a/lib/routes/setn/index.ts
+++ b/lib/routes/setn/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -42,7 +43,29 @@ const getCurrentUrl = (category) => {
return `${rootUrl}/viewall`;
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:category?',
+ categories: ['bbs'],
+ example: '/setn',
+ parameters: { category: '分类,见下表,默认为即時' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['setn.com/ViewAll.aspx', 'setn.com/'],
+ target: '',
+ },
+ name: '新聞',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? '即時';
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 42;
@@ -93,9 +116,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `三立新聞網 - ${category}`,
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/seu/cse/index.ts b/lib/routes/seu/cse/index.ts
index 2e932d8a9..5d351c24f 100644
--- a/lib/routes/seu/cse/index.ts
+++ b/lib/routes/seu/cse/index.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/cse/:type?',
+ categories: ['forecast'],
+ example: '/seu/cse/xyxw',
+ parameters: { type: '分类名,默认为 `xyxw`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['cse.seu.edu.cn/:type/list.htm', 'cse.seu.edu.cn/'],
+ },
+ name: '计算机技术与工程学院',
+ maintainers: ['LogicJake'],
+ handler,
+};
+
+async function handler(ctx) {
const host = 'https://cse.seu.edu.cn';
const map = {
@@ -55,10 +77,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
link,
title: `${$('meta[name=keywords]').attr('content')}${$('meta[name=description]').attr('content')} -- ${$('head title').text()}`,
description: '东南大学计算机技术与工程学院RSS',
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/seu/radio/academic.ts b/lib/routes/seu/radio/academic.ts
index c4f6d20c5..728ad379c 100644
--- a/lib/routes/seu/radio/academic.ts
+++ b/lib/routes/seu/radio/academic.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/radio/academic',
+ categories: ['forecast'],
+ example: '/seu/radio/academic',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['radio.seu.edu.cn/_s29/15986/list.psp', 'radio.seu.edu.cn/'],
+ },
+ name: '信息科学与工程学院学术活动',
+ maintainers: ['HenryQW'],
+ handler,
+};
+
+async function handler() {
const host = 'https://radio.seu.edu.cn';
const link = new URL('_s29/15986/list.psp', host).href;
const response = await got(link);
@@ -36,9 +58,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: '东南大学信息科学与工程学院 -- 学术活动',
link,
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/seu/yjs.ts b/lib/routes/seu/yjs.ts
index 383cae806..60acf8131 100644
--- a/lib/routes/seu/yjs.ts
+++ b/lib/routes/seu/yjs.ts
@@ -1,10 +1,32 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
const url = 'https://seugs.seu.edu.cn/26671/list.htm';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/yjs',
+ categories: ['forecast'],
+ example: '/seu/yjs',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['seugs.seu.edu.cn/26671/list.htm', 'seugs.seu.edu.cn/'],
+ },
+ name: '研究生院全部公告',
+ maintainers: ['Denkiyohou'],
+ handler,
+};
+
+async function handler() {
const response = await got(url);
const $ = load(response.data);
const list = $('.news')
@@ -30,9 +52,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: '东南大学研究生公告',
link: url,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/seu/yzb/index.ts b/lib/routes/seu/yzb/index.ts
index 8957cc10e..ac2897f52 100644
--- a/lib/routes/seu/yzb/index.ts
+++ b/lib/routes/seu/yzb/index.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/yzb/:type',
+ categories: ['forecast'],
+ example: '/seu/yzb/6676',
+ parameters: { type: '分类名,见下表' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['yzb.seu.edu.cn/:type/list.htm'],
+ },
+ name: '研究生招生网通知公告',
+ maintainers: ['fuzy112'],
+ handler,
+};
+
+async function handler(ctx) {
const host = 'https://yzb.seu.edu.cn';
const map = {
@@ -44,9 +66,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
link: url,
title: `东南大学研究生招生网 -- ${$('head title').text()}`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sfacg/novel-chapter.ts b/lib/routes/sfacg/novel-chapter.ts
index c7eaa94f2..8e25393d0 100644
--- a/lib/routes/sfacg/novel-chapter.ts
+++ b/lib/routes/sfacg/novel-chapter.ts
@@ -1,10 +1,32 @@
+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';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/novel/chapter/:id',
+ categories: ['government'],
+ example: '/sfacg/novel/chapter/672431',
+ parameters: { id: '小说 id, 可在对应小说页 URL 中找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['book.sfacg.com/Novel/:id/*'],
+ },
+ name: '章节',
+ maintainers: ['keocheung'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const limit = Number.parseInt(ctx.req.query('limit')) || 20;
@@ -42,11 +64,11 @@ export default async (ctx) => {
const { data: intro } = await got(`${baseUrl}/Novel/${id}/`);
const $i = load(intro);
- ctx.set('data', {
+ return {
title: `SF轻小说 ${$('h1.story-title').text()}`,
link: `${baseUrl}/Novel/${id}`,
description: $i('p.introduce').text(),
image: $i('div.summary-pic img').attr('src').replace('http://', 'https://'),
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/shcstheatre/programs.ts b/lib/routes/shcstheatre/programs.ts
index 6cadad842..8bd264798 100644
--- a/lib/routes/shcstheatre/programs.ts
+++ b/lib/routes/shcstheatre/programs.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -9,7 +10,28 @@ import timezone from '@/utils/timezone';
import { load } from 'cheerio';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/programs',
+ categories: ['game'],
+ example: '/shcstheatre/programs',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.shcstheatre.com/Program/programList.aspx'],
+ },
+ name: '节目列表',
+ maintainers: ['fuzy112'],
+ handler,
+};
+
+async function handler() {
const url = 'https://www.shcstheatre.com/Program/programList.aspx';
const res = await got.get(url);
const $ = load(res.data);
@@ -34,10 +56,10 @@ export default async (ctx) => {
);
const image = $('.menu-logo img').attr('src');
- ctx.set('data', {
+ return {
title: '上海文化广场 - 节目列表',
link: url,
image,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/shiep/index.ts b/lib/routes/shiep/index.ts
index 2be412f87..7227c7d21 100644
--- a/lib/routes/shiep/index.ts
+++ b/lib/routes/shiep/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -10,7 +11,25 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import { config } from './config';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:type/:id?',
+ categories: ['forecast'],
+ example: '/shiep/news/notice',
+ parameters: { type: '类型名称,见下表', id: '页面 ID,默认为通知公告或学院公告所对应的 ID' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '新闻网与学院通知',
+ maintainers: ['gumibea', 'TeamSUEP'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type');
if (!Object.keys(config).includes(type)) {
@@ -68,9 +87,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `上海电力大学-${title}`,
link,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/shisu/news.ts b/lib/routes/shisu/news.ts
index dd0c9ca54..0a4dbf9cd 100644
--- a/lib/routes/shisu/news.ts
+++ b/lib/routes/shisu/news.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -7,7 +8,28 @@ import timezone from '@/utils/timezone';
const url = 'https://news.shisu.edu.cn';
const banner = 'https://news.shisu.edu.cn/news/index/39adf3d9ae414bc39c6d3b9316ae531f.png';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/:category',
+ categories: ['forecast'],
+ example: '/shisu/news/notice',
+ parameters: { category: '新闻的分类可根据自己的需要选择,首页为全部新闻' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['news.shisu.edu.cn/:category/index.html'],
+ },
+ name: '上外新闻',
+ maintainers: ['Duuckjing'],
+ handler,
+};
+
+async function handler(ctx) {
const { section = 'news' } = ctx.req.param();
const { data: r } = await got(`${url}/${section}/index.html`);
const $ = load(r);
@@ -56,10 +78,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `上外新闻|SISU TODAY -${section.charAt(0).toUpperCase() + section.slice(1)}`,
image: 'https://bkimg.cdn.bcebos.com/pic/8d5494eef01f3a296b70affa9825bc315c607c4d?x-bce-process=image/resize,m_lfit,w_536,limit_1/quality,Q_70',
link: `${url}/${section}/index.html`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/shmeea/index.ts b/lib/routes/shmeea/index.ts
index cf84b1e27..ff423371e 100644
--- a/lib/routes/shmeea/index.ts
+++ b/lib/routes/shmeea/index.ts
@@ -1,10 +1,29 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:id?',
+ categories: ['journal'],
+ example: '/shmeea/08000',
+ parameters: { id: '页面 ID,可在 URL 中找到,默认为消息速递' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '消息',
+ maintainers: ['jialinghui', 'Misaka13514'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id') ?? '08000';
const baseURL = 'https://www.shmeea.edu.cn';
const link = `${baseURL}/page/${id}/index.html`;
@@ -46,9 +65,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title,
link,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/shmeea/self-study.ts b/lib/routes/shmeea/self-study.ts
index ba9e164e5..da8e9827f 100644
--- a/lib/routes/shmeea/self-study.ts
+++ b/lib/routes/shmeea/self-study.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -38,7 +39,28 @@ function load_detail(list, cache) {
);
}
-export default async (ctx) => {
+export const route: Route = {
+ path: '/self-study',
+ categories: ['journal'],
+ example: '/shmeea/self-study',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.shmeea.edu.cn/page/04000/index.html', 'www.shmeea.edu.cn/'],
+ },
+ name: '自学考试通知公告',
+ maintainers: ['h2ws'],
+ handler,
+};
+
+async function handler() {
const response = await got({
method: 'get',
url: 'http://www.shmeea.edu.cn/page/04000/index.html',
@@ -54,9 +76,9 @@ export default async (ctx) => {
const detail = await load_detail(list, cache);
- ctx.set('data', {
+ return {
title: '上海自学考试 - 通知公告',
link: 'http://www.shmeea.edu.cn/page/04000/index.html',
item: detail,
- });
-};
+ };
+}
diff --git a/lib/routes/shmtu/jwc.ts b/lib/routes/shmtu/jwc.ts
index 742a89106..2ce53fb58 100644
--- a/lib/routes/shmtu/jwc.ts
+++ b/lib/routes/shmtu/jwc.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -22,7 +23,28 @@ const ProcessFeed = (list, caches) =>
)
);
-export default async (ctx) => {
+export const route: Route = {
+ path: '/jwc/:type',
+ categories: ['forecast'],
+ example: '/shmtu/jwc/jwgg',
+ parameters: { type: '类型名称' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['jwc.shmtu.edu.cn/:type'],
+ },
+ name: '教务信息',
+ maintainers: ['imbytecat', 'simonsmh'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type');
const info = type === 'jwgg' ? '教务公告' : '教务新闻';
@@ -49,10 +71,10 @@ export default async (ctx) => {
const result = await ProcessFeed(list, cache);
- ctx.set('data', {
+ return {
title: `上海海事大学 ${info}`,
link: `${host}/${type}`,
description: '上海海事大学 教务信息',
item: result,
- });
-};
+ };
+}
diff --git a/lib/routes/shmtu/portal.ts b/lib/routes/shmtu/portal.ts
index 32a98e4f1..54d1a8729 100644
--- a/lib/routes/shmtu/portal.ts
+++ b/lib/routes/shmtu/portal.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -41,7 +42,28 @@ const processFeed = (list, caches) =>
)
);
-export default async (ctx) => {
+export const route: Route = {
+ path: '/portal/:type',
+ categories: ['forecast'],
+ example: '/shmtu/portal/bmtzgg',
+ parameters: { type: '类型名称' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['portal.shmtu.edu.cn/:type'],
+ },
+ name: '数字平台',
+ maintainers: ['imbytecat'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type');
let info;
switch (type) {
@@ -76,10 +98,10 @@ export default async (ctx) => {
const result = await processFeed(list, cache);
- ctx.set('data', {
+ return {
title: `上海海事大学 ${info}`,
link: 'https://portal.shmtu.edu.cn/bumentongzhigonggao',
description: '上海海事大学 数字平台',
item: result,
- });
-};
+ };
+}
diff --git a/lib/routes/shmtu/www.ts b/lib/routes/shmtu/www.ts
index 18e6159ff..48ddf6081 100644
--- a/lib/routes/shmtu/www.ts
+++ b/lib/routes/shmtu/www.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -22,7 +23,28 @@ const ProcessFeed = (list, caches) =>
)
);
-export default async (ctx) => {
+export const route: Route = {
+ path: '/www/:type',
+ categories: ['forecast'],
+ example: '/shmtu/www/events',
+ parameters: { type: '类型名称' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.shmtu.edu.cn/:type'],
+ },
+ name: '官网信息',
+ maintainers: ['imbytecat', 'simonsmh'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type');
const info = type === 'notes' ? '通知公告' : '学术讲座';
@@ -50,10 +72,10 @@ export default async (ctx) => {
const result = await ProcessFeed(list, cache);
- ctx.set('data', {
+ return {
title: `上海海事大学 ${info}`,
link: `${host}/${type}`,
description: '上海海事大学 官网信息',
item: result,
- });
-};
+ };
+}
diff --git a/lib/routes/shoac/recent-show.ts b/lib/routes/shoac/recent-show.ts
index 43a3db354..8da96ae31 100644
--- a/lib/routes/shoac/recent-show.ts
+++ b/lib/routes/shoac/recent-show.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -7,7 +8,28 @@ import * as path from 'node:path';
import { art } from '@/utils/render';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/recent-show',
+ categories: ['game'],
+ example: '/shoac/recent-show',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['shoac.com.cn/'],
+ },
+ name: '演出月历',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler() {
const baseUrl = 'https://www.shoac.com.cn';
const headers = {
@@ -68,9 +90,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: '演出月历 - 上海东方艺术中心管理有限公司',
link: baseUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/shopback/store.ts b/lib/routes/shopback/store.ts
index d0853b60c..6f0c06467 100644
--- a/lib/routes/shopback/store.ts
+++ b/lib/routes/shopback/store.ts
@@ -1,7 +1,29 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:store',
+ categories: ['game'],
+ example: '/shopback/shopee-mart',
+ parameters: { store: 'Store, can be found in URL' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['shopback.com.tw/:category', 'shopback.com.tw/'],
+ },
+ name: 'Store',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const store = ctx.req.param('store');
const rootUrl = 'https://www.shopback.com.tw';
@@ -29,9 +51,9 @@ export default async (ctx) => {
})
.get();
- ctx.set('data', {
+ return {
title: `${$('h1').text()} - ShopBack`,
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/shoppingdesign/posts.ts b/lib/routes/shoppingdesign/posts.ts
index 5fa67c254..a75de03c2 100644
--- a/lib/routes/shoppingdesign/posts.ts
+++ b/lib/routes/shoppingdesign/posts.ts
@@ -1,10 +1,32 @@
+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 asyncPool from 'tiny-async-pool';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/posts',
+ categories: ['live'],
+ example: '/shoppingdesign/posts',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.shoppingdesign.com.tw/post'],
+ },
+ name: '文章列表',
+ maintainers: ['miles170'],
+ handler,
+};
+
+async function handler() {
// sn_f parameter is required to prevent redirection
const currentUrl = 'https://www.shoppingdesign.com.tw/post?sn_f=1';
const response = await got(currentUrl);
@@ -35,10 +57,10 @@ export default async (ctx) => {
items.push(data);
}
- ctx.set('data', {
+ return {
title: $('meta[property="og:title"]').attr('content'),
link: currentUrl,
description: $('meta[property="og:description"]').attr('content'),
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/showstart/artist.ts b/lib/routes/showstart/artist.ts
index b8a444614..75cfdf812 100644
--- a/lib/routes/showstart/artist.ts
+++ b/lib/routes/showstart/artist.ts
@@ -1,15 +1,37 @@
+import { Route } from '@/types';
import { TITLE, HOST } from './const';
import { fetchPerformerInfo } from './service';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/artist/:id',
+ categories: ['game'],
+ example: '/showstart/artist/301783',
+ parameters: { id: '音乐人 ID' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.showstart.com/artist/:id'],
+ },
+ name: '音乐人 - 演出更新',
+ maintainers: ['lchtao26'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const artist = await fetchPerformerInfo({
performerId: id,
});
- ctx.set('data', {
+ return {
title: `${TITLE} - ${artist.name}`,
description: artist.content,
link: `${HOST}/artist/${artist.id}`,
item: artist.activityList,
- });
-};
+ };
+}
diff --git a/lib/routes/showstart/brand.ts b/lib/routes/showstart/brand.ts
index f15ab750c..729cabca7 100644
--- a/lib/routes/showstart/brand.ts
+++ b/lib/routes/showstart/brand.ts
@@ -1,15 +1,37 @@
+import { Route } from '@/types';
import { TITLE, HOST } from './const';
import { fetchBrandInfo } from './service';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/brand/:id',
+ categories: ['game'],
+ example: '/showstart/brand/34707',
+ parameters: { id: '厂牌 ID' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.showstart.com/host/:id'],
+ },
+ name: '厂牌 - 演出更新',
+ maintainers: ['lchtao26'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const brand = await fetchBrandInfo({
brandId: id,
});
- ctx.set('data', {
+ return {
title: `${TITLE} - ${brand.name}`,
description: brand.content,
link: `${HOST}/host/${brand.id}`,
item: brand.activityList,
- });
-};
+ };
+}
diff --git a/lib/routes/showstart/event.ts b/lib/routes/showstart/event.ts
index 31b067e29..f5b4cff10 100644
--- a/lib/routes/showstart/event.ts
+++ b/lib/routes/showstart/event.ts
@@ -1,7 +1,26 @@
+import { Route } from '@/types';
import { TITLE, HOST } from './const';
import { fetchActivityList, fetchDictionary } from './service';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/event/:cityCode/:showStyle?',
+ categories: ['game'],
+ example: '/showstart/event/571/3',
+ parameters: { cityCode: '演出城市 (编号)', showStyle: '演出风格 (编号)' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '演出更新',
+ maintainers: ['lchtao26'],
+ handler,
+};
+
+async function handler(ctx) {
const cityCode = Number.parseInt(ctx.req.param('cityCode'));
const showStyle = Number.parseInt(ctx.req.param('showStyle'));
const items = await fetchActivityList({
@@ -10,9 +29,9 @@ export default async (ctx) => {
});
const { cityName, showName } = await fetchDictionary(cityCode, showStyle);
const tags = [cityName, showName].filter(Boolean).join(' - ');
- ctx.set('data', {
+ return {
title: `${TITLE} - ${tags}`,
link: HOST,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/showstart/search.ts b/lib/routes/showstart/search.ts
index 8276762ec..cc21fbe66 100644
--- a/lib/routes/showstart/search.ts
+++ b/lib/routes/showstart/search.ts
@@ -1,51 +1,70 @@
+import { Route } from '@/types';
import { TITLE, HOST } from './const';
import { fetchActivityList, fetchPerformerList, fetchBrandList, fetchCityList, fetchStyleList } from './service';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/search/:type/:keyword?',
+ categories: ['game'],
+ example: '/showstart/search/live',
+ parameters: { type: '类别', keyword: '搜索关键词' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '演出搜索',
+ maintainers: ['lchtao26'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type') || '';
const keyword = ctx.req.param('keyword') || '';
switch (type) {
case 'event':
- ctx.set('data', {
+ return {
title: `${TITLE} - 搜演出 - ${keyword || '全部'}`,
link: HOST,
item: await fetchActivityList({ keyword }),
- });
+ };
break;
case 'artist':
- ctx.set('data', {
+ return {
title: `${TITLE} - 搜艺人 - ${keyword || '全部'}`,
link: HOST,
item: await fetchPerformerList({ searchKeyword: keyword }),
- });
+ };
break;
case 'brand':
- ctx.set('data', {
+ return {
title: `${TITLE} - 搜厂牌 - ${keyword || '全部'}`,
link: HOST,
item: await fetchBrandList({ searchKeyword: keyword }),
- });
+ };
break;
case 'city':
- ctx.set('data', {
+ return {
title: `${TITLE} - 搜城市 - ${keyword || '全部'}`,
link: HOST,
item: await fetchCityList(keyword),
- });
+ };
break;
case 'style':
- ctx.set('data', {
+ return {
title: `${TITLE} - 搜风格 - ${keyword || '全部'}`,
link: HOST,
item: await fetchStyleList(keyword),
- });
+ };
break;
default:
- ctx.set('data', {
+ return {
title: `${TITLE} - 搜演出 - ${type || '全部'}`,
link: HOST,
item: await fetchActivityList({ keyword: type }),
- });
+ };
}
-};
+}
diff --git a/lib/routes/shu/index.ts b/lib/routes/shu/index.ts
index 5f3a337fc..ff6b2b47c 100644
--- a/lib/routes/shu/index.ts
+++ b/lib/routes/shu/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -12,7 +13,29 @@ const alias = new Map([
['important', 'zyxw'], // 重要新闻
]);
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:type?',
+ categories: ['forecast'],
+ example: '/shu/news',
+ parameters: { type: '消息类型,默认为`news`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.shu.edu.cn/:type'],
+ target: '/:type',
+ },
+ name: '官网信息',
+ maintainers: ['lonelyion'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type') || 'news';
const link = `https://www.shu.edu.cn/${alias.get(type) || type}.htm`;
const respond = await got.get(link);
@@ -41,9 +64,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title,
link,
item: all,
- });
-};
+ };
+}
diff --git a/lib/routes/shu/jwb.ts b/lib/routes/shu/jwb.ts
index bb3e7971e..b72766031 100644
--- a/lib/routes/shu/jwb.ts
+++ b/lib/routes/shu/jwb.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -10,7 +11,18 @@ const alias = new Map([
['policy', 'zcwj'], // 政策文件
]);
-export default async (ctx) => {
+export const route: Route = {
+ path: ['/jwc/:type?', '/jwb/:type?'],
+ radar: {
+ source: ['www.shu.edu.cn/:type'],
+ target: '/:type',
+ },
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type') || 'notice';
const link = `https://jwb.shu.edu.cn/index/${alias.get(type) || type}.htm`;
const respond = await got.get(link);
@@ -38,9 +50,9 @@ export default async (ctx) => {
})
)
);
- ctx.set('data', {
+ return {
title,
link,
item: all,
- });
-};
+ };
+}
diff --git a/lib/routes/shuiguopai/index.ts b/lib/routes/shuiguopai/index.ts
index 03e147228..58c4b90ee 100644
--- a/lib/routes/shuiguopai/index.ts
+++ b/lib/routes/shuiguopai/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -9,7 +10,18 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/',
+ radar: {
+ source: ['shuiguopai.com/'],
+ target: '',
+ },
+ name: 'Unknown',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const rootUrl = 'https://sgptv.vip';
const apiRootUrl = 'https://api.cbbee0.com';
const listUrl = `${apiRootUrl}/v1_2/homePage`;
@@ -82,9 +94,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: '水果派',
link: rootUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sicau/dky.ts b/lib/routes/sicau/dky.ts
index deaed62e8..e1b85cd1a 100644
--- a/lib/routes/sicau/dky.ts
+++ b/lib/routes/sicau/dky.ts
@@ -1,10 +1,32 @@
+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';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/dky/:category?',
+ categories: ['forecast'],
+ example: '/sicau/dky/tzgg',
+ parameters: { category: '分类,见下表,默认为通知公告' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['dky.sicau.edu.cn/'],
+ },
+ name: '动物科技学院',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? 'tzgg';
const rootUrl = 'https://dky.sicau.edu.cn';
@@ -45,9 +67,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('title').text(),
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sicau/yan.ts b/lib/routes/sicau/yan.ts
index da96d503a..3fe1c091d 100644
--- a/lib/routes/sicau/yan.ts
+++ b/lib/routes/sicau/yan.ts
@@ -1,10 +1,32 @@
+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';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/yan/:category?',
+ categories: ['forecast'],
+ example: '/sicau/yan/xwgg',
+ parameters: { category: '分类,见下表,默认为新闻公告' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['yan.sicau.edu.cn/'],
+ },
+ name: '研究生院',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? 'xwgg';
const rootUrl = 'https://yan.sicau.edu.cn';
@@ -45,9 +67,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('title').text(),
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sicau/zsjy.ts b/lib/routes/sicau/zsjy.ts
index f2ac1e627..ec2d1a9f9 100644
--- a/lib/routes/sicau/zsjy.ts
+++ b/lib/routes/sicau/zsjy.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/zsjy/:category?',
+ categories: ['forecast'],
+ example: '/sicau/zsjy/bkszs',
+ parameters: { category: '分类,见下表,默认为本科生招生' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['dky.sicau.edu.cn/'],
+ },
+ name: '招生就业',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? 'bkszs';
const rootUrl = 'https://dky.sicau.edu.cn';
@@ -46,9 +68,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('title').text(),
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sigsac/ccs.ts b/lib/routes/sigsac/ccs.ts
index 8bb350dbd..69c947c38 100644
--- a/lib/routes/sigsac/ccs.ts
+++ b/lib/routes/sigsac/ccs.ts
@@ -1,10 +1,32 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
const url = 'https://www.sigsac.org/';
import { parseDate } from '@/utils/parse-date';
// https://www.sigsac.org/ccs/CCS2022/program/accepted-papers.html
-export default async (ctx) => {
+export const route: Route = {
+ path: '/ccs',
+ categories: ['finance'],
+ example: '/sigsac/ccs',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['sigsac.org/ccs.html', 'sigsac.org/'],
+ },
+ name: 'The ACM Conference on Computer and Communications Security',
+ maintainers: [],
+ handler,
+};
+
+async function handler() {
const last = new Date().getFullYear() + 1;
const yearList = Array.from({ length: last - 2020 }, (_, v) => `${url}ccs/CCS${v + 2020}/`);
const yearResponses = await got.all(yearList.map((url) => got(url)));
@@ -47,11 +69,11 @@ export default async (ctx) => {
return paperSection.length ? paperSection : paperTable;
});
- ctx.set('data', {
+ return {
title: 'ACM CCS',
link: url,
description: 'The ACM Conference on Computer and Communications Security (CCS) Accepted Papers',
allowEmpty: true,
item: items.flat(),
- });
-};
+ };
+}
diff --git a/lib/routes/simpleinfo/index.ts b/lib/routes/simpleinfo/index.ts
index 819e30060..720bd4c0a 100644
--- a/lib/routes/simpleinfo/index.ts
+++ b/lib/routes/simpleinfo/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -9,7 +10,29 @@ import * as path from 'node:path';
import { art } from '@/utils/render';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:category?',
+ categories: ['traditional-media'],
+ example: '/simpleinfo',
+ parameters: { category: '分类名' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['blog.simpleinfo.cc/blog/:category'],
+ target: '/:category',
+ },
+ name: '志祺七七',
+ maintainers: ['haukeng'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category');
const rootUrl = 'https://blog.simpleinfo.cc';
const link = `${rootUrl}${category ? (category === 'work' || category === 'talk' ? `/blog/${category}` : `/shasha77?category=${category}`) : '/shasha77'}`;
@@ -45,10 +68,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title,
link,
language: 'zh-tw',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sina/chuangshiji.ts b/lib/routes/sina/chuangshiji.ts
index 330b47c9c..0a988dd4b 100644
--- a/lib/routes/sina/chuangshiji.ts
+++ b/lib/routes/sina/chuangshiji.ts
@@ -1,7 +1,29 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import { getRollNewsList, parseRollNewsList, parseArticle } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/csj',
+ categories: ['traditional-media'],
+ example: '/sina/csj',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['tech.sina.com.cn/chuangshiji', 'tech.sina.com.cn/'],
+ },
+ name: '专栏 - 创事记',
+ maintainers: ['xapool'],
+ handler,
+};
+
+async function handler(ctx) {
const pageid = '402';
const lid = '2559';
const { limit = '50' } = ctx.req.query();
@@ -10,9 +32,9 @@ export default async (ctx) => {
const out = await Promise.all(list.map((item) => parseArticle(item, cache.tryGet)));
- ctx.set('data', {
+ return {
title: '新浪专栏-创事记',
link: 'https://tech.sina.com.cn/chuangshiji',
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/sina/discovery.ts b/lib/routes/sina/discovery.ts
index 0414749cd..a5d15009a 100644
--- a/lib/routes/sina/discovery.ts
+++ b/lib/routes/sina/discovery.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import { getRollNewsList, parseRollNewsList, parseArticle } from './utils';
@@ -13,7 +14,25 @@ const map = new Map([
['kjqy', { title: '科技前沿', id: '1802' }],
]);
-export default async (ctx) => {
+export const route: Route = {
+ path: '/discovery/:type',
+ categories: ['traditional-media'],
+ example: '/sina/discovery/zx',
+ parameters: { type: '订阅分区类型,见下表' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '科技 - 科学探索',
+ maintainers: ['LogicJake'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type');
const lid = map.get(type).id;
const title = map.get(type).title;
@@ -25,9 +44,9 @@ export default async (ctx) => {
const out = await Promise.all(list.map((item) => parseArticle(item, cache.tryGet)));
- ctx.set('data', {
+ return {
title: `${title}-新浪科技科学探索`,
link,
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/sina/finance/china.ts b/lib/routes/sina/finance/china.ts
index 81bdc06a2..bb03c3411 100644
--- a/lib/routes/sina/finance/china.ts
+++ b/lib/routes/sina/finance/china.ts
@@ -1,7 +1,30 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import { getRollNewsList, parseRollNewsList, parseArticle } from '../utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/finance/china/:lid?',
+ categories: ['traditional-media'],
+ example: '/sina/finance/china',
+ parameters: { lid: '分区 id,见下表,默认为 `1686`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['finance.sina.com.cn/china', 'finance.sina.com.cn/'],
+ target: '/finance/china',
+ },
+ name: '财经-国內',
+ maintainers: ['yubinbai'],
+ handler,
+};
+
+async function handler(ctx) {
const map = {
1686: '国内滚动',
1687: '宏观经济',
@@ -18,9 +41,9 @@ export default async (ctx) => {
const out = await Promise.all(list.map((item) => parseArticle(item, cache.tryGet)));
- ctx.set('data', {
+ return {
title: `新浪财经-${map[lid]}`,
link: 'http://finance.sina.com.cn/china/',
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/sina/finance/stock/usstock.ts b/lib/routes/sina/finance/stock/usstock.ts
index 3f7c589d8..e585dc316 100644
--- a/lib/routes/sina/finance/stock/usstock.ts
+++ b/lib/routes/sina/finance/stock/usstock.ts
@@ -1,9 +1,32 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { parseArticle } from '../../utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/finance/stock/usstock/:cids?',
+ categories: ['traditional-media'],
+ example: '/sina/finance/stock/usstock',
+ parameters: { cids: '分区 id,见下表,默认为 `57045`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['finance.sina.com.cn/stock/usstock', 'finance.sina.com.cn/'],
+ target: '/finance/stock/usstock',
+ },
+ name: '美股',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const { cids = '57045' } = ctx.req.param();
const { limit = '50' } = ctx.req.query();
const { data: response } = await got('https://interface.sina.cn/pc_api/public_news_data.d.json', {
@@ -30,9 +53,9 @@ export default async (ctx) => {
const items = await Promise.all(list.map((item) => parseArticle(item, cache.tryGet)));
- ctx.set('data', {
+ return {
title: '美股|美股行情|美股新闻 - 新浪财经',
link: 'https://finance.sina.com.cn/stock/usstock/',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sina/rollnews.ts b/lib/routes/sina/rollnews.ts
index 07d5cf1e0..10d974b9f 100644
--- a/lib/routes/sina/rollnews.ts
+++ b/lib/routes/sina/rollnews.ts
@@ -1,7 +1,26 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import { getRollNewsList, parseRollNewsList, parseArticle } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/rollnews/:lid?',
+ categories: ['traditional-media'],
+ example: '/sina/rollnews',
+ parameters: { lid: '分区 id,可在 URL 中找到,默认为 `2509`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '滚动新闻',
+ maintainers: ['xyqfer'],
+ handler,
+};
+
+async function handler(ctx) {
const map = {
2509: '全部',
2510: '国内',
@@ -24,9 +43,9 @@ export default async (ctx) => {
const out = await Promise.all(list.map((item) => parseArticle(item, cache.tryGet)));
- ctx.set('data', {
+ return {
title: `新浪${map[lid]}滚动新闻`,
link: `https://news.sina.com.cn/roll/#pageid=${pageid}&lid=${lid}&k=&num=${limit}&page=1`,
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/sina/sports.ts b/lib/routes/sina/sports.ts
index 6a518bb2e..637c8418b 100644
--- a/lib/routes/sina/sports.ts
+++ b/lib/routes/sina/sports.ts
@@ -1,9 +1,17 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseArticle } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/sports/:type?',
+ name: 'Unknown',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type');
let currentUrl = `https://sports.sina.com.cn/others/${type}.shtml`;
@@ -35,10 +43,10 @@ export default async (ctx) => {
const items = await Promise.all(list.map((item) => parseArticle(item, cache.tryGet)));
- ctx.set('data', {
+ return {
title: `${$('title').text().split('_')[0]} - 新浪体育`,
description: $('meta[name="description"]').attr('content'),
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sinchew/index.ts b/lib/routes/sinchew/index.ts
index 6d85f52d2..83eac7470 100644
--- a/lib/routes/sinchew/index.ts
+++ b/lib/routes/sinchew/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -10,7 +11,18 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '*',
+ radar: {
+ source: ['sinchew.com.my/'],
+ target: '',
+ },
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const rootUrl = 'https://www.sinchew.com.my';
const currentUrl = `${rootUrl}${getSubPath(ctx) === '/' ? '' : getSubPath(ctx)}`;
@@ -65,9 +77,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('title').text(),
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sis001/forum.ts b/lib/routes/sis001/forum.ts
index 7f2b06aeb..033212e58 100644
--- a/lib/routes/sis001/forum.ts
+++ b/lib/routes/sis001/forum.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -5,7 +6,25 @@ import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
const baseUrl = 'https://www.sis001.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/forum/:id?',
+ categories: ['blog'],
+ example: '/sis001/forum/322',
+ parameters: { id: '子版块 ID,可在子论坛 URL 找到,默认为 `Funny Jokes | 短篇笑话区`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '子版块',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const { id = 76 } = ctx.req.param();
const url = `${baseUrl}/forum/forum-${id}-1.html`;
@@ -54,10 +73,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('head title').text(),
description: $('meta[name=description]').attr('content'),
link: url,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sjtu/gs.ts b/lib/routes/sjtu/gs.ts
index d0cbee929..28d38b272 100644
--- a/lib/routes/sjtu/gs.ts
+++ b/lib/routes/sjtu/gs.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -5,7 +6,29 @@ import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
import { fetchArticle } from '@/utils/wechat-mp';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/gs/:type/:num?',
+ categories: ['forecast'],
+ example: '/sjtu/gs/enroll/59',
+ parameters: { type: '类别', num: '细分类别, 仅对`type`为`enroll`或`exchange`有效' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['gs.sjtu.edu.cn/announcement/:type'],
+ target: '/gs/:type',
+ },
+ name: '研究生通知公告',
+ maintainers: ['dzx-dzx'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type');
const num = ctx.req.param('num') ?? '';
@@ -53,9 +76,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `${num === '' ? '' : `${$('.category-nav-block .active').text().trim()} - `}${$('div.inner-banner-text .title').text().trim()} - ${$('title').text()}`,
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sjtu/jwc.ts b/lib/routes/sjtu/jwc.ts
index 0c764f442..64eae8134 100644
--- a/lib/routes/sjtu/jwc.ts
+++ b/lib/routes/sjtu/jwc.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -26,7 +27,25 @@ async function getFullArticle(link) {
return content.html() + ($('.Newslist2').length ? $('.Newslist2').html() : '');
}
-export default async (ctx) => {
+export const route: Route = {
+ path: '/jwc/:type?',
+ categories: ['forecast'],
+ example: '/sjtu/jwc',
+ parameters: { type: '默认为 notice' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '教务处通知公告',
+ maintainers: ['SeanChao'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type') ?? 'notice';
const config = {
all: {
@@ -109,9 +128,9 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: '上海交通大学教务处 ' + config[type].section,
link: sectionLink,
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/sjtu/seiee/utils.ts b/lib/routes/sjtu/seiee/utils.ts
index 75ce02869..2ba1d9fd9 100644
--- a/lib/routes/sjtu/seiee/utils.ts
+++ b/lib/routes/sjtu/seiee/utils.ts
@@ -32,10 +32,10 @@ export default function (meta, extract) {
})
);
- ctx.set('data', {
+ return {
title,
link,
item: out,
- });
+ };
};
}
diff --git a/lib/routes/sjtu/tongqu/activity.ts b/lib/routes/sjtu/tongqu/activity.ts
index 7a76aac1a..1f42fb794 100644
--- a/lib/routes/sjtu/tongqu/activity.ts
+++ b/lib/routes/sjtu/tongqu/activity.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -7,7 +8,25 @@ import * as path from 'node:path';
const urlRoot = 'https://tongqu.sjtu.edu.cn';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/tongqu/:type?',
+ categories: ['forecast'],
+ example: '/sjtu/tongqu/lecture',
+ parameters: { type: '类型,默认为全部' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '同去网最新活动',
+ maintainers: ['SeanChao'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type') || 'all';
const config = {
all: 0,
@@ -39,9 +58,9 @@ export default async (ctx) => {
description: art(path.join(__dirname, '../templates/activity.art'), { e }),
}));
- ctx.set('data', {
+ return {
title: '同去网活动',
link,
item: feeds,
- });
-};
+ };
+}
diff --git a/lib/routes/sjtu/yzb/zkxx.ts b/lib/routes/sjtu/yzb/zkxx.ts
index b17bea1e6..cae40072b 100644
--- a/lib/routes/sjtu/yzb/zkxx.ts
+++ b/lib/routes/sjtu/yzb/zkxx.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
@@ -5,7 +6,25 @@ import { parseDate } from '@/utils/parse-date';
const baseTitle = '上海交通大学研究生招生网招考信息';
const baseUrl = 'https://yzb.sjtu.edu.cn/index/zkxx/';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/yzb/zkxx/:type',
+ categories: ['forecast'],
+ example: '/sjtu/yzb/zkxx/sszs',
+ parameters: { type: '无默认选项' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '研究生招生网招考信息',
+ maintainers: ['stdrc'],
+ handler,
+};
+
+async function handler(ctx) {
const pageUrl = `${baseUrl}${ctx.req.param('type')}.htm`;
const response = await got({
@@ -18,7 +37,7 @@ export default async (ctx) => {
const $ = load(response.data);
- ctx.set('data', {
+ return {
link: pageUrl,
title: `${baseTitle} -- ${$('title').text().split('-')[0]}`,
item: $('li[id^="line"] a')
@@ -28,5 +47,5 @@ export default async (ctx) => {
title: elem.attribs.title,
pubDate: parseDate($(elem.next).text().trim()),
})),
- });
-};
+ };
+}
diff --git a/lib/routes/skysports/news.ts b/lib/routes/skysports/news.ts
index 32a85f360..b8ebc0447 100644
--- a/lib/routes/skysports/news.ts
+++ b/lib/routes/skysports/news.ts
@@ -1,9 +1,28 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/:team',
+ categories: ['traditional-media'],
+ example: '/skysports/news/ac-milan',
+ parameters: { team: 'Team id, can be found in URL to the team page' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'News',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const team = ctx.req.param('team');
const rootUrl = 'https://www.skysports.com';
@@ -48,9 +67,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('title').text(),
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/slowmist/slowmist.ts b/lib/routes/slowmist/slowmist.ts
index 4683b7804..06435650e 100644
--- a/lib/routes/slowmist/slowmist.ts
+++ b/lib/routes/slowmist/slowmist.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
const baseUrl = 'https://www.slowmist.com';
import { finishArticleItem } from '@/utils/wechat-mp';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:type?',
+ categories: ['traditional-media'],
+ example: '/slowmist/research',
+ parameters: { type: '分类,见下表,默认为公司新闻' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['slowmist.com/zh/news.html'],
+ },
+ name: '动态',
+ maintainers: ['AtlasQuan'],
+ handler,
+};
+
+async function handler(ctx) {
let type = ctx.req.param('type');
let title = '慢雾科技 - ';
@@ -41,9 +63,9 @@ export default async (ctx) => {
items = await Promise.all(items.map((item) => finishArticleItem(item)));
- ctx.set('data', {
+ return {
title,
link: url,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/smashingmagazine/category.ts b/lib/routes/smashingmagazine/category.ts
index e8d266932..1cdd0a0b2 100644
--- a/lib/routes/smashingmagazine/category.ts
+++ b/lib/routes/smashingmagazine/category.ts
@@ -1,9 +1,32 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:category?',
+ categories: ['design'],
+ example: '/smashingmagazine/react',
+ parameters: { category: 'Find in URL or Table below' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['smashingmagazine.com/category/:category'],
+ target: '/:category',
+ },
+ name: 'Category',
+ maintainers: ['Rjnishant530'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category');
const baseUrl = 'https://www.smashingmagazine.com';
const route = category ? `/category/${category}` : '/articles';
@@ -52,7 +75,7 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: 'Smashing Magazine Articles',
link: `${baseUrl}${route}`,
item: items,
@@ -60,5 +83,5 @@ export default async (ctx) => {
logo: 'https://www.smashingmagazine.com/images/favicon/apple-touch-icon.png',
icon: 'https://www.smashingmagazine.com/images/favicon/favicon.svg',
language: 'en-us',
- });
-};
+ };
+}
diff --git a/lib/routes/smzdm/article.ts b/lib/routes/smzdm/article.ts
index 21b48710d..a01520527 100644
--- a/lib/routes/smzdm/article.ts
+++ b/lib/routes/smzdm/article.ts
@@ -1,10 +1,32 @@
+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';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/article/:uid',
+ categories: ['game'],
+ example: '/smzdm/article/6902738986',
+ parameters: { uid: '用户id,网址上直接可以看到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['zhiyou.smzdm.com/member/:uid/article'],
+ },
+ name: '用户文章',
+ maintainers: ['xfangbao'],
+ handler,
+};
+
+async function handler(ctx) {
const link = `https://zhiyou.smzdm.com/member/${ctx.req.param('uid')}/article/`;
const response = await got(link);
@@ -36,9 +58,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `${title}-什么值得买`,
link,
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/smzdm/baoliao.ts b/lib/routes/smzdm/baoliao.ts
index 3b4a9b0bc..1e2a96232 100644
--- a/lib/routes/smzdm/baoliao.ts
+++ b/lib/routes/smzdm/baoliao.ts
@@ -1,10 +1,32 @@
+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';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/baoliao/:uid',
+ categories: ['game'],
+ example: '/smzdm/baoliao/7367111021',
+ parameters: { uid: '用户id,网址上直接可以看到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['zhiyou.smzdm.com/member/:uid/baoliao'],
+ },
+ name: '用户爆料',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const link = `https://zhiyou.smzdm.com/member/${ctx.req.param('uid')}/baoliao/`;
const response = await got(link);
@@ -36,9 +58,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `${title}的爆料 - 什么值得买`,
link,
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/smzdm/haowen-fenlei.ts b/lib/routes/smzdm/haowen-fenlei.ts
index aafd0467f..7b9f76424 100644
--- a/lib/routes/smzdm/haowen-fenlei.ts
+++ b/lib/routes/smzdm/haowen-fenlei.ts
@@ -1,9 +1,32 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/haowen/fenlei/:name/:sort?',
+ categories: ['game'],
+ example: '/smzdm/haowen/fenlei/shenghuodianqi',
+ parameters: { name: '分类名,可在 URL 中查看', sort: '排序方式,默认为最新' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['post.smzdm.com/fenlei/:name'],
+ target: '/haowen/fenlei/:name',
+ },
+ name: '好文分类',
+ maintainers: ['LogicJake'],
+ handler,
+};
+
+async function handler(ctx) {
const name = ctx.req.param('name');
const sort = ctx.req.param('sort') || '0';
@@ -44,9 +67,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `${title}- 什么值得买好文分类`,
link,
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/smzdm/haowen.ts b/lib/routes/smzdm/haowen.ts
index 48264b0f7..6354017ae 100644
--- a/lib/routes/smzdm/haowen.ts
+++ b/lib/routes/smzdm/haowen.ts
@@ -1,10 +1,29 @@
+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';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/haowen/:day?',
+ categories: ['game'],
+ example: '/smzdm/haowen/1',
+ parameters: { day: '以天为时间跨度,默认为 `all`,其余可以选择 `1`,`7`,`30`,`365`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '好文',
+ maintainers: ['LogicJake'],
+ handler,
+};
+
+async function handler(ctx) {
const day = ctx.req.param('day') ?? 'all';
const link = `https://post.smzdm.com/hot_${day}/`;
@@ -41,9 +60,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `${title}-什么值得买好文`,
link,
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/smzdm/keyword.ts b/lib/routes/smzdm/keyword.ts
index 9664789e0..11d9f3ba4 100644
--- a/lib/routes/smzdm/keyword.ts
+++ b/lib/routes/smzdm/keyword.ts
@@ -1,9 +1,28 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/keyword/:keyword',
+ categories: ['game'],
+ example: '/smzdm/keyword/女装',
+ parameters: { keyword: '你想订阅的关键词' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '关键词',
+ maintainers: ['DIYgod', 'MeanZhang'],
+ handler,
+};
+
+async function handler(ctx) {
const keyword = ctx.req.param('keyword');
const response = await got(`https://search.smzdm.com`, {
@@ -24,7 +43,7 @@ export default async (ctx) => {
const $ = load(data);
const list = $('.feed-row-wide');
- ctx.set('data', {
+ return {
title: `${keyword} - 什么值得买`,
link: `https://search.smzdm.com/?c=home&s=${encodeURIComponent(keyword)}&order=time`,
item:
@@ -38,5 +57,5 @@ export default async (ctx) => {
link: item.find('.feed-block-title a').attr('href'),
};
}),
- });
-};
+ };
+}
diff --git a/lib/routes/smzdm/ranking.ts b/lib/routes/smzdm/ranking.ts
index 665db0fcc..b6559ac13 100644
--- a/lib/routes/smzdm/ranking.ts
+++ b/lib/routes/smzdm/ranking.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import timezone from '@/utils/timezone';
@@ -12,7 +13,25 @@ const getTrueHour = (rank_type, rank_id, hour) => {
}
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/ranking/:rank_type/:rank_id/:hour',
+ categories: ['game'],
+ example: '/smzdm/ranking/pinlei/11/3',
+ parameters: { rank_type: '榜单类型', rank_id: '榜单ID', hour: '时间跨度' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '排行榜',
+ maintainers: ['DIYgod'],
+ handler,
+};
+
+async function handler(ctx) {
const { rank_type, rank_id, hour } = ctx.req.param();
// When the hour is 3, some special rank_id require a special hour num
@@ -42,7 +61,7 @@ export default async (ctx) => {
}
const list = [...list1, ...list2];
- ctx.set('data', {
+ return {
title: `${rank_type}榜-${rank_id}-${hour}小时`,
link: 'https://www.smzdm.com/top/',
allowEmpty: true,
@@ -52,5 +71,5 @@ export default async (ctx) => {
pubDate: timezone(item.article_pubdate, +8),
link: item.article_url,
})),
- });
-};
+ };
+}
diff --git a/lib/routes/snowpeak/us-new-arrivals.ts b/lib/routes/snowpeak/us-new-arrivals.ts
index a3abd0059..15af35153 100644
--- a/lib/routes/snowpeak/us-new-arrivals.ts
+++ b/lib/routes/snowpeak/us-new-arrivals.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -6,7 +7,28 @@ import { art } from '@/utils/render';
import * as path from 'node:path';
import { load } from 'cheerio';
const host = 'https://www.snowpeak.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/us/new-arrivals',
+ categories: ['game'],
+ example: '/snowpeak/us/new-arrivals',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['snowpeak.com/collections/new-arrivals', 'snowpeak.com/'],
+ },
+ name: 'New Arrivals(USA)',
+ maintainers: ['EthanWng97'],
+ handler,
+};
+
+async function handler() {
const url = `${host}/collections/new-arrivals`;
const response = await got({
method: 'get',
@@ -33,7 +55,7 @@ export default async (ctx) => {
return data;
})
.get();
- ctx.set('data', {
+ return {
title: 'Snow Peak - New Arrivals',
link: `${host}/new-arrivals`,
description: 'Snow Peak - New Arrivals',
@@ -44,5 +66,5 @@ export default async (ctx) => {
pubDate: item.pubDate,
link: item.link,
})),
- });
-};
+ };
+}
diff --git a/lib/routes/sobooks/date.ts b/lib/routes/sobooks/date.ts
index 03675b458..2e0aae20b 100644
--- a/lib/routes/sobooks/date.ts
+++ b/lib/routes/sobooks/date.ts
@@ -1,7 +1,30 @@
+import { Route } from '@/types';
import utils from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/date/:date?',
+ categories: ['government'],
+ example: '/sobooks/date/2020-11',
+ parameters: { date: '日期,见例子,默认为当前年月' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['sobooks.net/:category'],
+ target: '/:category',
+ },
+ name: '归档',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const date = ctx.req.param('date') ?? `${new Date().getFullYear()}/${new Date().getMonth()}`;
ctx.set('data', await utils(ctx, `books/date/${date.replace('-', '/')}`));
-};
+}
diff --git a/lib/routes/sobooks/index.ts b/lib/routes/sobooks/index.ts
index bba1f4b1a..1b9536a65 100644
--- a/lib/routes/sobooks/index.ts
+++ b/lib/routes/sobooks/index.ts
@@ -1,7 +1,30 @@
+import { Route } from '@/types';
import utils from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:category?',
+ categories: ['government'],
+ example: '/sobooks',
+ parameters: { category: '分类, 见下表' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['sobooks.net/:category'],
+ target: '/:category',
+ },
+ name: '首页',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? '';
ctx.set('data', await utils(ctx, category));
-};
+}
diff --git a/lib/routes/sobooks/tag.ts b/lib/routes/sobooks/tag.ts
index 1d9b35711..8c1a2af1a 100644
--- a/lib/routes/sobooks/tag.ts
+++ b/lib/routes/sobooks/tag.ts
@@ -1,7 +1,30 @@
+import { Route } from '@/types';
import utils from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/tag/:id?',
+ categories: ['government'],
+ example: '/sobooks/tag/小说',
+ parameters: { id: '标签, 见下表,默认为小说' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['sobooks.net/books/tag/:tag'],
+ target: '/tag/:tag',
+ },
+ name: '标签',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id') ?? '小说';
ctx.set('data', await utils(ctx, `books/tag/${id}`));
-};
+}
diff --git a/lib/routes/sogou/doodles.ts b/lib/routes/sogou/doodles.ts
index a43dbc287..1ee23c790 100644
--- a/lib/routes/sogou/doodles.ts
+++ b/lib/routes/sogou/doodles.ts
@@ -1,6 +1,25 @@
+import { Route } from '@/types';
import got from '@/utils/got';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/doodles',
+ categories: ['other'],
+ example: '/sogou/doodles',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '特色 LOGO',
+ maintainers: ['xyqfer'],
+ handler,
+};
+
+async function handler() {
const response = await got({
method: 'get',
url: 'http://help.sogou.com/logo/doodle_logo_list.html',
@@ -8,7 +27,7 @@ export default async (ctx) => {
const data = response.data.split(/\r\n/).slice(1);
- ctx.set('data', {
+ return {
title: '搜狗特色LOGO',
link: 'http://help.sogou.com/logo/',
item: data.map((item) => {
@@ -22,5 +41,5 @@ export default async (ctx) => {
guid: item[4],
};
}),
- });
-};
+ };
+}
diff --git a/lib/routes/sogou/search.ts b/lib/routes/sogou/search.ts
index 7e80b3b57..98de3fca0 100644
--- a/lib/routes/sogou/search.ts
+++ b/lib/routes/sogou/search.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -11,7 +12,25 @@ import { config } from '@/config';
const renderDescription = (description, images) => art(path.join(__dirname, './templates/description.art'), { description, images });
-export default async (ctx) => {
+export const route: Route = {
+ path: '/search/:keyword',
+ categories: ['other'],
+ example: '/sogou/search/rss',
+ parameters: { keyword: '搜索关键词' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '搜索',
+ maintainers: ['CaoMeiYouRen'],
+ handler,
+};
+
+async function handler(ctx) {
const keyword = ctx.req.param('keyword');
const url = `https://www.sogou.com/web?query=${encodeURIComponent(keyword)}`;
const key = `sogou-search:${url}`;
@@ -49,10 +68,10 @@ export default async (ctx) => {
false
);
- ctx.set('data', {
+ return {
title: `${keyword} - 搜狗搜索`,
description: `${keyword} - 搜狗搜索`,
link: url,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sohu/mp.ts b/lib/routes/sohu/mp.ts
index 897d581f1..ef1faaddd 100644
--- a/lib/routes/sohu/mp.ts
+++ b/lib/routes/sohu/mp.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -8,7 +9,25 @@ import { parseDate } from '@/utils/parse-date';
import * as path from 'node:path';
import { art } from '@/utils/render';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/mp/:id',
+ categories: ['traditional-media'],
+ example: '/sohu/mp/119097',
+ parameters: { id: '搜狐号 ID' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '更新',
+ maintainers: ['HenryQW'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const authorArticleAPI = `https://v2.sohu.com/author-page-api/author-articles/pc/${id}`;
@@ -61,9 +80,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `搜狐号 - ${author}`,
link,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/solidot/main.ts b/lib/routes/solidot/main.ts
index b5defdbb6..f8e9f5e28 100644
--- a/lib/routes/solidot/main.ts
+++ b/lib/routes/solidot/main.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
// Warning: The author still knows nothing about javascript!
@@ -9,7 +10,25 @@ import { load } from 'cheerio'; // html parser
import get_article from './_article';
import { isValidHost } from '@/utils/valid-host';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:type?',
+ categories: ['bbs'],
+ example: '/solidot/linux',
+ parameters: { type: '消息类型。默认为 www. 在网站上方选择后复制子域名即可' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '最新消息',
+ maintainers: ['sgqy', 'hang333', 'TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type') ?? 'www';
if (!isValidHost(type)) {
throw new Error('Invalid type');
@@ -34,9 +53,9 @@ export default async (ctx) => {
const msg_list = await Promise.all(urls.map((u) => cache.tryGet(u, () => get_article(u))));
// feed the data
- ctx.set('data', {
+ return {
title: '奇客的资讯,重要的东西',
link: base_url,
item: msg_list,
- });
-};
+ };
+}
diff --git a/lib/routes/sony/downloads.ts b/lib/routes/sony/downloads.ts
index 8d05d9f96..5135cd1c1 100644
--- a/lib/routes/sony/downloads.ts
+++ b/lib/routes/sony/downloads.ts
@@ -1,7 +1,29 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
const host = 'https://www.sony.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/downloads/:productType/:productId',
+ categories: ['university'],
+ example: '/sony/downloads/product/nw-wm1am2',
+ parameters: { productType: 'product type', productId: 'product id' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['sony.com/electronics/support/:productType/:productId/downloads'],
+ },
+ name: 'Software Downloads',
+ maintainers: ['EthanWng97'],
+ handler,
+};
+
+async function handler(ctx) {
const { productType, productId } = ctx.req.param();
const url = `${host}/electronics/support/${productType}/${productId}/downloads`;
const response = await got({
@@ -34,7 +56,7 @@ export default async (ctx) => {
}
return data;
});
- ctx.set('data', {
+ return {
title: `Sony - ${productId.toUpperCase()}`,
link: url,
description: `Sony - ${productId.toUpperCase()}`,
@@ -44,5 +66,5 @@ export default async (ctx) => {
link: item.url,
pubDate: item.pubDate,
})),
- });
-};
+ };
+}
diff --git a/lib/routes/soundofhope/channel.ts b/lib/routes/soundofhope/channel.ts
index c8033fc60..8b6e6cf21 100644
--- a/lib/routes/soundofhope/channel.ts
+++ b/lib/routes/soundofhope/channel.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -6,7 +7,28 @@ import timezone from '@/utils/timezone';
const host = 'https://www.soundofhope.org';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:channel/:id',
+ categories: ['bbs'],
+ example: '/soundofhope/term/203',
+ parameters: { channel: '频道', id: '子频道 ID' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['soundofhope.org/:channel/:id'],
+ },
+ name: '频道',
+ maintainers: ['Fatpandac'],
+ handler,
+};
+
+async function handler(ctx) {
const channel = ctx.req.param('channel');
const id = ctx.req.param('id');
const url = `${host}/${channel}/${id}`;
@@ -35,9 +57,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `希望之声 - ${title}`,
link: url,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sourceforge/index.ts b/lib/routes/sourceforge/index.ts
index b638b4464..9992d1758 100644
--- a/lib/routes/sourceforge/index.ts
+++ b/lib/routes/sourceforge/index.ts
@@ -1,8 +1,27 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:routeParams?',
+ categories: ['university'],
+ example: '/sourceforge/topic=artificial-intelligence&os=windows',
+ parameters: { routeParams: 'route params, see below' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Software',
+ maintainers: ['JimenezLi'],
+ handler,
+};
+
+async function handler(ctx) {
const routeParams = ctx.req.param('routeParams');
const baseURL = 'https://sourceforge.net';
@@ -12,7 +31,7 @@ export default async (ctx) => {
const $ = load(response.data);
const itemList = $('ul.projects li[itemprop=itemListElement]');
- ctx.set('data', {
+ return {
title: $('.content h1').text().trim(),
link,
item: itemList.toArray().map((element) => {
@@ -29,5 +48,5 @@ export default async (ctx) => {
pubDate,
};
}),
- });
-};
+ };
+}
diff --git a/lib/routes/southcn/nfapp/column.ts b/lib/routes/southcn/nfapp/column.ts
index d46229abe..a9a679234 100644
--- a/lib/routes/southcn/nfapp/column.ts
+++ b/lib/routes/southcn/nfapp/column.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -9,7 +10,25 @@ import { art } from '@/utils/render';
import * as path from 'node:path';
import { parseArticle } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/nfapp/column/:column?',
+ categories: ['bbs'],
+ example: '/southcn/nfapp/column/38',
+ parameters: { column: '栏目或南方号 ID' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '南方 +(按栏目 ID)',
+ maintainers: ['TimWu007'],
+ handler,
+};
+
+async function handler(ctx) {
const columnId = ctx.req.param('column') ?? 38;
const getColumnDetail = `https://api.nfapp.southcn.com/nanfang_if/getColumn?columnId=${columnId}`;
@@ -41,9 +60,9 @@ export default async (ctx) => {
const items = await Promise.all(list.map((item) => parseArticle(item, cache.tryGet)));
- ctx.set('data', {
+ return {
title: columnName,
link: columnLink,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/southcn/nfapp/reporter.ts b/lib/routes/southcn/nfapp/reporter.ts
index e3bdf039d..9a6f82dc4 100644
--- a/lib/routes/southcn/nfapp/reporter.ts
+++ b/lib/routes/southcn/nfapp/reporter.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -9,7 +10,25 @@ import { art } from '@/utils/render';
import { parseArticle } from './utils';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/nfapp/reporter/:reporter',
+ categories: ['bbs'],
+ example: '/southcn/nfapp/reporter/969927791',
+ parameters: { reporter: '作者 UUID' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '南方 +(按作者)',
+ maintainers: ['TimWu007'],
+ handler,
+};
+
+async function handler(ctx) {
const reporterId = ctx.req.param('reporter');
const currentUrl = `https://api.nfapp.southcn.com/nanfang_if/reporter/list?reporterUuid=${reporterId}&pageSize=20&pageNo=1&origin=0`;
@@ -29,9 +48,9 @@ export default async (ctx) => {
const items = await Promise.all(list.map((item) => parseArticle(item, cache.tryGet)));
- ctx.set('data', {
+ return {
title: `南方+ - ${response.data.reportInfo.reporterName}`,
link: `https://static.nfapp.southcn.com/apptpl/reporterWorksList/index.html?reporterUuid=${reporterId}`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/spotify/artist.ts b/lib/routes/spotify/artist.ts
index b4d9625d3..ec5a5c027 100644
--- a/lib/routes/spotify/artist.ts
+++ b/lib/routes/spotify/artist.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import utils from './utils';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/artist/:id',
+ categories: ['picture'],
+ example: '/spotify/artist/6k9TBCxyr4bXwZ8Y21Kwn1',
+ parameters: { id: 'Artist ID' },
+ features: {
+ requireConfig: true,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['open.spotify.com/artist/:id'],
+ },
+ name: 'Artist Albums',
+ maintainers: ['outloudvi'],
+ handler,
+};
+
+async function handler(ctx) {
const token = await utils.getPublicToken();
const id = ctx.req.param('id');
const meta = await got
@@ -22,7 +44,7 @@ export default async (ctx) => {
.json();
const albums = itemsResponse.items;
- ctx.set('data', {
+ return {
title: `Albums of ${meta.name}`,
link: meta.external_urls.spotify,
allowEmpty: true,
@@ -34,5 +56,5 @@ export default async (ctx) => {
link: x.external_urls.spotify,
})),
image: meta.images.length ? meta.images[0].url : undefined,
- });
-};
+ };
+}
diff --git a/lib/routes/spotify/artists-top.ts b/lib/routes/spotify/artists-top.ts
index 90983d4a4..7a0bbf02d 100644
--- a/lib/routes/spotify/artists-top.ts
+++ b/lib/routes/spotify/artists-top.ts
@@ -1,7 +1,29 @@
+import { Route } from '@/types';
import utils from './utils';
import got from '@/utils/got';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/top/artists',
+ categories: ['picture'],
+ example: '/spotify/top/artists',
+ parameters: {},
+ features: {
+ requireConfig: true,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['open.spotify.com/'],
+ },
+ name: 'Personal Top Artists',
+ maintainers: ['outloudvi'],
+ handler,
+};
+
+async function handler() {
const token = await utils.getPrivateToken();
const itemsResponse = await got
.get(`https://api.spotify.com/v1/me/top/artists`, {
@@ -12,9 +34,9 @@ export default async (ctx) => {
.json();
const items = itemsResponse.items;
- ctx.set('data', {
+ return {
title: `Spotify: My Top Artists`,
allowEmpty: true,
item: items.map((element) => utils.parseArtist(element)),
- });
-};
+ };
+}
diff --git a/lib/routes/spotify/playlist.ts b/lib/routes/spotify/playlist.ts
index 5e2668a39..c0ef34e93 100644
--- a/lib/routes/spotify/playlist.ts
+++ b/lib/routes/spotify/playlist.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import utils from './utils';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/playlist/:id',
+ categories: ['picture'],
+ example: '/spotify/playlist/4UBVy1LttvodwivPUuwJk2',
+ parameters: { id: 'Playlist ID' },
+ features: {
+ requireConfig: true,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['open.spotify.com/playlist/:id'],
+ },
+ name: 'Playlist',
+ maintainers: ['outloudvi'],
+ handler,
+};
+
+async function handler(ctx) {
const token = await utils.getPublicToken();
const id = ctx.req.param('id');
const meta = await got
@@ -14,7 +36,7 @@ export default async (ctx) => {
.json();
const tracks = meta.tracks.items;
- ctx.set('data', {
+ return {
title: meta.name,
link: meta.external_urls.spotify,
description: meta.description,
@@ -24,5 +46,5 @@ export default async (ctx) => {
pubDate: parseDate(x.added_at),
})),
image: meta.images.length ? meta.images[0].url : undefined,
- });
-};
+ };
+}
diff --git a/lib/routes/spotify/saved.ts b/lib/routes/spotify/saved.ts
index 6afd65e4f..45f0ea9eb 100644
--- a/lib/routes/spotify/saved.ts
+++ b/lib/routes/spotify/saved.ts
@@ -1,8 +1,31 @@
+import { Route } from '@/types';
import utils from './utils';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/saved/:limit?',
+ categories: ['picture'],
+ example: '/spotify/saved/50',
+ parameters: { limit: 'Track count, 50 by default' },
+ features: {
+ requireConfig: true,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['open.spotify.com/collection/tracks'],
+ target: '/saved',
+ },
+ name: 'Personal Saved Tracks',
+ maintainers: ['outloudvi'],
+ handler,
+};
+
+async function handler(ctx) {
const token = await utils.getPrivateToken();
const limit = ctx.req.param('limit');
@@ -17,7 +40,7 @@ export default async (ctx) => {
.json();
const tracks = itemsResponse.items;
- ctx.set('data', {
+ return {
title: 'Spotify: My Saved Tracks',
link: 'https://open.spotify.com/collection/tracks',
description: `Latest ${pageSize} saved tracks on Spotify.`,
@@ -26,5 +49,5 @@ export default async (ctx) => {
...utils.parseTrack(x.track),
pubDate: parseDate(x.added_at),
})),
- });
-};
+ };
+}
diff --git a/lib/routes/spotify/show.ts b/lib/routes/spotify/show.ts
index 0eb684ef6..e58642a00 100644
--- a/lib/routes/spotify/show.ts
+++ b/lib/routes/spotify/show.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import utils from './utils';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/show/:id',
+ categories: ['picture'],
+ example: '/spotify/show/5CfCWKI5pZ28U0uOzXkDHe',
+ parameters: { id: 'Show ID' },
+ features: {
+ requireConfig: true,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['open.spotify.com/show/:id'],
+ },
+ name: 'Show',
+ maintainers: ['caiohsramos'],
+ handler,
+};
+
+async function handler(ctx) {
const token = await utils.getPublicToken();
const id = ctx.req.param('id');
@@ -16,7 +38,7 @@ export default async (ctx) => {
const episodes = meta.episodes.items;
- ctx.set('data', {
+ return {
title: meta.name,
description: meta.description,
link: meta.external_urls.spotify,
@@ -36,5 +58,5 @@ export default async (ctx) => {
enclosure_type: 'audio/mpeg',
})),
image: meta.images.length ? meta.images[0].url : undefined,
- });
-};
+ };
+}
diff --git a/lib/routes/spotify/tracks-top.ts b/lib/routes/spotify/tracks-top.ts
index 5bd558d72..2f70366b9 100644
--- a/lib/routes/spotify/tracks-top.ts
+++ b/lib/routes/spotify/tracks-top.ts
@@ -1,7 +1,29 @@
+import { Route } from '@/types';
import utils from './utils';
import got from '@/utils/got';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/top/tracks',
+ categories: ['picture'],
+ example: '/spotify/top/tracks',
+ parameters: {},
+ features: {
+ requireConfig: true,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['open.spotify.com/'],
+ },
+ name: 'Personal Top Tracks',
+ maintainers: ['outloudvi'],
+ handler,
+};
+
+async function handler() {
const token = await utils.getPrivateToken();
const itemsResponse = await got
.get(`https://api.spotify.com/v1/me/top/tracks`, {
@@ -12,9 +34,9 @@ export default async (ctx) => {
.json();
const items = itemsResponse.items;
- ctx.set('data', {
+ return {
title: `Spotify: My Top Tracks`,
allowEmpty: true,
item: items.map((element) => utils.parseTrack(element)),
- });
-};
+ };
+}
diff --git a/lib/routes/springer/journal.ts b/lib/routes/springer/journal.ts
index 03988c335..dc24e029a 100644
--- a/lib/routes/springer/journal.ts
+++ b/lib/routes/springer/journal.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -10,7 +11,28 @@ import { art } from '@/utils/render';
import { CookieJar } from 'tough-cookie';
const cookieJar = new CookieJar();
-export default async (ctx) => {
+export const route: Route = {
+ path: '/journal/:journal',
+ categories: ['finance'],
+ example: '/springer/journal/10450',
+ parameters: { journal: 'Journal Code, the number in the URL from the journal homepage' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.springer.com/journal/:journal/*'],
+ },
+ name: 'Journal',
+ maintainers: ['Derekmini', 'TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const host = 'https://www.springer.com';
const journal = ctx.req.param('journal');
const jrnlUrl = `${host}/journal/${journal}`;
@@ -68,9 +90,9 @@ export default async (ctx) => {
})
)
);
- ctx.set('data', {
+ return {
title: jrnlName,
link: jrnlUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sputniknews/index.ts b/lib/routes/sputniknews/index.ts
index 93e5624a4..c7443a2cd 100644
--- a/lib/routes/sputniknews/index.ts
+++ b/lib/routes/sputniknews/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -36,7 +37,25 @@ const languages = {
portuguese: 'https://br.sputniknews.com',
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:category?/:language?',
+ categories: ['bbs'],
+ example: '/sputniknews',
+ parameters: { category: 'Category, can be found in URL, `news` by default', language: 'Language, see below, English by default' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Category',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? 'news';
const language = ctx.req.param('language') ?? 'english';
@@ -87,9 +106,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `${category} - Sputnik News`,
link: `${rootUrl}/${category}`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sqmc/www.ts b/lib/routes/sqmc/www.ts
index a44a5b3b9..111432bd8 100644
--- a/lib/routes/sqmc/www.ts
+++ b/lib/routes/sqmc/www.ts
@@ -1,10 +1,32 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/www/:category?',
+ categories: ['forecast'],
+ example: '/sqmc/www/3157',
+ parameters: { category: '分类ID,默认为`3157`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['sqmc.edu.cn/:category/list.htm'],
+ },
+ name: '官网信息',
+ maintainers: ['nyaShine'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') || '3157';
const rootUrl = 'https://www.sqmc.edu.cn';
@@ -18,7 +40,7 @@ export default async (ctx) => {
const $ = load(response.data);
const list = $('div#wp_news_w9 ul li').get();
- ctx.set('data', {
+ return {
title: `新乡医学院三全学院官网信息${$('title').text()}`,
link: currentUrl,
item: await Promise.all(
@@ -46,5 +68,5 @@ export default async (ctx) => {
return cacheIn;
})
),
- });
-};
+ };
+}
diff --git a/lib/routes/sse/convert.ts b/lib/routes/sse/convert.ts
index 0dd9c461f..8e6757796 100644
--- a/lib/routes/sse/convert.ts
+++ b/lib/routes/sse/convert.ts
@@ -1,7 +1,26 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/convert/:query?',
+ categories: ['other'],
+ example: '/sse/convert/beginDate=2018-08-18&endDate=2019-08-18&companyCode=603283&title=股份',
+ parameters: { query: '筛选条件,见示例' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '可转换公司债券公告',
+ maintainers: ['kt286'],
+ handler,
+};
+
+async function handler(ctx) {
const query = ctx.req.param('query') ?? ''; // beginDate=2018-08-18&endDate=2019-08-18&companyCode=603283&title=股份
const pageUrl = 'https://bond.sse.com.cn/disclosure/announ/convertible/';
const host = 'https://www.sse.com.cn';
@@ -31,9 +50,9 @@ export default async (ctx) => {
author: item.security_Code,
}));
- ctx.set('data', {
+ return {
title: '上证债券信息网 - 可转换公司债券公告',
link: pageUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sse/disclosure.ts b/lib/routes/sse/disclosure.ts
index 44bbb6295..7daffbcbd 100644
--- a/lib/routes/sse/disclosure.ts
+++ b/lib/routes/sse/disclosure.ts
@@ -1,7 +1,26 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/disclosure/:query?',
+ categories: ['other'],
+ example: '/sse/disclosure/beginDate=2018-08-18&endDate=2020-08-25&productId=600696',
+ parameters: { query: '筛选条件,见示例' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '上市公司信息最新公告披露',
+ maintainers: ['harveyqiu'],
+ handler,
+};
+
+async function handler(ctx) {
const query = ctx.req.param('query') ?? ''; // beginDate=2018-08-18&endDate=2020-09-01&productId=600696
const queries = query.split('&').reduce((acc, cur) => {
const [key, value] = cur.split('=');
@@ -39,9 +58,9 @@ export default async (ctx) => {
author: item.SECURITY_NAME,
}));
- ctx.set('data', {
+ return {
title: `上海证券交易所 - 上市公司信息 - ${items[0].author}最新公告`,
link: pageUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sse/inquire.ts b/lib/routes/sse/inquire.ts
index 412c3ca83..3e5be8fb5 100644
--- a/lib/routes/sse/inquire.ts
+++ b/lib/routes/sse/inquire.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -6,7 +7,28 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/inquire',
+ categories: ['other'],
+ example: '/sse/inquire',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.sse.com.cn/disclosure/credibility/supervision/inquiries', 'www.sse.com.cn/'],
+ },
+ name: '监管问询',
+ maintainers: ['Jeason0228'],
+ handler,
+};
+
+async function handler() {
const refererUrl = 'https://www.sse.com.cn/disclosure/credibility/supervision/inquiries/';
const response = await got('https://query.sse.com.cn/commonSoaQuery.do', {
searchParams: {
@@ -40,9 +62,9 @@ export default async (ctx) => {
author: item.extGSJC,
}));
- ctx.set('data', {
+ return {
title: '上海证券交易所 - 科创板股票审核',
link: refererUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sse/lawandrules.ts b/lib/routes/sse/lawandrules.ts
index fce7a7ac7..207905e13 100644
--- a/lib/routes/sse/lawandrules.ts
+++ b/lib/routes/sse/lawandrules.ts
@@ -1,9 +1,28 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/lawandrules/:slug?',
+ categories: ['other'],
+ example: '/sse/lawandrules',
+ parameters: { slug: '见下文,默认为 `latest`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '本所业务指南与流程',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const slug = ctx.req.param('slug') ?? 'latest';
const rootUrl = 'https://www.sse.com.cn';
@@ -36,9 +55,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('title').text(),
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sse/renewal.ts b/lib/routes/sse/renewal.ts
index 7570952de..b9d53fa29 100644
--- a/lib/routes/sse/renewal.ts
+++ b/lib/routes/sse/renewal.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -12,7 +13,28 @@ dayjs.extend(localizedFormat);
const currStatusName = ['全部', '已受理', '已询问', '通过', '未通过', '提交注册', '补充审核', '注册结果', '中止', '终止'];
-export default async (ctx) => {
+export const route: Route = {
+ path: '/renewal',
+ categories: ['other'],
+ example: '/sse/renewal',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['kcb.sse.com.cn/home', 'kcb.sse.com.cn/'],
+ },
+ name: '科创板项目动态',
+ maintainers: ['Jeason0228'],
+ handler,
+};
+
+async function handler() {
const pageUrl = 'https://kcb.sse.com.cn/renewal/';
const host = `https://kcb.sse.com.cn`;
@@ -52,9 +74,9 @@ export default async (ctx) => {
author: item.stockAuditName,
}));
- ctx.set('data', {
+ return {
title: '上海证券交易所 - 科创板项目动态',
link: pageUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/ssm/news.ts b/lib/routes/ssm/news.ts
index c68d9c58d..309482fc5 100644
--- a/lib/routes/ssm/news.ts
+++ b/lib/routes/ssm/news.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -10,7 +11,28 @@ import { art } from '@/utils/render';
const rootUrl = `https://www.ssm.gov.mo`;
const newsUrl = `${rootUrl}/apps1/content/ch/973/itemlist.aspx?defaultcss=false&dlimit=20&showdate=true&dorder=cridate%20desc,displaydate%20desc&withattach=true`;
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news',
+ categories: ['study'],
+ example: '/ssm/news',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.ssm.gov.mo/', 'www.ssm.gov.mo/portal'],
+ },
+ name: '最新消息',
+ maintainers: ['Fatpandac'],
+ handler,
+};
+
+async function handler() {
const response = await got.get(newsUrl);
const $ = load(response.data);
const list = $('body > div > div > ul > li');
@@ -33,9 +55,9 @@ export default async (ctx) => {
})
.get();
- ctx.set('data', {
+ return {
title: '澳门卫生局-最新消息',
link: rootUrl,
item,
- });
-};
+ };
+}
diff --git a/lib/routes/sspai/activity.ts b/lib/routes/sspai/activity.ts
index a411e7850..c45ccdf01 100644
--- a/lib/routes/sspai/activity.ts
+++ b/lib/routes/sspai/activity.ts
@@ -1,7 +1,30 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/activity/:slug',
+ categories: ['traditional-media'],
+ example: '/sspai/activity/urfp0d9i',
+ parameters: { slug: '作者 slug,可在作者主页URL中找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['sspai.com/u/:id/updates'],
+ target: '/activity/:id',
+ },
+ name: '作者动态',
+ maintainers: ['umm233'],
+ handler,
+};
+
+async function handler(ctx) {
const slug = ctx.req.param('slug');
const link = `https://sspai.com/u/${slug}/updates`;
@@ -29,7 +52,7 @@ export default async (ctx) => {
let item_desc = '';
let item_url = '';
- ctx.set('data', {
+ return {
title: `少数派用户「${user_nickname}」动态更新`,
link,
description: `少数派用户「${user_nickname}」的动态更新`,
@@ -79,5 +102,5 @@ export default async (ctx) => {
pubDate: parseDate(item.created_at * 1000),
};
}),
- });
-};
+ };
+}
diff --git a/lib/routes/sspai/author.ts b/lib/routes/sspai/author.ts
index e57ad2bd0..9c518d318 100644
--- a/lib/routes/sspai/author.ts
+++ b/lib/routes/sspai/author.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -18,7 +19,28 @@ async function getUserId(slug) {
return response.data.data.id;
}
-export default async (ctx) => {
+export const route: Route = {
+ path: '/author/:id',
+ categories: ['traditional-media'],
+ example: '/sspai/author/796518',
+ parameters: { id: '作者 slug 或 id,slug 可在作者主页URL中找到,id 不易查找,仅作兼容' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['sspai.com/u/:id/posts'],
+ },
+ name: '作者',
+ maintainers: ['SunShinenny', 'hoilc'],
+ handler,
+};
+
+async function handler(ctx) {
const id = /^\d+$/.test(ctx.req.param('id')) ? ctx.req.param('id') : await getUserId(ctx.req.param('id'));
const api_url = `https://sspai.com/api/v1/articles?offset=0&limit=20&author_ids=${id}&include_total=false`;
const resp = await got({
@@ -49,10 +71,10 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: `${author_nickname} - 少数派作者`,
link: `https://sspai.com/u/${author_slug}/posts`,
description: `${author_nickname} 更新推送 `,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sspai/bookmarks.ts b/lib/routes/sspai/bookmarks.ts
index d185bffb5..76f7145cf 100644
--- a/lib/routes/sspai/bookmarks.ts
+++ b/lib/routes/sspai/bookmarks.ts
@@ -1,7 +1,29 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/bookmarks/:slug',
+ categories: ['traditional-media'],
+ example: '/sspai/bookmarks/urfp0d9i',
+ parameters: { slug: '用户 slug,可在个人主页URL中找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['sspai.com/u/:slug/bookmark_posts'],
+ },
+ name: '用户收藏',
+ maintainers: ['curly210102'],
+ handler,
+};
+
+async function handler(ctx) {
const slug = ctx.req.param('slug');
const link = `https://sspai.com/u/${slug}/bookmark_posts`;
@@ -26,7 +48,7 @@ export default async (ctx) => {
).data.data;
const { nickname } = user;
- ctx.set('data', {
+ return {
title: `${nickname} 的全部收藏 - 少数派`,
link,
description: `少数派用户「${nickname}」的全部收藏`,
@@ -37,5 +59,5 @@ export default async (ctx) => {
pubDate: parseDate(article.released_time * 1000),
author: article.author.nickname,
})),
- });
-};
+ };
+}
diff --git a/lib/routes/sspai/column.ts b/lib/routes/sspai/column.ts
index d3df7b3fc..fd3992ce7 100644
--- a/lib/routes/sspai/column.ts
+++ b/lib/routes/sspai/column.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/column/:id',
+ categories: ['traditional-media'],
+ example: '/sspai/column/262',
+ parameters: { id: '专栏 id' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['sspai.com/column/:id'],
+ },
+ name: '专栏',
+ maintainers: ['LogicJake'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const link = `https://sspai.com/column/${id}`;
@@ -54,10 +76,10 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: `少数派专栏-${title}`,
link,
description,
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/sspai/index.ts b/lib/routes/sspai/index.ts
index 8b1b7ad67..6ae13dea1 100644
--- a/lib/routes/sspai/index.ts
+++ b/lib/routes/sspai/index.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/index',
+ categories: ['traditional-media'],
+ example: '/sspai/index',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['sspai.com/index'],
+ },
+ name: '首页',
+ maintainers: ['HenryQW'],
+ handler,
+};
+
+async function handler() {
const api_url = 'https://sspai.com/api/v1/article/index/page/get?limit=10&offset=0&created_at=0';
const resp = await got({
method: 'get',
@@ -29,10 +51,10 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: '少数派 -- 首页',
link: 'https://sspai.com',
description: '少数派 -- 首页',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sspai/matrix.ts b/lib/routes/sspai/matrix.ts
index 0c8a37508..7619c413b 100644
--- a/lib/routes/sspai/matrix.ts
+++ b/lib/routes/sspai/matrix.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/matrix',
+ categories: ['traditional-media'],
+ example: '/sspai/matrix',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['sspai.com/matrix'],
+ },
+ name: 'Matrix',
+ maintainers: ['feigaoxyz'],
+ handler,
+};
+
+async function handler() {
const api_url = 'https://sspai.com/api/v1/articles?offset=0&limit=20&is_matrix=1&sort=matrix_at&include_total=false';
const resp = await got({
method: 'get',
@@ -30,10 +52,10 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: '少数派 -- Matrix',
link: 'https://sspai.com/matrix',
description: '少数派 -- Matrix',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sspai/series-update.ts b/lib/routes/sspai/series-update.ts
index 3fabb9b1a..279dafc94 100644
--- a/lib/routes/sspai/series-update.ts
+++ b/lib/routes/sspai/series-update.ts
@@ -1,7 +1,29 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/series/:id',
+ categories: ['traditional-media'],
+ example: '/sspai/series/77',
+ parameters: { id: '专栏 id' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['sspai.com/series/:id', 'sspai.com/series/:id/list', 'sspai.com/series/:id/metadata'],
+ },
+ name: '付费专栏文章更新',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const seriesInfo = await got.get(`https://sspai.com/api/v1/series/info/get?id=${id}&view=second`);
@@ -27,10 +49,10 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: `${seriesInfo.data.data.title} - 少数派`,
description: `${seriesInfo.data.data.description} - 少数派`,
link: `https://sspai.com/series/${id}`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sspai/series.ts b/lib/routes/sspai/series.ts
index c2201716d..c18458edf 100644
--- a/lib/routes/sspai/series.ts
+++ b/lib/routes/sspai/series.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/series',
+ categories: ['traditional-media'],
+ example: '/sspai/series',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['sspai.com/series'],
+ },
+ name: '最新上架付费专栏',
+ maintainers: ['HenryQW'],
+ handler,
+};
+
+async function handler() {
const response = await got('https://sspai.com/api/v1/series/tag/all/get');
const products = response.data.data.reduce((acc, cate) => {
@@ -38,10 +60,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: '少数派 -- 最新上架付费专栏',
link: 'https://sspai.com/series',
description: '少数派 -- 最新上架付费专栏',
item,
- });
-};
+ };
+}
diff --git a/lib/routes/sspai/shortcuts-gallery.ts b/lib/routes/sspai/shortcuts-gallery.ts
index 641d23d6e..2bdb0f569 100644
--- a/lib/routes/sspai/shortcuts-gallery.ts
+++ b/lib/routes/sspai/shortcuts-gallery.ts
@@ -1,7 +1,29 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/shortcuts',
+ categories: ['traditional-media'],
+ example: '/sspai/shortcuts',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['shortcuts.sspai.com/*'],
+ },
+ name: 'Shortcuts Gallery',
+ maintainers: ['Andiedie'],
+ handler,
+};
+
+async function handler() {
const {
data: { data: categories },
} = await got('https://shortcuts.sspai.com/api/v1/user/workflow/all/get');
@@ -20,10 +42,10 @@ export default async (ctx) => {
}
}
- ctx.set('data', {
+ return {
title: 'Shortcuts Gallery - 少数派',
link: 'https://shortcuts.sspai.com/#/main/workflow',
description: 'Shortcuts Gallery - 少数派',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sspai/tag.ts b/lib/routes/sspai/tag.ts
index fa4f5f59f..770a9532d 100644
--- a/lib/routes/sspai/tag.ts
+++ b/lib/routes/sspai/tag.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/tag/:keyword',
+ categories: ['traditional-media'],
+ example: '/sspai/tag/apple',
+ parameters: { keyword: '关键词' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['sspai.com/tag/:keyword'],
+ },
+ name: '标签订阅',
+ maintainers: ['Jeason0228'],
+ handler,
+};
+
+async function handler(ctx) {
const keyword = ctx.req.param('keyword');
const keyword_encode = encodeURIComponent(decodeURIComponent(keyword));
const api_url = `https://sspai.com/api/v1/articles?offset=0&limit=50&has_tag=1&tag=${keyword_encode}&include_total=false`;
@@ -34,10 +56,10 @@ export default async (ctx) => {
});
})
);
- ctx.set('data', {
+ return {
title: `#${keyword} - 少数派`,
link: host,
description: `${keyword} 更新推送 `,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sspai/topic.ts b/lib/routes/sspai/topic.ts
index e36a7f0ac..25aff9db3 100644
--- a/lib/routes/sspai/topic.ts
+++ b/lib/routes/sspai/topic.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/topic/:id',
+ categories: ['traditional-media'],
+ example: '/sspai/topic/250',
+ parameters: { id: '专题 id,可在专题主页URL中找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['sspai.com/topic/:id'],
+ },
+ name: '专题内文章更新',
+ maintainers: ['SunShinenny'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const api_url = `https://sspai.com/api/v1/articles?offset=0&limit=20&topic_id=${id}&sort=created_at&include_total=false`;
const response = await got({
@@ -42,10 +64,10 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: `少数派专题-${topic_title}`,
link: topic_link,
description: topic_des,
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/sspai/topics.ts b/lib/routes/sspai/topics.ts
index 2fb2466e0..14fc3fee1 100644
--- a/lib/routes/sspai/topics.ts
+++ b/lib/routes/sspai/topics.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/topics',
+ categories: ['traditional-media'],
+ example: '/sspai/topics',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['sspai.com/topics'],
+ },
+ name: '专题',
+ maintainers: ['SunShinenny'],
+ handler,
+};
+
+async function handler() {
const api_url = `https://sspai.com/api/v1/topics?offset=0&limit=20&include_total=false`;
const resp = await got({
method: 'get',
@@ -29,10 +51,10 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: `少数派专题广场更新推送`,
link: `https://sspai.com/topics`,
description: `仅仅推送新的专题(集合型而非具体文章) `,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sspu/jwc.ts b/lib/routes/sspu/jwc.ts
index 475645b4b..3d4e15c76 100644
--- a/lib/routes/sspu/jwc.ts
+++ b/lib/routes/sspu/jwc.ts
@@ -1,10 +1,21 @@
+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';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/jwc/:listId',
+ radar: {
+ source: ['jwc.sspu.edu.cn/jwc/:listId/list.htm'],
+ },
+ name: 'Unknown',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const listId = ctx.req.param('listId');
const baseUrl = 'https://jwc.sspu.edu.cn';
@@ -37,9 +48,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('head title').text(),
link,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/sspu/pe.ts b/lib/routes/sspu/pe.ts
index 415633747..2f8ab8971 100644
--- a/lib/routes/sspu/pe.ts
+++ b/lib/routes/sspu/pe.ts
@@ -1,9 +1,21 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/pe/:id?',
+ radar: {
+ source: ['pe2016.sspu.edu.cn/:id/list.htm'],
+ target: '/pe/:id',
+ },
+ name: 'Unknown',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const { id = '342' } = ctx.req.param();
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30;
@@ -52,7 +64,7 @@ export default async (ctx) => {
const subtitle = $('title').text();
const icon = new URL($('link[rel="shortcut icon"]').prop('href'), rootUrl).href;
- ctx.set('data', {
+ return {
item: items,
title: `${author} - ${subtitle}`,
link: currentUrl,
@@ -62,5 +74,5 @@ export default async (ctx) => {
logo: icon,
subtitle,
author,
- });
-};
+ };
+}
diff --git a/lib/routes/startuplatte/index.ts b/lib/routes/startuplatte/index.ts
index 29783c283..1bb24bdea 100644
--- a/lib/routes/startuplatte/index.ts
+++ b/lib/routes/startuplatte/index.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:category?',
+ categories: ['traditional-media'],
+ example: '/startuplatte',
+ parameters: { category: '分类,见下表,默认为首頁' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['startuplatte.com/category/:category', 'startuplatte.com/'],
+ },
+ name: '分类',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? '';
const rootUrl = 'https://startuplatte.com';
@@ -49,9 +71,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('title').text(),
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/stbu/jsjxy.ts b/lib/routes/stbu/jsjxy.ts
index 45df6d1d0..a6a553150 100644
--- a/lib/routes/stbu/jsjxy.ts
+++ b/lib/routes/stbu/jsjxy.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -6,7 +7,28 @@ import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
const gbk2utf8 = (s) => iconv.decode(s, 'gbk');
-export default async (ctx) => {
+export const route: Route = {
+ path: '/jsjxy',
+ categories: ['forecast'],
+ example: '/stbu/jsjxy',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['jsjxy.stbu.edu.cn/news', 'jsjxy.stbu.edu.cn/'],
+ },
+ name: '计算机学院通知公告',
+ maintainers: ['HyperCherry'],
+ handler,
+};
+
+async function handler() {
const baseUrl = 'https://jsjxy.stbu.edu.cn/news/';
const { data: response } = await got(baseUrl, {
responseType: 'buffer',
@@ -43,10 +65,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: '四川工商学院计算机学院 - 新闻动态',
link: baseUrl,
description: '四川工商学院计算机学院 - 新闻动态',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/stbu/xyxw.ts b/lib/routes/stbu/xyxw.ts
index 726a923e2..78e2a448f 100644
--- a/lib/routes/stbu/xyxw.ts
+++ b/lib/routes/stbu/xyxw.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -6,7 +7,17 @@ import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
const gbk2utf8 = (s) => iconv.decode(s, 'gbk');
-export default async (ctx) => {
+export const route: Route = {
+ path: '/xyxw',
+ radar: {
+ source: ['stbu.edu.cn/html/news/xueyuan', 'stbu.edu.cn/'],
+ },
+ name: 'Unknown',
+ maintainers: ['HyperCherry'],
+ handler,
+};
+
+async function handler() {
const baseUrl = 'https://www.stbu.edu.cn';
const requestUrl = `${baseUrl}/html/news/xueyuan/`;
const { data: response } = await got(requestUrl, {
@@ -44,10 +55,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: '四川工商学院 - 学院新闻',
link: requestUrl,
description: '四川工商学院 - 学院新闻',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/stcn/index.ts b/lib/routes/stcn/index.ts
index c47f0828e..228c0af37 100644
--- a/lib/routes/stcn/index.ts
+++ b/lib/routes/stcn/index.ts
@@ -1,10 +1,29 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:id?',
+ categories: ['other'],
+ example: '/stcn/yw',
+ parameters: { id: '栏目 id,见下表,默认为要闻' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '栏目',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id') ?? 'yw';
const rootUrl = 'https://www.stcn.com';
@@ -57,9 +76,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `证券时报网 - ${$('.breadcrumb a').last().text()}`,
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/stdaily/digitalpaper.ts b/lib/routes/stdaily/digitalpaper.ts
index 021e9b297..7f9b106f2 100644
--- a/lib/routes/stdaily/digitalpaper.ts
+++ b/lib/routes/stdaily/digitalpaper.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -96,7 +97,25 @@ const getListArticles = async (list, cache) => {
return items;
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/digitalpaper',
+ categories: ['bbs'],
+ example: '/stdaily/digitalpaper',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '科技日报',
+ maintainers: ['lyqluis'],
+ handler,
+};
+
+async function handler() {
const date = new Date();
const dateStr = date.toLocaleDateString('zh-CN', {
year: 'numeric',
@@ -121,9 +140,9 @@ export default async (ctx) => {
// get all articles
const items = await getListArticles(allPageArticleLists, cache);
- ctx.set('data', {
+ return {
title: `科技日报`,
link: 'http://digitalpaper.stdaily.com',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/stheadline/std/realtime.ts b/lib/routes/stheadline/std/realtime.ts
index 88ac8f443..7e5b9155d 100644
--- a/lib/routes/stheadline/std/realtime.ts
+++ b/lib/routes/stheadline/std/realtime.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -6,7 +7,14 @@ import timezone from '@/utils/timezone';
const baseUrl = 'https://std.stheadline.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/std/realtime/:category{.+}?',
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const { category = '即時' } = ctx.req.param();
const url = `${baseUrl}/realtime/${category}`;
const { data: response } = await got(url);
@@ -39,11 +47,11 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('head title').text(),
description: $('meta[name=description]').attr('content'),
image: 'https://std.stheadline.com/dist/images/favicon/icon-512.png',
link: url,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/stockedge/daily-news.ts b/lib/routes/stockedge/daily-news.ts
index 650560b0c..177078dcb 100644
--- a/lib/routes/stockedge/daily-news.ts
+++ b/lib/routes/stockedge/daily-news.ts
@@ -1,7 +1,29 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import { getData, getList } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/daily-updates/news',
+ categories: ['other'],
+ example: '/stockedge/daily-updates/news',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['web.stockedge.com/daily-updates/news'],
+ },
+ name: 'Daily Updates News',
+ maintainers: ['Rjnishant530'],
+ handler,
+};
+
+async function handler() {
const baseUrl = 'https://web.stockedge.com/daily-updates?section=news';
const apiPath = 'https://api.stockedge.com/Api/DailyDashboardApi/GetLatestNewsItems';
const apiInfo = 'https://api.stockedge.com/Api/SecurityDashboardApi/GetSecurityOverview';
@@ -18,7 +40,7 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: 'Stock Edge',
link: baseUrl,
item: items,
@@ -26,5 +48,5 @@ export default async (ctx) => {
logo: 'https://web.stockedge.com/assets/icon/favicon.png',
icon: 'https://web.stockedge.com/assets/img/light/icon.png',
language: 'en-us',
- });
-};
+ };
+}
diff --git a/lib/routes/storm/index.ts b/lib/routes/storm/index.ts
index c4f12355e..e80dce6ba 100644
--- a/lib/routes/storm/index.ts
+++ b/lib/routes/storm/index.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:category?/:id?',
+ categories: ['traditional-media'],
+ example: '/storm',
+ parameters: { category: '分类,见下表,默认为新聞總覽', id: '子分类 ID,可在 URL 中找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['storm.mg/:category/:id'],
+ },
+ name: '分类',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? 'articles';
const id = ctx.req.param('id') ?? '';
@@ -50,9 +72,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('title').text(),
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/storyfm/episodes.ts b/lib/routes/storyfm/episodes.ts
index 97e77a614..d3b2d2eb8 100644
--- a/lib/routes/storyfm/episodes.ts
+++ b/lib/routes/storyfm/episodes.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -8,7 +9,28 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/episodes',
+ categories: ['picture'],
+ example: '/storyfm/episodes',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: true,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['storyfm.cn/episodes-list', 'storyfm.cn/'],
+ },
+ name: '播客',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler() {
const rootUrl = 'https://storyfm.cn';
const currentUrl = `${rootUrl}/episodes-list/`;
@@ -57,11 +79,11 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: '故事FM',
link: currentUrl,
item: items,
itunes_author: '故事FM',
image: $('.custom-logo-link img').attr('src'),
- });
-};
+ };
+}
diff --git a/lib/routes/storyfm/index.ts b/lib/routes/storyfm/index.ts
index 29bab8b8c..99f0524b7 100644
--- a/lib/routes/storyfm/index.ts
+++ b/lib/routes/storyfm/index.ts
@@ -1,7 +1,29 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/index',
+ categories: ['picture'],
+ example: '/storyfm/index',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['storyfm.cn/'],
+ },
+ name: '首页',
+ maintainers: ['sanmmm'],
+ handler,
+};
+
+async function handler() {
const url = 'http://storyfm.cn';
const response = await got(url);
const $ = load(response.data);
@@ -35,10 +57,10 @@ export default async (ctx) => {
};
})
.get();
- ctx.set('data', {
+ return {
title: '故事说FM',
description: '故事说FM',
link: url,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/stratechery/index.ts b/lib/routes/stratechery/index.ts
index 0939b8880..db1d7667c 100644
--- a/lib/routes/stratechery/index.ts
+++ b/lib/routes/stratechery/index.ts
@@ -1,6 +1,14 @@
+import { Route } from '@/types';
import buildData from '@/utils/common-config';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/',
+ name: 'Unknown',
+ maintainers: ['chazeon'],
+ handler,
+};
+
+async function handler(ctx) {
const link = 'https://stratechery.com/';
ctx.set(
@@ -20,4 +28,4 @@ export default async (ctx) => {
},
})
);
-};
+}
diff --git a/lib/routes/studygolang/go.ts b/lib/routes/studygolang/go.ts
index 8ab2780b5..c5bd48bc5 100644
--- a/lib/routes/studygolang/go.ts
+++ b/lib/routes/studygolang/go.ts
@@ -1,5 +1,27 @@
+import { Route } from '@/types';
import { FetchGoItems } from './utils';
-export default async (ctx) => {
- ctx.set('data', await FetchGoItems(ctx));
+export const route: Route = {
+ path: '/go/:id?',
+ categories: ['design'],
+ example: '/studygolang/go/daily',
+ parameters: { id: '板块 id,默认为周刊' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['studygolang.com/go/:id', 'studygolang.com/'],
+ },
+ name: '板块',
+ maintainers: ['nczitzk'],
+ handler,
};
+
+async function handler(ctx) {
+ ctx.set('data', await FetchGoItems(ctx));
+}
diff --git a/lib/routes/studygolang/jobs.ts b/lib/routes/studygolang/jobs.ts
index c3cd7a5e9..f845189a8 100644
--- a/lib/routes/studygolang/jobs.ts
+++ b/lib/routes/studygolang/jobs.ts
@@ -1,5 +1,24 @@
+import { Route } from '@/types';
import { FetchGoItems } from './utils';
-export default async (ctx) => {
- ctx.set('data', await FetchGoItems(ctx, 'jobs'));
+export const route: Route = {
+ path: '/jobs',
+ categories: ['design'],
+ example: '/studygolang/jobs',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '招聘',
+ maintainers: ['CcccFz', 'nczitzk'],
+ handler,
};
+
+async function handler(ctx) {
+ ctx.set('data', await FetchGoItems(ctx, 'jobs'));
+}
diff --git a/lib/routes/studygolang/weekly.ts b/lib/routes/studygolang/weekly.ts
index 1e557343c..288c33b0e 100644
--- a/lib/routes/studygolang/weekly.ts
+++ b/lib/routes/studygolang/weekly.ts
@@ -1,5 +1,24 @@
+import { Route } from '@/types';
import { FetchGoItems } from './utils';
-export default async (ctx) => {
- ctx.set('data', await FetchGoItems(ctx, 'weekly'));
+export const route: Route = {
+ path: '/weekly',
+ categories: ['design'],
+ example: '/studygolang/weekly',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '周刊',
+ maintainers: ['CWeilet', 'nczitzk'],
+ handler,
};
+
+async function handler(ctx) {
+ ctx.set('data', await FetchGoItems(ctx, 'weekly'));
+}
diff --git a/lib/routes/subhd/index.ts b/lib/routes/subhd/index.ts
index dba7e2be8..7033df399 100644
--- a/lib/routes/subhd/index.ts
+++ b/lib/routes/subhd/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -18,7 +19,14 @@ const config = {
},
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:type?/:category?',
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type') ?? 'sub';
const category = ctx.req.param('category') ?? config[type].category;
@@ -72,9 +80,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('title').text(),
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/supchina/index.ts b/lib/routes/supchina/index.ts
index 829117ecb..dfc9a06cf 100644
--- a/lib/routes/supchina/index.ts
+++ b/lib/routes/supchina/index.ts
@@ -1,9 +1,21 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/',
+ radar: {
+ source: ['supchina.com/feed', 'supchina.com/'],
+ target: '',
+ },
+ name: 'Unknown',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const rootUrl = 'https://supchina.com';
const currentUrl = `${rootUrl}/feed/`;
@@ -60,9 +72,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('title').first().text(),
link: rootUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/supchina/podcasts.ts b/lib/routes/supchina/podcasts.ts
index 52abf75ff..df62ec4c0 100644
--- a/lib/routes/supchina/podcasts.ts
+++ b/lib/routes/supchina/podcasts.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/podcasts',
+ categories: ['traditional-media'],
+ example: '/supchina/podcasts',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['supchina.com/podcasts', 'supchina.com/'],
+ },
+ name: 'Podcasts',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const rootUrl = 'https://supchina.com';
const currentUrl = `${rootUrl}/feed/podcast`;
@@ -57,11 +79,11 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: 'SupChina - Podcasts',
link: `${rootUrl}/podcasts`,
itunes_author: $('channel itunes\\:author').first().text(),
image: $('itunes\\:image').attr('href'),
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/surfshark/blog.ts b/lib/routes/surfshark/blog.ts
index fbe55e12d..c908a62aa 100644
--- a/lib/routes/surfshark/blog.ts
+++ b/lib/routes/surfshark/blog.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -8,7 +9,14 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/blog/:category{.+}?',
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category');
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 15;
@@ -80,7 +88,7 @@ export default async (ctx) => {
const icon = new URL($('link[rel="apple-touch-icon"]').prop('href'), rootUrl).href;
- ctx.set('data', {
+ return {
item: items,
title: $('title').text(),
link: currentUrl,
@@ -91,5 +99,5 @@ export default async (ctx) => {
logo: icon,
subtitle: $('meta[property="og:title"]').prop('content'),
author: $('meta[property="og:site_name"]').prop('content'),
- });
-};
+ };
+}
diff --git a/lib/routes/sustech/bidding.ts b/lib/routes/sustech/bidding.ts
index 6d4b10203..9e1da9ad1 100644
--- a/lib/routes/sustech/bidding.ts
+++ b/lib/routes/sustech/bidding.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/bidding',
+ categories: ['forecast'],
+ example: '/sustech/bidding',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['biddingoffice.sustech.edu.cn/'],
+ },
+ name: '采购与招标管理部',
+ maintainers: ['sparkcyf'],
+ handler,
+};
+
+async function handler() {
const link = 'http://biddingoffice.sustech.edu.cn';
const response = await got({
method: 'get',
@@ -15,7 +37,7 @@ export default async (ctx) => {
const list = $('.index-wrap.index-2 ul li');
- ctx.set('data', {
+ return {
title: '南方科技大学采购与招标管理部',
link,
item:
@@ -30,5 +52,5 @@ export default async (ctx) => {
link: a.attr('href'),
};
}),
- });
-};
+ };
+}
diff --git a/lib/routes/sustech/newshub-zh.ts b/lib/routes/sustech/newshub-zh.ts
index d0d147792..8a27f96d1 100644
--- a/lib/routes/sustech/newshub-zh.ts
+++ b/lib/routes/sustech/newshub-zh.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/newshub-zh',
+ categories: ['forecast'],
+ example: '/sustech/newshub-zh',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['newshub.sustech.edu.cn/news'],
+ },
+ name: '新闻网(中文)',
+ maintainers: ['sparkcyf'],
+ handler,
+};
+
+async function handler() {
const baseUrl = 'https://newshub.sustech.edu.cn';
const link = `${baseUrl}/news`;
const response = await got({
@@ -16,7 +38,7 @@ export default async (ctx) => {
const list = $('.m-newslist ul li');
- ctx.set('data', {
+ return {
title: '南方科技大学新闻网-中文',
link,
item:
@@ -35,5 +57,5 @@ export default async (ctx) => {
link: `${baseUrl}${item.find('a').attr('href')}`,
};
}),
- });
-};
+ };
+}
diff --git a/lib/routes/sustech/yjs.ts b/lib/routes/sustech/yjs.ts
index 522c668ab..6c3ea87f4 100644
--- a/lib/routes/sustech/yjs.ts
+++ b/lib/routes/sustech/yjs.ts
@@ -1,7 +1,29 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/yjs',
+ categories: ['forecast'],
+ example: '/sustech/yjs',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['gs.sustech.edu.cn/'],
+ },
+ name: '研究生网通知公告',
+ maintainers: ['shengmaosu'],
+ handler,
+};
+
+async function handler() {
const baseUrl = 'https://gs.sustech.edu.cn';
const link = `${baseUrl}/#/common/index?current_id=99&id=99`;
const response = await got(`${baseUrl}/api/www/v1/article/list`, {
@@ -20,10 +42,10 @@ export default async (ctx) => {
pubDate: parseDate(item.published_at, 'YYYY-MM-DD'),
}));
- ctx.set('data', {
+ return {
title: '南方科技大学研究生院',
link,
description: '南方科技大学研招网通知公告',
item: list,
- });
-};
+ };
+}
diff --git a/lib/routes/swissinfo/index.ts b/lib/routes/swissinfo/index.ts
index 2e488f3cf..ca8c3e8fd 100644
--- a/lib/routes/swissinfo/index.ts
+++ b/lib/routes/swissinfo/index.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:language?/:category?',
+ categories: ['traditional-media'],
+ example: '/swissinfo/eng/latest-news',
+ parameters: { language: 'Language, eng by default', category: 'Category, Latest News by default' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['swissinfo.ch/:language/:category', 'swissinfo.ch/'],
+ },
+ name: 'Category',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const language = ctx.req.param('language') ?? 'eng';
const category = ctx.req.param('category') ?? 'latest-news';
@@ -66,9 +88,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title,
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/swjtu/jtys/yjs.ts b/lib/routes/swjtu/jtys/yjs.ts
index b26aaf0d5..0d594c8d5 100644
--- a/lib/routes/swjtu/jtys/yjs.ts
+++ b/lib/routes/swjtu/jtys/yjs.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -31,7 +32,25 @@ const getItem = (item, cache) => {
});
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/jtys/yjs',
+ categories: ['forecast'],
+ example: '/swjtu/jtys/yjs',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '交通运输与物流学院',
+ maintainers: ['qizidog'],
+ handler,
+};
+
+async function handler() {
const resp = await got({
method: 'get',
url: url_addr,
@@ -47,10 +66,10 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: '西南交大交运学院-研究生通知',
link: url_addr,
item: items,
allowEmpty: true,
- });
-};
+ };
+}
diff --git a/lib/routes/swjtu/jwc.ts b/lib/routes/swjtu/jwc.ts
index b6fab2468..e8a48d3ad 100644
--- a/lib/routes/swjtu/jwc.ts
+++ b/lib/routes/swjtu/jwc.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -43,7 +44,28 @@ const getItem = (item, cache) => {
});
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/jwc',
+ categories: ['forecast'],
+ example: '/swjtu/jwc',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['jwc.swjtu.edu.cn/vatuu/WebAction', 'jwc.swjtu.edu.cn/'],
+ },
+ name: '教务网',
+ maintainers: ['mobyw'],
+ handler,
+};
+
+async function handler() {
const resp = await got({
method: 'get',
url: pageURL,
@@ -59,10 +81,10 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: '西南交大-教务网通知',
link: pageURL,
item: items,
allowEmpty: true,
- });
-};
+ };
+}
diff --git a/lib/routes/swjtu/jyzpxx.ts b/lib/routes/swjtu/jyzpxx.ts
index 51f6c3482..9812c4375 100644
--- a/lib/routes/swjtu/jyzpxx.ts
+++ b/lib/routes/swjtu/jyzpxx.ts
@@ -1,10 +1,32 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import utils from './utils';
import got from '@/utils/got';
const rootURL = 'https://jiuye.swjtu.edu.cn/career';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/jyzpxx',
+ categories: ['forecast'],
+ example: '/swjtu/jyzpxx',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['jiuye.swjtu.edu.cn/career', 'jiuye.swjtu.edu.cn/'],
+ },
+ name: '就业招聘信息',
+ maintainers: ['qizidog'],
+ handler,
+};
+
+async function handler(ctx) {
const limit = Math.min(ctx.req.query('limit') ?? 10, 50);
const resp = await got({
method: 'post',
@@ -20,10 +42,10 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: '西南交大-就业招聘信息',
link: `${rootURL}/zpxx/zpxx`,
item: items,
allowEmpty: true,
- });
-};
+ };
+}
diff --git a/lib/routes/swjtu/xg.ts b/lib/routes/swjtu/xg.ts
index 311012499..4e3cf64f9 100644
--- a/lib/routes/swjtu/xg.ts
+++ b/lib/routes/swjtu/xg.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -48,7 +49,29 @@ const getItem = (item, cache) => {
});
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/xg/:code?',
+ categories: ['forecast'],
+ example: '/swjtu/xg/tzgg',
+ parameters: { code: '栏目(默认为tzgg)' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['xg.swjtu.edu.cn/web/Home/PushNewsList', 'xg.swjtu.edu.cn/web/Home/NewsList', 'xg.swjtu.edu.cn/web/Home/ColourfulCollegeNewsList', 'xg.swjtu.edu.cn/web/Publicity/List', 'xg.swjtu.edu.cn/'],
+ target: '/xg',
+ },
+ name: '扬华素质网',
+ maintainers: ['mobyw'],
+ handler,
+};
+
+async function handler(ctx) {
const code = ctx.req.param('code') ?? 'tzgg';
const pageURL = listURL[code];
@@ -71,10 +94,10 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: '西南交大-扬华素质网',
link: pageURL,
item: items,
allowEmpty: true,
- });
-};
+ };
+}
diff --git a/lib/routes/swpu/bgw.ts b/lib/routes/swpu/bgw.ts
index 5a9b629f6..a40663d9d 100644
--- a/lib/routes/swpu/bgw.ts
+++ b/lib/routes/swpu/bgw.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import { joinUrl } from './utils';
import { parseDate } from '@/utils/parse-date';
@@ -5,7 +6,29 @@ import { load } from 'cheerio';
import got from '@/utils/got';
import timezone from '@/utils/timezone';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/bgw/:code',
+ categories: ['forecast'],
+ example: '/swpu/bgw/zytzgg',
+ parameters: { code: '栏目代码' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['swpu.edu.cn/'],
+ target: '',
+ },
+ name: '办公网',
+ maintainers: ['CYTMWIA'],
+ handler,
+};
+
+async function handler(ctx) {
const url = `https://www.swpu.edu.cn/bgw2/${ctx.req.param('code')}.htm`;
const res = await got.get(url);
@@ -49,11 +72,11 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: `西南石油大学办公网 ${title}`,
link: url,
description: `西南石油大学办公网 ${title} 列表`,
language: 'zh-CN',
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/swpu/cjxy.ts b/lib/routes/swpu/cjxy.ts
index 649966d3d..c08d2bc8f 100644
--- a/lib/routes/swpu/cjxy.ts
+++ b/lib/routes/swpu/cjxy.ts
@@ -1,10 +1,33 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import { parseDate } from '@/utils/parse-date';
import { load } from 'cheerio';
import got from '@/utils/got';
import timezone from '@/utils/timezone';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/cjxy/:code',
+ categories: ['forecast'],
+ example: '/swpu/cjxy/xyxw',
+ parameters: { code: '栏目代码' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['swpu.edu.cn/'],
+ target: '',
+ },
+ name: '财经学院',
+ maintainers: ['RiverTwilight'],
+ handler,
+};
+
+async function handler(ctx) {
const url = `https://www.swpu.edu.cn/nccjxy/xydt/${ctx.req.param('code')}.htm`;
const res = await got(url);
@@ -44,11 +67,11 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `西南石油大学财经学院 ${title}`,
link: url,
description: `西南石油大学财经学院 ${title}`,
language: 'zh-CN',
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/swpu/dean.ts b/lib/routes/swpu/dean.ts
index 9cd74cbbf..eae1a7e61 100644
--- a/lib/routes/swpu/dean.ts
+++ b/lib/routes/swpu/dean.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import { joinUrl } from './utils';
import { parseDate } from '@/utils/parse-date';
@@ -5,7 +6,29 @@ import { load } from 'cheerio';
import got from '@/utils/got';
import timezone from '@/utils/timezone';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/dean/:code',
+ categories: ['forecast'],
+ example: '/swpu/dean/tzgg',
+ parameters: { code: '栏目代码' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['swpu.edu.cn/'],
+ target: '',
+ },
+ name: '教务处',
+ maintainers: ['CYTMWIA'],
+ handler,
+};
+
+async function handler(ctx) {
const url = `https://www.swpu.edu.cn/dean/${ctx.req.param('code')}.htm`;
const res = await got.get(url);
@@ -50,11 +73,11 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: `西南石油大学教务处 ${title}`,
link: url,
description: `西南石油大学教务处 ${title}`,
language: 'zh-CN',
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/swpu/dxy.ts b/lib/routes/swpu/dxy.ts
index 210a7a0d9..86534ab9a 100644
--- a/lib/routes/swpu/dxy.ts
+++ b/lib/routes/swpu/dxy.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import { joinUrl } from './utils';
import { parseDate } from '@/utils/parse-date';
@@ -5,7 +6,29 @@ import { load } from 'cheerio';
import got from '@/utils/got';
import timezone from '@/utils/timezone';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/dxy/:code',
+ categories: ['forecast'],
+ example: '/swpu/dxy/1156',
+ parameters: { code: '栏目代码' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['swpu.edu.cn/'],
+ target: '',
+ },
+ name: '电气信息学院',
+ maintainers: ['CYTMWIA'],
+ handler,
+};
+
+async function handler(ctx) {
// 移除 urltype=tree.TreeTempUrl 虽然也能顺利访问页面,
// 但标题会缺失,而且在其他地方定位提取标题也比较麻烦。
const url = `https://www.swpu.edu.cn/dxy/list1.jsp?urltype=tree.TreeTempUrl&wbtreeid=${ctx.req.param('code')}`;
@@ -52,11 +75,11 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: `西南石油大学电气信息学院 ${title}`,
link: url,
description: `西南石油大学电气信息学院 ${title}`,
language: 'zh-CN',
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/swpu/is.ts b/lib/routes/swpu/is.ts
index 2707d4370..f006cea8f 100644
--- a/lib/routes/swpu/is.ts
+++ b/lib/routes/swpu/is.ts
@@ -1,10 +1,33 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import { parseDate } from '@/utils/parse-date';
import { load } from 'cheerio';
import got from '@/utils/got';
import timezone from '@/utils/timezone';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/is/:code',
+ categories: ['forecast'],
+ example: '/swpu/is/xyxw',
+ parameters: { code: '栏目代码' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['swpu.edu.cn/'],
+ target: '',
+ },
+ name: '信息学院',
+ maintainers: ['RiverTwilight'],
+ handler,
+};
+
+async function handler(ctx) {
const url = `https://www.swpu.edu.cn/is/xydt/${ctx.req.param('code')}.htm`;
const res = await got(url);
@@ -44,11 +67,11 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `西南石油大学信息学院 ${title}`,
link: url,
description: `西南石油大学信息学院 ${title}`,
language: 'zh-CN',
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/swpu/scs.ts b/lib/routes/swpu/scs.ts
index a83553edd..28527d674 100644
--- a/lib/routes/swpu/scs.ts
+++ b/lib/routes/swpu/scs.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import { joinUrl } from './utils';
import { parseDate } from '@/utils/parse-date';
@@ -5,7 +6,29 @@ import { load } from 'cheerio';
import got from '@/utils/got';
import timezone from '@/utils/timezone';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/scs/:code',
+ categories: ['forecast'],
+ example: '/swpu/scs/tzgg',
+ parameters: { code: '栏目代码' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['swpu.edu.cn/'],
+ target: '',
+ },
+ name: '计算机科学学院',
+ maintainers: ['CYTMWIA'],
+ handler,
+};
+
+async function handler(ctx) {
const url = `https://www.swpu.edu.cn/scs/index/${ctx.req.param('code')}.htm`;
const res = await got.get(url);
@@ -49,11 +72,11 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: `西南石油大学计算机科学学院 ${title}`,
link: url,
description: `西南石油大学计算机科学学院 ${title}`,
language: 'zh-CN',
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/syosetu/chapter.ts b/lib/routes/syosetu/chapter.ts
index 2e64eb8fa..9fccb797f 100644
--- a/lib/routes/syosetu/chapter.ts
+++ b/lib/routes/syosetu/chapter.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -8,7 +9,28 @@ import { CookieJar } from 'tough-cookie';
const cookieJar = new CookieJar();
cookieJar.setCookieSync('over18=yes', 'https://novel18.syosetu.com/');
-export default async (ctx) => {
+export const route: Route = {
+ path: '/chapter/:id',
+ categories: ['government'],
+ example: '/syosetu/chapter/n1976ey',
+ parameters: { id: 'Novel id, can be found in URL' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['novel18.syosetu.com/:id'],
+ },
+ name: 'chapter',
+ maintainers: ['huangliangshusheng'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const limit = Number.parseInt(ctx.req.query('limit')) || 5;
const link = `https://ncode.syosetu.com/${id}`;
@@ -42,14 +64,14 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title,
description,
link,
language: 'ja',
item: item_list,
- });
-};
+ };
+}
const get = async (url) => {
const response = await got({
diff --git a/lib/routes/sysu/cse.ts b/lib/routes/sysu/cse.ts
index 00b129d0c..fed27ab61 100644
--- a/lib/routes/sysu/cse.ts
+++ b/lib/routes/sysu/cse.ts
@@ -1,7 +1,29 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/cse',
+ categories: ['forecast'],
+ example: '/sysu/cse',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['cse.sysu.edu.cn/'],
+ },
+ name: '数据科学与计算机学院动态',
+ maintainers: [],
+ handler,
+};
+
+async function handler() {
const response = await got({
method: 'get',
url: 'http://cse.sysu.edu.cn/',
@@ -93,11 +115,11 @@ export default async (ctx) => {
item_data.sort(compareLink);
// console.log(item_data);
- ctx.set('data', {
+ return {
title: `中山大学 - 数据科学与计算机学院`,
link: `http://cse.sysu.edu.cn`,
description: `中山大学 - 数据科学与计算机学院`,
language: `zh-cn`,
item: item_data,
- });
-};
+ };
+}
diff --git a/lib/routes/sysu/ygafz.ts b/lib/routes/sysu/ygafz.ts
index 54f9f5d8c..4e895900b 100644
--- a/lib/routes/sysu/ygafz.ts
+++ b/lib/routes/sysu/ygafz.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -6,7 +7,28 @@ import logger from '@/utils/logger';
import { CookieJar } from 'tough-cookie';
import puppeteer from '@/utils/puppeteer';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/ygafz/:type?',
+ categories: ['forecast'],
+ example: '/sysu/ygafz',
+ parameters: { type: '分类,见下表,默认为 `notice`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: true,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['ygafz.sysu.edu.cn/:type?'],
+ },
+ name: 'Unknown',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const { type = 'notice' } = ctx.req.param();
const baseUrl = 'https://ygafz.sysu.edu.cn';
const url = `${baseUrl}/${type}`;
@@ -63,9 +85,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('title').text(),
link: url,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/szse/inquire.ts b/lib/routes/szse/inquire.ts
index d2d61f16a..680e8aee1 100644
--- a/lib/routes/szse/inquire.ts
+++ b/lib/routes/szse/inquire.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -6,7 +7,29 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/inquire/:category?/:select?/:keyword?',
+ categories: ['other'],
+ example: '/szse/inquire',
+ parameters: { category: '类型,见下表,默认为 `0` 即 主板', select: '函件类别, 见下表,默认为全部函件类别', keyword: '公司代码或简称,默认为空' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['szse.cn/disclosure/supervision/inquire/index.html', 'szse.cn/'],
+ target: '/inquire',
+ },
+ name: '问询函件',
+ maintainers: ['Jeason0228', 'nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? '0';
const select = ctx.req.param('select') ?? '全部函件类别';
const keyword = ctx.req.param('keyword') ?? '';
@@ -35,10 +58,10 @@ export default async (ctx) => {
};
});
- ctx.set('data', {
+ return {
title: `深圳证券交易所 - 问询函件 - ${data.metadata.name}`,
link: `${rootUrl}/disclosure/supervision/inquire/index.html`,
item: items,
description: `函件类别:${select}${keyword ? `; 公司代码或简称:${keyword}` : ''}`,
- });
-};
+ };
+}
diff --git a/lib/routes/szse/notice.ts b/lib/routes/szse/notice.ts
index 4b777516c..f1507c2e0 100644
--- a/lib/routes/szse/notice.ts
+++ b/lib/routes/szse/notice.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import * as url from 'node:url';
const host = 'http://www.szse.cn/';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/notice',
+ categories: ['other'],
+ example: '/szse/notice',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['szse.cn/disclosure/notice/company/index.html', 'szse.cn/'],
+ },
+ name: '上市公告 - 可转换债券',
+ maintainers: ['Jeason0228', 'nczitzk'],
+ handler,
+};
+
+async function handler() {
const link = 'http://www.szse.cn/disclosure/notice/company/index.html';
const response = await got.get(link, {
Referer: host,
@@ -64,9 +86,9 @@ export default async (ctx) => {
return single;
})
);
- ctx.set('data', {
+ return {
title: '深圳证券交易所——上市公告-可转换债券',
link,
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/szse/projectdynamic.ts b/lib/routes/szse/projectdynamic.ts
index 1f017f10c..44f406721 100644
--- a/lib/routes/szse/projectdynamic.ts
+++ b/lib/routes/szse/projectdynamic.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -8,7 +9,28 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/projectdynamic/:type?/:stage?/:status?',
+ categories: ['other'],
+ example: '/szse/projectdynamic',
+ parameters: { type: '类型,见下表,默认为IPO', stage: '阶段,见下表,默认为全部', status: '状态,见下表,默认为全部' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['listing.szse.cn/projectdynamic/1/index.html', 'listing.szse.cn/projectdynamic/2/index.html', 'listing.szse.cn/projectdynamic/3/index.html', 'listing.szse.cn/'],
+ },
+ name: '创业板项目动态',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const typeMap = {
1: 'IPO',
2: '再融资',
@@ -86,9 +108,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `${typeMap[type]}项目动态${status === '0' ? (stage === '0' ? '' : ` (${stageMap[stage]}) `) : ` (${statusMap[status]}) `} - 创业板发行上市审核信息公开网站 - 深圳证券交易所`,
link: `${rootUrl}/projectdynamic/${type}/index.html`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/szse/rule.ts b/lib/routes/szse/rule.ts
index e2b4ab757..bcea390aa 100644
--- a/lib/routes/szse/rule.ts
+++ b/lib/routes/szse/rule.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/rule',
+ categories: ['other'],
+ example: '/szse/rule',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['szse.cn/lawrules/rule/new', 'szse.cn/'],
+ },
+ name: '最新规则',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler() {
const rootUrl = 'http://www.szse.cn';
const currentUrl = `${rootUrl}/api/search/content`;
@@ -44,9 +66,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: '最新规则 - 深圳证券交易所',
link: `${rootUrl}/lawrules/rule/new`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/szu/yz/index.ts b/lib/routes/szu/yz/index.ts
index 81ddf1278..e03d0e3c5 100644
--- a/lib/routes/szu/yz/index.ts
+++ b/lib/routes/szu/yz/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -8,7 +9,25 @@ const map = new Map([
[2, { title: '博士招生 - 深圳大学研究生招生网' }],
]);
-export default async (ctx) => {
+export const route: Route = {
+ path: '/yz/:type?',
+ categories: ['forecast'],
+ example: '/szu/yz/1',
+ parameters: { type: '默认为 `1`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '研究生招生网',
+ maintainers: ['NagaruZ'],
+ handler,
+};
+
+async function handler(ctx) {
let type = Number.parseInt(ctx.req.param('type'));
const struct = {
1: {
@@ -43,10 +62,10 @@ export default async (ctx) => {
const name = $('title').text();
const result = await util.ProcessFeed(list, cache, struct[type]);
- ctx.set('data', {
+ return {
title: map.get(type).title,
link: url,
description: name,
item: result,
- });
-};
+ };
+}
diff --git a/lib/routes/t66y/index.ts b/lib/routes/t66y/index.ts
index e4384cc6c..20bb206c8 100644
--- a/lib/routes/t66y/index.ts
+++ b/lib/routes/t66y/index.ts
@@ -1,10 +1,29 @@
+import { Route } from '@/types';
import * as cheerio from 'cheerio';
import got from '@/utils/got';
import cache from '@/utils/cache';
import { parseDate } from '@/utils/parse-date';
import { baseUrl, parseContent } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:id/:type?',
+ categories: ['picture'],
+ example: '/t66y/20/2',
+ parameters: { id: '分区 id, 可在分区页 URL 中找到', type: '类型 id, 可在分区类型过滤后的 URL 中找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '分区帖子',
+ maintainers: ['zhboner'],
+ handler,
+};
+
+async function handler(ctx) {
const { id, type } = ctx.req.param();
const url = new URL(`thread0806.php?fid=${id}&search=today`, baseUrl);
@@ -47,9 +66,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: (type ? `[${$('.t .fn b').text()}]` : '') + $('head title').text(),
link: url.href,
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/t66y/post.ts b/lib/routes/t66y/post.ts
index 87cb3916e..16f73f7bb 100644
--- a/lib/routes/t66y/post.ts
+++ b/lib/routes/t66y/post.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import * as cheerio from 'cheerio';
import got from '@/utils/got';
import cache from '@/utils/cache';
@@ -29,7 +30,25 @@ function parseItems(tid: string, $: cheerio.CheerioAPI) {
});
}
-export default async (ctx) => {
+export const route: Route = {
+ path: '/post/:tid',
+ categories: ['picture'],
+ example: '/t66y/post/3286088',
+ parameters: { tid: '帖子 id, 可在帖子 URL 中找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '帖子跟踪',
+ maintainers: ['cnzgray'],
+ handler,
+};
+
+async function handler(ctx) {
const tid = ctx.req.param('tid') as string;
const { data: response } = await got(`${baseUrl}/read.php?tid=${tid}`);
// 跟踪重定向
@@ -67,9 +86,9 @@ export default async (ctx) => {
const items = [...firstPage, ...nextPages.flat()];
- ctx.set('data', {
+ return {
title: $('head title').text(),
link,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/tableau/viz-of-the-day.ts b/lib/routes/tableau/viz-of-the-day.ts
index 8dfa559d0..a0226bcf4 100644
--- a/lib/routes/tableau/viz-of-the-day.ts
+++ b/lib/routes/tableau/viz-of-the-day.ts
@@ -1,8 +1,27 @@
+import { Route } from '@/types';
import got from '@/utils/got';
// import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/viz-of-the-day',
+ categories: ['journal'],
+ example: '/tableau/viz-of-the-day',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Viz of the day',
+ maintainers: [],
+ handler,
+};
+
+async function handler() {
// Your logic here
const rootUrl = 'https://public.tableau.com/api/gallery?page=0&count=20&galleryType=viz-of-the-day';
const { data: response } = await got(rootUrl);
@@ -16,11 +35,11 @@ export default async (ctx) => {
itunes_item_image: item.screenshot,
}));
- ctx.set('data', {
+ return {
// Your RSS output here
title: 'Tableau Viz of the Day',
link: 'https://public.tableau.com/app/discover/viz-of-the-day',
image: 'https://help.tableau.com/current/pro/desktop/en-us/Resources/tableau-logo.png',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/taiwannews/hot.ts b/lib/routes/taiwannews/hot.ts
index 14dd0bc2c..58e1e7ba4 100644
--- a/lib/routes/taiwannews/hot.ts
+++ b/lib/routes/taiwannews/hot.ts
@@ -1,10 +1,33 @@
+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';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/hot/:lang?',
+ categories: ['bbs'],
+ example: '/taiwannews/hot',
+ parameters: { lang: 'Language, `en` or `zh`, `en` by default' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['taiwannews.com.tw/:lang/index'],
+ target: '/hot/:lang',
+ },
+ name: 'Hot News',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const baseUrl = 'https://www.taiwannews.com.tw';
const { lang = 'en' } = ctx.req.param();
const url = `${baseUrl}/${lang}/index`;
@@ -44,11 +67,11 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `${$('.categories').eq(0).text()} - ${$('head title').text()}`,
description: $('meta[name="description"]').attr('content'),
link: url,
item: items,
language: lang,
- });
-};
+ };
+}
diff --git a/lib/routes/tangshufang/index.ts b/lib/routes/tangshufang/index.ts
index 92368c7b2..3c655ef0d 100644
--- a/lib/routes/tangshufang/index.ts
+++ b/lib/routes/tangshufang/index.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:category?',
+ categories: ['traditional-media'],
+ example: '/tangshufang',
+ parameters: { category: '分类,见下表,默认为首页' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['tangshufang.com/:category', 'tangshufang.com/'],
+ },
+ name: '分类',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? '';
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 10;
@@ -52,9 +74,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('title').text(),
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/taobao/zhongchou.ts b/lib/routes/taobao/zhongchou.ts
index f6bee5020..1320a03ff 100644
--- a/lib/routes/taobao/zhongchou.ts
+++ b/lib/routes/taobao/zhongchou.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -5,7 +6,25 @@ import got from '@/utils/got';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/zhongchou/:type?',
+ categories: ['game'],
+ example: '/taobao/zhongchou/all',
+ parameters: { type: '类型, 默认为 `all` 全部' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '众筹项目',
+ maintainers: ['xyqfer', 'Fatpandac'],
+ handler,
+};
+
+async function handler(ctx) {
const { type = 'all' } = ctx.req.param();
const map = {
all: '',
@@ -41,9 +60,9 @@ export default async (ctx) => {
};
});
- ctx.set('data', {
+ return {
title: `淘宝众筹-${type}`,
link: 'https://izhongchou.taobao.com/index.htm',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/taoguba/blog.ts b/lib/routes/taoguba/blog.ts
index 43b4690e1..84149ae46 100644
--- a/lib/routes/taoguba/blog.ts
+++ b/lib/routes/taoguba/blog.ts
@@ -1,10 +1,32 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: ['/blog/:id', '/user/:id'],
+ categories: ['other'],
+ example: '/taoguba/blog/252069',
+ parameters: { id: '博客 id,可在对应博客页中找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['taoguba.com.cn/blog/:id', 'taoguba.com.cn/'],
+ },
+ name: '用户博客',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const rootUrl = 'https://www.taoguba.com.cn';
@@ -75,11 +97,11 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `淘股吧 - ${author}`,
description: $('meta[http-equiv="description"]').attr('content'),
image: $('meta[property="og:image"]').attr('content'),
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/taoguba/index.ts b/lib/routes/taoguba/index.ts
index 67f8349c1..26dd624a5 100644
--- a/lib/routes/taoguba/index.ts
+++ b/lib/routes/taoguba/index.ts
@@ -1,10 +1,18 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: ['/index', '/:category?'],
+ name: 'Unknown',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? 'zongban';
const rootUrl = 'https://www.taoguba.com.cn';
@@ -75,9 +83,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('head title').text().trim().split('_')[0],
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/taptap/changelog.ts b/lib/routes/taptap/changelog.ts
index 979564505..e3b32c1d3 100644
--- a/lib/routes/taptap/changelog.ts
+++ b/lib/routes/taptap/changelog.ts
@@ -1,8 +1,31 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { getRootUrl, appDetail, X_UA } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: ['/changelog/:id/:lang?', '/intl/changelog/:id/:lang?'],
+ categories: ['reading'],
+ example: '/taptap/changelog/60809/en_US',
+ parameters: { id: '游戏 ID,游戏主页 URL 中获取', lang: '语言,默认使用 `zh_CN`,亦可使用 `en_US`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['taptap.com/app/:id'],
+ target: '/changelog/:id',
+ },
+ name: '游戏更新',
+ maintainers: ['hoilc', 'ETiV'],
+ handler,
+};
+
+async function handler(ctx) {
const is_intl = ctx.req.url.indexOf('/intl/') === 0;
const id = ctx.req.param('id');
const lang = ctx.req.param('lang') ?? (is_intl ? 'en_US' : 'zh_CN');
@@ -25,7 +48,7 @@ export default async (ctx) => {
const list = response.data.data.list;
- ctx.set('data', {
+ return {
title: `TapTap 更新记录 ${app_name}`,
description: app_description,
link: url,
@@ -37,5 +60,5 @@ export default async (ctx) => {
link: url,
guid: item.version_label,
})),
- });
-};
+ };
+}
diff --git a/lib/routes/taptap/review.ts b/lib/routes/taptap/review.ts
index 4e4a068db..c9e26fe08 100644
--- a/lib/routes/taptap/review.ts
+++ b/lib/routes/taptap/review.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { getRootUrl, appDetail, X_UA } from './utils';
@@ -103,7 +104,29 @@ const fetchIntlItems = async (params) => {
});
};
-export default async (ctx) => {
+export const route: Route = {
+ path: ['/review/:id/:order?/:lang?', '/intl/review/:id/:order?/:lang?'],
+ categories: ['reading'],
+ example: '/taptap/review/142793/hot',
+ parameters: { id: '游戏 ID,游戏主页 URL 中获取', order: '排序方式,空为默认排序,可选如下', lang: '语言,`zh-CN`或`zh-TW`,默认为`zh-CN`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['taptap.com/app/:id/review', 'taptap.com/app/:id'],
+ target: '/review/:id',
+ },
+ name: '游戏评价',
+ maintainers: ['hoilc', 'TonyRL'],
+ handler,
+};
+
+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';
@@ -115,12 +138,12 @@ export default async (ctx) => {
const items = is_intl ? await fetchIntlItems(ctx.params) : await fetchMainlandItems(ctx.params);
- ctx.set('data', {
+ return {
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', {
title: `TapTap 评价 ${app_name} - ${(is_intl ? intlSortMap : sortMap)[order][lang]}排序`,
@@ -128,4 +151,4 @@ export default async (ctx) => {
image: app_img,
item: items,
});
-};
+}
diff --git a/lib/routes/taptap/topic.ts b/lib/routes/taptap/topic.ts
index 1fb97879b..9a944b96b 100644
--- a/lib/routes/taptap/topic.ts
+++ b/lib/routes/taptap/topic.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -22,7 +23,29 @@ const typeMap = {
},
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/topic/:id/:type?/:sort?/:lang?',
+ categories: ['reading'],
+ example: '/taptap/topic/142793/official',
+ parameters: { id: '游戏 ID,游戏主页 URL 中获取', type: '论坛版块,默认显示所有帖子,论坛版块 URL 中 `type` 参数,见下表,默认为 `feed`', sort: '排序,见下表,默认为 `created`', lang: '语言,`zh-CN`或`zh-TW`,默认为`zh-CN`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['taptap.com/app/:id/topic', 'taptap.com/app/:id'],
+ target: '/topic/:id',
+ },
+ name: '游戏论坛',
+ maintainers: ['hoilc', 'TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const lang = ctx.req.param('lang') ?? 'zh_CN';
const appId = ctx.req.param('id');
const appData = await appDetail(appId, lang);
@@ -67,12 +90,12 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: `${app_name} - ${typeMap[type][lang]} - TapTap 论坛`,
link: `${getRootUrl(false)}/app/${appId}/topic?type=${type}&sort=${sort}`,
image: app_img,
item: out.filter((item) => item !== ''),
- });
+ };
ctx.set('json', {
title: `${app_name} - ${typeMap[type][lang]} - TapTap 论坛`,
@@ -83,4 +106,4 @@ export default async (ctx) => {
groupId,
topics_list,
});
-};
+}
diff --git a/lib/routes/tass/news.ts b/lib/routes/tass/news.ts
index 22d62f19e..cbad48961 100644
--- a/lib/routes/tass/news.ts
+++ b/lib/routes/tass/news.ts
@@ -1,9 +1,32 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:category?',
+ categories: ['bbs'],
+ example: '/tass/politics',
+ parameters: { category: 'Category, can be found in URL, `politics` by default' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['tass.com/:category'],
+ target: '/:category',
+ },
+ name: 'News',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const { category = 'politics' } = ctx.req.param();
const { data: categoryPage, url: link } = await got(`https://tass.com/${category}`);
@@ -47,7 +70,7 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('head title').text(),
link,
language: 'en',
@@ -55,5 +78,5 @@ export default async (ctx) => {
icon: $('head link[rel="apple-touch-icon"]').attr('href'),
logo: $('head link[rel="apple-touch-icon"]').attr('href'),
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/techcrunch/news.ts b/lib/routes/techcrunch/news.ts
index ca0697bb0..cd4d6b007 100644
--- a/lib/routes/techcrunch/news.ts
+++ b/lib/routes/techcrunch/news.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import parser from '@/utils/rss-parser';
import got from '@/utils/got';
import { load } from 'cheerio';
const host = 'https://techcrunch.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news',
+ categories: ['traditional-media'],
+ example: '/techcrunch/news',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['techcrunch.com/'],
+ },
+ name: 'News',
+ maintainers: ['EthanWng97'],
+ handler,
+};
+
+async function handler() {
const rssUrl = `${host}/feed/`;
const feed = await parser.parseURL(rssUrl);
const items = await Promise.all(
@@ -30,10 +52,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: 'TechCrunch',
link: host,
description: 'Reporting on the business of technology, startups, venture capital funding, and Silicon Valley.',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/techflowpost/express.ts b/lib/routes/techflowpost/express.ts
index cf2270a8e..1a95bbc4e 100644
--- a/lib/routes/techflowpost/express.ts
+++ b/lib/routes/techflowpost/express.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
import dayjs from 'dayjs';
-export default async (ctx) => {
+export const route: Route = {
+ path: ['/express', '/newsflash'],
+ categories: ['other'],
+ example: '/techflowpost/express',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['techflowpost.com/newsletter/index.html'],
+ },
+ name: '快讯',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const rootUrl = 'https://www.techflowpost.com';
const currentUrl = `${rootUrl}/newsletter/index.html`;
@@ -23,9 +45,9 @@ export default async (ctx) => {
description: item.scontent,
}));
- ctx.set('data', {
+ return {
title: '深潮TechFlow - 快讯',
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/techflowpost/index.ts b/lib/routes/techflowpost/index.ts
index 3ee9370e8..5361a070d 100644
--- a/lib/routes/techflowpost/index.ts
+++ b/lib/routes/techflowpost/index.ts
@@ -1,8 +1,20 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/',
+ radar: {
+ source: ['techflowpost.com/'],
+ target: '',
+ },
+ name: 'Unknown',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const rootUrl = 'https://www.techflowpost.com';
const { data: response } = await got.post('https://www.techflowpost.com/ashx/index.ashx', {
@@ -22,9 +34,9 @@ export default async (ctx) => {
description: item.scontent,
}));
- ctx.set('data', {
+ return {
title: '深潮TechFlow',
link: rootUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/techpowerup/index.ts b/lib/routes/techpowerup/index.ts
index be235b4cd..4ff7c1878 100644
--- a/lib/routes/techpowerup/index.ts
+++ b/lib/routes/techpowerup/index.ts
@@ -1,10 +1,22 @@
+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 { baseUrl, headers, fixImages, parseReviews } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/',
+ radar: {
+ source: ['techpowerup.com/'],
+ target: '',
+ },
+ name: 'Unknown',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler() {
const { data: response } = await got(baseUrl, {
headers,
});
@@ -60,11 +72,11 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: 'TechPowerUp',
link: baseUrl,
language: 'en',
image: 'https://tpucdn.com/apple-touch-icon-v1684568903519.png',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/techpowerup/review.ts b/lib/routes/techpowerup/review.ts
index 27250e63c..3b0fcad8c 100644
--- a/lib/routes/techpowerup/review.ts
+++ b/lib/routes/techpowerup/review.ts
@@ -1,10 +1,33 @@
+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 { baseUrl, headers, fixImages, parseReviews } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/review/:keyword?',
+ categories: ['traditional-media'],
+ example: '/techpowerup/review/4090',
+ parameters: { keyword: 'Search Keyword' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['techpowerup.com/'],
+ target: '',
+ },
+ name: 'Reviews',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const keyword = ctx.req.param('keyword');
const url = new URL(`${baseUrl}/review/${keyword ? 'search/' : ''}`);
@@ -60,11 +83,11 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: 'Reviews | TechPowerUp',
link: url.href,
language: 'en',
image: 'https://tpucdn.com/apple-touch-icon-v1684568903519.png',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/telecompaper/news.ts b/lib/routes/telecompaper/news.ts
index 9f8a35aab..a7a68623c 100644
--- a/lib/routes/telecompaper/news.ts
+++ b/lib/routes/telecompaper/news.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -5,7 +6,30 @@ import tough from 'tough-cookie';
// eslint-disable-next-line n/no-extraneous-require
import FormData from 'form-data';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/:caty/:year?/:country?/:type?',
+ categories: ['finance'],
+ example: '/telecompaper/news/mobile/2020/China/News',
+ parameters: {
+ caty: 'Category, see table below',
+ year: 'Year. The year in respective category page filter, `all` for unlimited year, empty by default',
+ country: 'Country or continent, `all` for unlimited country or continent, empty by default',
+ type: 'Type, can be found in the `Types` filter, `all` for unlimited type, unlimited by default',
+ },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'News',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const rootUrl = `https://www.telecompaper.com/${ctx.req.param('caty') === 'industry-resources' ? ctx.req.param('caty') : 'international/news/' + ctx.req.param('caty')}`;
const year = ctx.req.param('year') ?? 'all';
const country = ctx.req.param('country') ? ctx.req.param('country').split('-').join(' ') : 'all';
@@ -94,9 +118,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: 'Telecompaper - ' + $('h1').text(),
link: rootUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/telecompaper/search.ts b/lib/routes/telecompaper/search.ts
index d8fb9beb6..69b78fb4c 100644
--- a/lib/routes/telecompaper/search.ts
+++ b/lib/routes/telecompaper/search.ts
@@ -1,8 +1,27 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/search/:keyword?/:company?/:sort?/:period?',
+ categories: ['finance'],
+ example: '/telecompaper/search/Nokia',
+ parameters: { keyword: 'Keyword', company: 'Company name, empty by default', sort: 'Sorting, see table below, `Date Descending` by default', period: 'Date selection, Last 12 months by default' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Search',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const keyword = ctx.req.param('keyword') || '';
const company = ctx.req.param('company') || '';
const sort = ctx.req.param('sort') || '2';
@@ -69,9 +88,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `Telecompaper Search - ${keyword}`,
link: rootUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/telegram/blog.ts b/lib/routes/telegram/blog.ts
index af54242bc..1c073aa46 100644
--- a/lib/routes/telegram/blog.ts
+++ b/lib/routes/telegram/blog.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import cherrio from 'cheerio';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/blog',
+ categories: ['new-media'],
+ example: '/telegram/blog',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['telegram.org/blog'],
+ },
+ name: 'Telegram Blog',
+ maintainers: ['fengkx'],
+ handler,
+};
+
+async function handler() {
const link = 'https://telegram.org/blog';
const res = await got(link);
@@ -28,9 +50,9 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: $$('title').text(),
link,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/telegram/channel.ts b/lib/routes/telegram/channel.ts
index e41b05968..f504e66f2 100644
--- a/lib/routes/telegram/channel.ts
+++ b/lib/routes/telegram/channel.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -54,7 +55,29 @@ const mediaTagDict = {
PARTIALLY_UNSUPPORTED: ['', ''],
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/channel/:username/:routeParams?',
+ categories: ['new-media'],
+ example: '/telegram/channel/awesomeDIYgod/searchQuery=twitter',
+ parameters: { username: 'channel username', routeParams: 'extra parameters, see the table below' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['t.me/s/:username'],
+ target: '/channel/:username',
+ },
+ name: 'Channel',
+ maintainers: ['DIYgod', 'Rongronggg9'],
+ handler,
+};
+
+async function handler(ctx) {
const useWeb = ctx.req.param('routeParams') || !config.telegram.session;
if (!useWeb) {
return tglibchannel(ctx);
@@ -116,7 +139,7 @@ export default async (ctx) => {
const channelName = $('.tgme_channel_info_header_title').text();
const feedTitle = (searchQuery ? `"${searchQuery}" - ` : '') + channelName + ' - Telegram Channel';
- ctx.set('data', {
+ return {
title: feedTitle,
description: $('.tgme_channel_info_description').text(),
link: resourceUrl,
@@ -598,5 +621,5 @@ export default async (ctx) => {
.get()
.filter(Boolean)
.reverse(),
- });
-};
+ };
+}
diff --git a/lib/routes/telegram/stickerpack.ts b/lib/routes/telegram/stickerpack.ts
index a06a61275..9e8e6fed2 100644
--- a/lib/routes/telegram/stickerpack.ts
+++ b/lib/routes/telegram/stickerpack.ts
@@ -1,7 +1,26 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { config } from '@/config';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/stickerpack/:name',
+ categories: ['new-media'],
+ example: '/telegram/stickerpack/DIYgod',
+ parameters: { name: 'Sticker Pack name, available in the sharing URL' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Sticker Pack',
+ maintainers: ['DIYgod'],
+ handler,
+};
+
+async function handler(ctx) {
if (!config.telegram || !config.telegram.token) {
throw new Error('Telegram Sticker Pack RSS is disabled due to the lack of relevant config');
}
@@ -14,7 +33,7 @@ export default async (ctx) => {
const data = response.data.result;
- ctx.set('data', {
+ return {
title: `${data.title} - Telegram Sticker Pack`,
link: `https://t.me/addstickers/${name}`,
item: data.stickers.map((item) => ({
@@ -22,5 +41,5 @@ export default async (ctx) => {
description: item.file_id,
guid: item.file_id,
})),
- });
-};
+ };
+}
diff --git a/lib/routes/telegram/tglib/channel.ts b/lib/routes/telegram/tglib/channel.ts
index b31f7f2c0..41c61e503 100644
--- a/lib/routes/telegram/tglib/channel.ts
+++ b/lib/routes/telegram/tglib/channel.ts
@@ -152,14 +152,14 @@ export default async (ctx) => {
}
}
- ctx.set('data', {
+ return {
title: channelInfo.title,
language: null,
link: `https://t.me/${channelInfo.username}`,
item,
allowEmpty: ctx.req.param('id') === 'allow_empty',
description: `@${channelInfo.username} on Telegram`,
- });
+ };
};
export { getMedia };
diff --git a/lib/routes/tencent/news/author.ts b/lib/routes/tencent/news/author.ts
index 1f951195a..1edf47ff3 100644
--- a/lib/routes/tencent/news/author.ts
+++ b/lib/routes/tencent/news/author.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -9,7 +10,28 @@ import { config } from '@/config';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/author/:mid',
+ categories: ['traditional-media'],
+ example: '/tencent/news/author/5933889',
+ parameters: { mid: '企鹅号 ID' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['new.qq.com/omn/author/:mid'],
+ },
+ name: '更新',
+ maintainers: ['LogicJake', 'miles170'],
+ handler,
+};
+
+async function handler(ctx) {
const mid = ctx.req.param('mid');
const homePageInfoUrl = `https://i.news.qq.com/i/getUserHomepageInfo?chlid=${mid}`;
const userInfo = await cache.tryGet(homePageInfoUrl, async () => (await got(homePageInfoUrl)).data.userinfo);
@@ -67,10 +89,10 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title,
description,
link: `https://new.qq.com/omn/author/${mid}`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/tencent/news/coronavirus/data.ts b/lib/routes/tencent/news/coronavirus/data.ts
index 9a009f61d..269703665 100644
--- a/lib/routes/tencent/news/coronavirus/data.ts
+++ b/lib/routes/tencent/news/coronavirus/data.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -6,7 +7,14 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/coronavirus/data/:province?/:city?',
+ name: 'Unknown',
+ maintainers: ['CaoMeiYouRen'],
+ handler,
+};
+
+async function handler(ctx) {
const province = ctx.req.param('province') || '';
const city = ctx.req.param('city') || '';
@@ -66,9 +74,9 @@ export default async (ctx) => {
item.push(info);
- ctx.set('data', {
+ return {
title,
link,
item,
- });
-};
+ };
+}
diff --git a/lib/routes/tencent/news/coronavirus/total.ts b/lib/routes/tencent/news/coronavirus/total.ts
index 6c56690e8..2053e0b79 100644
--- a/lib/routes/tencent/news/coronavirus/total.ts
+++ b/lib/routes/tencent/news/coronavirus/total.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -6,7 +7,17 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/coronavirus/total',
+ radar: {
+ source: ['new.qq.com/zt2020/page/feiyan.htm'],
+ },
+ name: 'Unknown',
+ maintainers: ['CaoMeiYouRen'],
+ handler,
+};
+
+async function handler() {
const title = '腾讯新闻 - 新型冠状病毒肺炎疫情实时追踪';
const link = 'https://news.qq.com/zt2020/page/feiyan.htm#/';
const item = [];
@@ -29,9 +40,9 @@ export default async (ctx) => {
};
item.push(info);
- ctx.set('data', {
+ return {
title,
link,
item,
- });
-};
+ };
+}
diff --git a/lib/routes/tencent/pvp/newsindex.ts b/lib/routes/tencent/pvp/newsindex.ts
index 59acf790c..7236ad667 100644
--- a/lib/routes/tencent/pvp/newsindex.ts
+++ b/lib/routes/tencent/pvp/newsindex.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
const map = new Map([
@@ -36,7 +37,25 @@ const getPage = async (id, typeName) => {
);
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/pvp/newsindex/:type',
+ categories: ['reading'],
+ example: '/tencent/pvp/newsindex/all',
+ parameters: { type: '栏目分类,见下表' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '新闻中心',
+ maintainers: ['Jeason0228', 'HenryQW'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type');
const OutName = map.get(type).name;
const OutId = map.get(type).channelid;
@@ -57,10 +76,10 @@ export default async (ctx) => {
item = await getPage(OutId, OutName);
}
- ctx.set('data', {
+ return {
title: `【${OutName}】 - 王者荣耀 - 新闻列表`,
link,
description: `《王者荣耀》是腾讯天美工作室历时3年推出的东方英雄即时对战手游大作,抗塔强杀、团灭超神,领略爽热血竞技的酣畅淋漓!1v1、3v3、闯关等丰富游戏模式,随时战,更自由!跨服匹配秒开局,好友组队战排位,不靠装备、没有等级,更公平、更爽快的无差异对战!`,
item,
- });
-};
+ };
+}
diff --git a/lib/routes/tencent/qq/sdk/changelog.ts b/lib/routes/tencent/qq/sdk/changelog.ts
index 9ce663461..b788d19df 100644
--- a/lib/routes/tencent/qq/sdk/changelog.ts
+++ b/lib/routes/tencent/qq/sdk/changelog.ts
@@ -1,7 +1,26 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/qq/sdk/changelog/:platform',
+ categories: ['university'],
+ example: '/tencent/qq/sdk/changelog/iOS',
+ parameters: { platform: '平台,iOS / Android' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '更新日志',
+ maintainers: ['nuomi1'],
+ handler,
+};
+
+async function handler(ctx) {
const platform = ctx.req.param('platform');
let title = '';
@@ -48,9 +67,9 @@ export default async (ctx) => {
return changelog;
});
- ctx.set('data', {
+ return {
title,
link,
item: changelogs,
- });
-};
+ };
+}
diff --git a/lib/routes/tesla/cx.ts b/lib/routes/tesla/cx.ts
index f19cf9e61..1e88f6a14 100644
--- a/lib/routes/tesla/cx.ts
+++ b/lib/routes/tesla/cx.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -8,7 +9,25 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/cx/:category?/:city?',
+ categories: ['game'],
+ example: '/tesla/cx/生活方式/北京',
+ parameters: { category: '分类,见下表,默认为空,即全部', city: '城市,默认为空,即全国' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '权益中心',
+ maintainers: ['simonsmh', 'nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const { category, city } = ctx.req.param();
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 10;
@@ -107,7 +126,7 @@ export default async (ctx) => {
const description = `${city ?? ''}${category ?? ''}`;
const icon = new URL($('link[rel="icon"]').prop('href'), rootUrl).href;
- ctx.set('data', {
+ return {
item: items,
title: `${author}权益中心${description ? ` - ${description}` : ''}`,
link: currentUrl,
@@ -119,5 +138,5 @@ export default async (ctx) => {
subtitle: description,
author,
allowEmpty: true,
- });
-};
+ };
+}
diff --git a/lib/routes/tesla/price/index.ts b/lib/routes/tesla/price/index.ts
index 8631a2f40..0ef3a9e6f 100644
--- a/lib/routes/tesla/price/index.ts
+++ b/lib/routes/tesla/price/index.ts
@@ -1,6 +1,28 @@
+import { Route } from '@/types';
import { getTeslaPrice } from './get-price';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/price',
+ categories: ['game'],
+ example: '/tesla/price',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['tesla.cn/model3/design', 'tesla.cn/'],
+ },
+ name: '价格',
+ maintainers: ['xiaokyo'],
+ handler,
+};
+
+async function handler() {
const cars = [
{
name: 'Model 3',
@@ -27,7 +49,7 @@ export default async (ctx) => {
const promises = cars.map((car) => getTeslaPrice(car.link));
const prices = await Promise.all(promises);
- ctx.set('data', {
+ return {
title: 'Tesla Model 系列价格更新',
link: 'https://www.tesla.cn/model3/design#overview',
description: 'Tesla Model 系列价格更新',
@@ -37,5 +59,5 @@ export default async (ctx) => {
author: 'Tesla',
guid: `https://www.tesla.cn/${cars[index].slug}/design#overview#${price}`,
})),
- });
-};
+ };
+}
diff --git a/lib/routes/test/index.ts b/lib/routes/test/index.ts
index 4d249c8f2..52c884ea0 100644
--- a/lib/routes/test/index.ts
+++ b/lib/routes/test/index.ts
@@ -1,12 +1,19 @@
+import { Route, DataItem } from '@/types';
import { config } from '@/config';
import got from '@/utils/got';
import wait from '@/utils/wait';
import cache from '@/utils/cache';
-import { DataItem } from '@/types';
let cacheIndex = 0;
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:id',
+ name: 'Unknown',
+ maintainers: ['DIYgod', 'NeverBehave'],
+ handler,
+};
+
+async function handler(ctx) {
if (ctx.req.param('id') === 'error') {
throw new Error('Error test');
}
@@ -373,7 +380,7 @@ export default async (ctx) => {
];
}
- ctx.set('data', {
+ return {
title: `Test ${ctx.req.param('id')}`,
itunes_author: ctx.req.param('id') === 'enclosure' ? 'DIYgod' : null,
link: 'https://github.com/DIYgod/RSSHub',
@@ -381,5 +388,5 @@ export default async (ctx) => {
allowEmpty: ctx.req.param('id') === 'allow_empty',
description:
ctx.req.param('id') === 'complicated' ? ' ${item.target.excerpt}' : ctx.req.param('id') === 'multimedia' ? '' : 'A test route for RSSHub',
- });
-};
+ };
+}
diff --git a/lib/routes/tfc-taiwan/index.ts b/lib/routes/tfc-taiwan/index.ts
index 87becb498..b2ab829eb 100644
--- a/lib/routes/tfc-taiwan/index.ts
+++ b/lib/routes/tfc-taiwan/index.ts
@@ -1,9 +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';
-export default async (ctx) => {
+export const route: Route = {
+ path: ['/', '/category/:id{.+}', '/info', '/report', '/topic/:id'],
+ name: 'Unknown',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const requestPath = ctx.req.path;
const isTopic = requestPath.startsWith('/topic/');
let link = baseUrl;
@@ -25,7 +33,7 @@ export default async (ctx) => {
const items = await parseItems(list, cache.tryGet);
- ctx.set('data', {
+ return {
title: $('head title').text(),
description: $('head meta[name="description"]').attr('content'),
image: $('head meta[property="og:image"]').attr('content'),
@@ -33,5 +41,5 @@ export default async (ctx) => {
icon: $('head link[rel="shortcut icon"]').attr('href'),
link,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/theatlantic/news.ts b/lib/routes/theatlantic/news.ts
index 135d111ae..f694e54d8 100644
--- a/lib/routes/theatlantic/news.ts
+++ b/lib/routes/theatlantic/news.ts
@@ -1,7 +1,29 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { getArticleDetails } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:category',
+ categories: ['bbs'],
+ example: '/theatlantic/latest',
+ parameters: { category: 'category, see below' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.theatlantic.com/:category'],
+ },
+ name: 'News',
+ maintainers: ['EthanWng97'],
+ handler,
+};
+
+async function handler(ctx) {
const host = 'https://www.theatlantic.com';
const category = ctx.req.param('category');
const url = `${host}/${category}/`;
@@ -23,10 +45,10 @@ export default async (ctx) => {
});
const items = await getArticleDetails(list);
- ctx.set('data', {
+ return {
title: `The Atlantic - ${category.toUpperCase()}`,
link: url,
description: `The Atlantic - ${category.toUpperCase()}`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/theblockbeats/index.ts b/lib/routes/theblockbeats/index.ts
index 4055513c0..80bf5aaa6 100644
--- a/lib/routes/theblockbeats/index.ts
+++ b/lib/routes/theblockbeats/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -20,7 +21,25 @@ const channelMap = {
},
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:channel?',
+ categories: ['other'],
+ example: '/theblockbeats/newsflash',
+ parameters: { channel: '类型,见下表,默认为快讯' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '新闻快讯',
+ maintainers: ['Fatpandac', 'jameshih'],
+ handler,
+};
+
+async function handler(ctx) {
const { channel = 'newsflash' } = ctx.req.param();
const { data: response } = await got(channelMap[channel].api);
@@ -46,9 +65,9 @@ export default async (ctx) => {
);
}
- ctx.set('data', {
+ return {
title: `TheBlockBeats - ${channelMap[channel].title}`,
link: channelMap[channel].link,
item: list,
- });
-};
+ };
+}
diff --git a/lib/routes/thecatcity/index.ts b/lib/routes/thecatcity/index.ts
index cd4b12a80..aeaf5808b 100644
--- a/lib/routes/thecatcity/index.ts
+++ b/lib/routes/thecatcity/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -5,7 +6,29 @@ import { termsMap } from './terms-map';
const baseUrl = 'https://thecatcity.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:term?',
+ categories: ['traditional-media'],
+ example: '/thecatcity',
+ parameters: { term: '見下表,留空為全部文章' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['thecatcity.com/'],
+ target: '',
+ },
+ name: '分類',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const term = ctx.req.param('term');
const { data } = await got(`${baseUrl}/node_api/v1/articles/posts`, {
searchParams: {
@@ -39,11 +62,11 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: termsMap[term] ? termsMap[term].title : termsMap[''].title,
description: '提供貓咪日常照顧、新手準備、貓用品、貓咪醫療、貓飲食與行為等相關知識,以及療癒貓影片、貓趣聞、貓小物流行資訊,不論你是貓奴、還是貓控,一切所需都在貓奴日常找到',
link: baseUrl,
image: 'https://assets.presslogic.com/presslogic-hk-tc/static/favicon.ico',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/thecover/channel.ts b/lib/routes/thecover/channel.ts
index 87caf6c0f..60fcb01d6 100644
--- a/lib/routes/thecover/channel.ts
+++ b/lib/routes/thecover/channel.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -23,7 +24,25 @@ const nodes = {
17: '封面号',
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/channel/:id?',
+ categories: ['traditional-media'],
+ example: '/thecover/channel/3560',
+ parameters: { id: '对应id,可在频道链接中获取,默认为3892' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '频道',
+ maintainers: ['yuxinliu-alex'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id') ?? '3892';
const targetUrl = rootUrl.concat(`/channel_${id}`);
const resp = await got({
@@ -57,11 +76,11 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `${nodes[id]}-封面新闻`,
link: targetUrl,
description: `封面新闻作为华西都市报深度融合转型和打造新型主流媒体的载体,牢固确立移动优先战略,创新移动新闻产品,打造移动传播矩阵,封面新闻的传播力、引导力、影响力和公信力不断得到各方肯定。封面新闻突破千万的用户下载量,呈现出以四川为主阵地的全国分布态势,用户年龄构成以20-35岁为主,“亿万年轻人的生活方式”的定位初步得到体现。`,
language: 'zh-cn',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/thehindu/topic.ts b/lib/routes/thehindu/topic.ts
index 913a322aa..2916da76e 100644
--- a/lib/routes/thehindu/topic.ts
+++ b/lib/routes/thehindu/topic.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/topic/:topic',
+ categories: ['bbs'],
+ example: '/thehindu/topic/rains',
+ parameters: { topic: 'Topic slug, can be found in URL.' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['thehindu.com/topic/:topic'],
+ },
+ name: 'Topic',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const baseUrl = 'https://www.thehindu.com';
const topic = ctx.req.param('topic');
const link = `${baseUrl}/topic/${topic}/`;
@@ -46,7 +68,7 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('head title').text().trim(),
link: `${baseUrl}/topic/${topic}/`,
image: $('meta[property="og:image"]').attr('content'),
@@ -54,5 +76,5 @@ export default async (ctx) => {
icon: $('link[rel="icon"]').attr('href'),
language: 'en',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/theinitium/full.ts b/lib/routes/theinitium/full.ts
index a762f7338..4a680f0d6 100644
--- a/lib/routes/theinitium/full.ts
+++ b/lib/routes/theinitium/full.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { config } from '@/config';
@@ -6,7 +7,14 @@ import { parseDate } from '@/utils/parse-date';
const TOKEN = 'Basic YW5vbnltb3VzOkdpQ2VMRWp4bnFCY1ZwbnA2Y0xzVXZKaWV2dlJRY0FYTHY=';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:model?/:type?/:language?',
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
// model是channel/tag/etc.,而type是latest/feature/quest-academy这些一级栏目/标签/作者名的slug名。如果是追踪的话,那就是model是follow,type是articles。
const model = ctx.req.param('model') ?? 'channel';
const type = ctx.req.param('type') ?? 'latest';
@@ -160,11 +168,11 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: `端传媒 - ${name}`,
link: listLink,
icon: 'https://theinitium.com/misc/about/logo192.png',
item: items,
image,
- });
-};
+ };
+}
diff --git a/lib/routes/themoviedb/collection.ts b/lib/routes/themoviedb/collection.ts
index 3d73ca37a..7c827efac 100644
--- a/lib/routes/themoviedb/collection.ts
+++ b/lib/routes/themoviedb/collection.ts
@@ -1,8 +1,27 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import apiKey from './api-key';
import { handleMovieItem } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/collection/:id/:lang?',
+ categories: ['picture'],
+ example: '/themoviedb/collection/131292/en-US',
+ parameters: { id: 'Collection ID', lang: 'Language' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Collection',
+ maintainers: ['x2cf'],
+ handler,
+};
+
+async function handler(ctx) {
const { id, lang } = ctx.req.param();
const { data } = await got(`https://api.themoviedb.org/3/collection/${id}`, {
searchParams: {
@@ -11,11 +30,11 @@ export default async (ctx) => {
},
});
- ctx.set('data', {
+ return {
title: `${data.name} — TMDB`,
description: data.overview.trim(),
image: `https://image.tmdb.org/t/p/original${data.poster_path}`,
link: `https://www.themoviedb.org/collection/${data.id}`,
item: data.parts.map((item) => handleMovieItem(item, lang)),
- });
-};
+ };
+}
diff --git a/lib/routes/themoviedb/episodes.ts b/lib/routes/themoviedb/episodes.ts
index 9cc783c6d..cda456b61 100644
--- a/lib/routes/themoviedb/episodes.ts
+++ b/lib/routes/themoviedb/episodes.ts
@@ -1,9 +1,28 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import apiKey from './api-key';
import { handleDescription } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/tv/:id/seasons/:seasonNumber/episodes/:lang?',
+ categories: ['picture'],
+ example: '/themoviedb/tv/70593/seasons/1/episodes/en-US',
+ parameters: { id: 'TV show ID', seasonNumber: 'Season number', lang: 'Language' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'TV Show Episodes',
+ maintainers: ['x2cf'],
+ handler,
+};
+
+async function handler(ctx) {
const { id, seasonNumber, lang } = ctx.req.param();
const searchParams = {
language: lang,
@@ -12,7 +31,7 @@ export default async (ctx) => {
const { data: tvShowDetails } = await got(`https://api.themoviedb.org/3/tv/${id}`, { searchParams });
const { data: seasonDetails } = await got(`https://api.themoviedb.org/3/tv/${id}/season/${seasonNumber}`, { searchParams });
- ctx.set('data', {
+ return {
title: `${tvShowDetails.name} ${seasonDetails.name} — TMDB`,
description: seasonDetails.overview.trim(),
image: `https://image.tmdb.org/t/p/original${seasonDetails.poster_path}`,
@@ -23,5 +42,5 @@ export default async (ctx) => {
description: handleDescription(item),
pubDate: item.air_date ? parseDate(item.air_date) : undefined,
})),
- });
-};
+ };
+}
diff --git a/lib/routes/themoviedb/seasons.ts b/lib/routes/themoviedb/seasons.ts
index 798848f97..b14cb99e0 100644
--- a/lib/routes/themoviedb/seasons.ts
+++ b/lib/routes/themoviedb/seasons.ts
@@ -1,9 +1,28 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import apiKey from './api-key';
import { handleDescription } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/tv/:id/seasons/:lang?',
+ categories: ['picture'],
+ example: '/themoviedb/tv/70593/seasons/en-US',
+ parameters: { id: 'TV show ID', lang: 'Language' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'TV Show Seasons',
+ maintainers: ['x2cf'],
+ handler,
+};
+
+async function handler(ctx) {
const { id, lang } = ctx.req.param();
const { data } = await got(`https://api.themoviedb.org/3/tv/${id}`, {
searchParams: {
@@ -12,7 +31,7 @@ export default async (ctx) => {
},
});
- ctx.set('data', {
+ return {
title: `${data.name} - Seasons — TMDB`,
description: data.overview.trim(),
image: `https://image.tmdb.org/t/p/original${data.poster_path}`,
@@ -27,5 +46,5 @@ export default async (ctx) => {
pubDate: item.air_date ? parseDate(item.air_date) : undefined,
};
}),
- });
-};
+ };
+}
diff --git a/lib/routes/themoviedb/sheet.ts b/lib/routes/themoviedb/sheet.ts
index 483b81b28..4939c530c 100644
--- a/lib/routes/themoviedb/sheet.ts
+++ b/lib/routes/themoviedb/sheet.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import apiKey from './api-key';
import { MEDIA_TYPE_TO_ITEM_HANDLE } from './utils';
@@ -28,7 +29,25 @@ const TITLE = {
},
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:mediaType/:sheet/:lang?',
+ categories: ['picture'],
+ example: '/themoviedb/tv/top-rated/en-US',
+ parameters: { mediaType: '`movie` or `tv`', sheet: 'Sheet, see below', lang: 'Language' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Sheet',
+ maintainers: ['x2cf'],
+ handler,
+};
+
+async function handler(ctx) {
const { mediaType, sheet, lang } = ctx.req.param();
const { data } = await got(`https://api.themoviedb.org/3/${API_PATH[mediaType][sheet]}`, {
searchParams: {
@@ -37,9 +56,9 @@ export default async (ctx) => {
},
});
- ctx.set('data', {
+ return {
title: `${TITLE[mediaType][sheet]} — TMDB`,
link: `https://www.themoviedb.org/${mediaType}/${sheet}`,
item: data.results.map((item) => MEDIA_TYPE_TO_ITEM_HANDLE[mediaType](item, lang)),
- });
-};
+ };
+}
diff --git a/lib/routes/themoviedb/trending.ts b/lib/routes/themoviedb/trending.ts
index 117ee4bfc..4760dd32f 100644
--- a/lib/routes/themoviedb/trending.ts
+++ b/lib/routes/themoviedb/trending.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import apiKey from './api-key';
import { MEDIA_TYPE_TO_ITEM_HANDLE } from './utils';
@@ -7,7 +8,25 @@ const MEDIA_TYPE_TO_TITLE = {
movie: 'Movies',
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/trending/:mediaType/:timeWindow/:lang?',
+ categories: ['picture'],
+ example: '/themoviedb/trending/tv/day/en-US',
+ parameters: { mediaType: '`movie` or `tv`', timeWindow: '`day` or `week`', lang: 'Language' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Trending',
+ maintainers: ['x2cf'],
+ handler,
+};
+
+async function handler(ctx) {
const { mediaType, timeWindow, lang } = ctx.req.param();
const { data } = await got(`https://api.themoviedb.org/3/trending/${mediaType}/${timeWindow}`, {
searchParams: {
@@ -16,9 +35,9 @@ export default async (ctx) => {
},
});
- ctx.set('data', {
+ return {
title: `Popular ${MEDIA_TYPE_TO_TITLE[mediaType]} — TMDB`,
link: `https://www.themoviedb.org/${mediaType}`,
item: data.results.map((item) => MEDIA_TYPE_TO_ITEM_HANDLE[mediaType](item, lang)),
- });
-};
+ };
+}
diff --git a/lib/routes/thenewslens/index.ts b/lib/routes/thenewslens/index.ts
index b7b49412e..6d7544604 100644
--- a/lib/routes/thenewslens/index.ts
+++ b/lib/routes/thenewslens/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -9,7 +10,14 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '*',
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const rootUrl = 'https://www.thenewslens.com';
const currentUrl = `${rootUrl}${getSubPath(ctx) === '/' ? '/latest-article' : getSubPath(ctx)}`;
@@ -76,9 +84,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('title').text(),
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/thepaper/839studio/category.ts b/lib/routes/thepaper/839studio/category.ts
index c9debbbc4..a35562be7 100644
--- a/lib/routes/thepaper/839studio/category.ts
+++ b/lib/routes/thepaper/839studio/category.ts
@@ -1,7 +1,18 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/839studio/:id',
+ radar: {
+ source: ['thepaper.cn/'],
+ },
+ name: 'Unknown',
+ maintainers: ['umm233'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const link = `http://projects.thepaper.cn/thepaper-cases/839studio/?cat=${id}`;
@@ -20,7 +31,7 @@ export default async (ctx) => {
const category = $('div[class=lefth]').find('h1').text();
const desc = $('div[class=leftc]').find('p').text();
- ctx.set('data', {
+ return {
title: `澎湃美数课作品集-${category}`,
link,
description: desc,
@@ -36,5 +47,5 @@ export default async (ctx) => {
};
})
.get(),
- });
-};
+ };
+}
diff --git a/lib/routes/thepaper/839studio/studio.ts b/lib/routes/thepaper/839studio/studio.ts
index f1edd5ede..eba04c445 100644
--- a/lib/routes/thepaper/839studio/studio.ts
+++ b/lib/routes/thepaper/839studio/studio.ts
@@ -1,7 +1,15 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/839studio',
+ name: 'Unknown',
+ maintainers: ['umm233'],
+ handler,
+};
+
+async function handler() {
// 发起 HTTP GET 请求
const response = await got({
method: 'get',
@@ -14,7 +22,7 @@ export default async (ctx) => {
const $ = load(data);
const list = $('div[class=imgtext]');
- ctx.set('data', {
+ return {
title: '澎湃美数课作品集',
link: 'http://projects.thepaper.cn/thepaper-cases/839studio/',
item:
@@ -29,5 +37,5 @@ export default async (ctx) => {
};
})
.get(),
- });
-};
+ };
+}
diff --git a/lib/routes/thepaper/channel.ts b/lib/routes/thepaper/channel.ts
index d8c6694c6..2856ad59e 100644
--- a/lib/routes/thepaper/channel.ts
+++ b/lib/routes/thepaper/channel.ts
@@ -1,8 +1,27 @@
+import { Route } from '@/types';
import utils from './utils';
import { load } from 'cheerio';
import got from '@/utils/got';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/channel/:id',
+ categories: ['bbs'],
+ example: '/thepaper/channel/25950',
+ parameters: { id: '频道 id,可在频道页 URL 中找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '频道',
+ maintainers: ['xyqfer', 'nczitzk', 'bigfei'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const channel_url = `https://m.thepaper.cn/channel/${id}`;
const channel_url_resp = await got(channel_url);
@@ -16,11 +35,11 @@ export default async (ctx) => {
const list = resp.data.data.list;
const items = await Promise.all(list.map((item) => utils.ProcessItem(item, ctx)));
- ctx.set('data', {
+ return {
title: `澎湃新闻频道 - ${utils.ChannelIdToName(id, channel_url_data)}`,
link: channel_url,
item: items,
itunes_author: '澎湃新闻',
image: utils.ExtractLogo(channel_url_resp),
- });
-};
+ };
+}
diff --git a/lib/routes/thepaper/factpaper.ts b/lib/routes/thepaper/factpaper.ts
index 374881834..4f1d1556a 100644
--- a/lib/routes/thepaper/factpaper.ts
+++ b/lib/routes/thepaper/factpaper.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -8,7 +9,29 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/factpaper/:status?',
+ categories: ['bbs'],
+ example: '/thepaper/factpaper',
+ parameters: { status: '状态 id,可选 `1` 即 有定论 或 `0` 即 核查中,默认为 `1`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['factpaper.cn/'],
+ target: '/factpaper/:status',
+ },
+ name: '明查',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const status = Number.parseInt(ctx.req.param('status') ?? '1');
const rootUrl = 'https://www.factpaper.cn';
@@ -57,9 +80,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `澎湃明查 - ${status === 1 ? '有定论' : '核查中'}`,
link: rootUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/thepaper/featured.ts b/lib/routes/thepaper/featured.ts
index 3cd1bf014..a73f97dd3 100644
--- a/lib/routes/thepaper/featured.ts
+++ b/lib/routes/thepaper/featured.ts
@@ -1,18 +1,40 @@
+import { Route } from '@/types';
import utils from './utils';
import { load } from 'cheerio';
import got from '@/utils/got';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/featured',
+ categories: ['bbs'],
+ example: '/thepaper/featured',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['thepaper.cn/'],
+ },
+ name: '首页头条',
+ maintainers: ['HenryQW', 'nczitzk', 'bigfei'],
+ handler,
+};
+
+async function handler(ctx) {
const response = await got('https://m.thepaper.cn');
const data = JSON.parse(load(response.data)('#__NEXT_DATA__').html());
const list = [...data.props.pageProps.data.list, ...data.props.pageProps.topData.recommendImg];
const items = await Promise.all(list.map((item) => utils.ProcessItem(item, ctx)));
- ctx.set('data', {
+ return {
title: '澎湃新闻 - 首页头条',
link: 'https://m.thepaper.cn',
item: items,
itunes_author: '澎湃新闻',
image: utils.ExtractLogo(response),
- });
-};
+ };
+}
diff --git a/lib/routes/thepaper/list.ts b/lib/routes/thepaper/list.ts
index fc0915e55..fa8c7f581 100644
--- a/lib/routes/thepaper/list.ts
+++ b/lib/routes/thepaper/list.ts
@@ -1,8 +1,27 @@
+import { Route } from '@/types';
import utils from './utils';
import { load } from 'cheerio';
import got from '@/utils/got';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/list/:id',
+ categories: ['bbs'],
+ example: '/thepaper/list/25457',
+ parameters: { id: '栏目 id,可在栏目页 URL 中找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '栏目',
+ maintainers: ['nczitzk', 'bigfei'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const list_url = `https://m.thepaper.cn/list/${id}`;
const list_url_resp = await got(list_url);
@@ -17,11 +36,11 @@ export default async (ctx) => {
const list = pagePropsData.list;
const items = await Promise.all(list.map((item) => utils.ProcessItem(item, ctx)));
- ctx.set('data', {
+ return {
title: `澎湃新闻栏目 - ${utils.ListIdToName(id, list_url_data)}`,
link: list_url,
item: items,
itunes_author: '澎湃新闻',
image: pagePropsData.nodeInfo?.pic ?? utils.ExtractLogo(list_url_resp),
- });
-};
+ };
+}
diff --git a/lib/routes/thepaper/sidebar.ts b/lib/routes/thepaper/sidebar.ts
index e89a20b96..e5a11a3bb 100644
--- a/lib/routes/thepaper/sidebar.ts
+++ b/lib/routes/thepaper/sidebar.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import utils from './utils';
import got from '@/utils/got';
@@ -7,7 +8,18 @@ const sections = {
morningEveningNews: '早晚报',
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/sidebar/:sec?',
+ radar: {
+ source: ['thepaper.cn/'],
+ target: '/sidebar',
+ },
+ name: 'Unknown',
+ maintainers: ['bigfei'],
+ handler,
+};
+
+async function handler(ctx) {
const { sec = 'hotNews' } = ctx.req.param();
const sidebar_url = `https://cache.thepaper.cn/contentapi/wwwIndex/rightSidebar`;
@@ -16,9 +28,9 @@ export default async (ctx) => {
const list = sidebar_url_data.data[sec];
const items = await Promise.all(list.filter((item) => item.contId).map((item) => utils.ProcessItem(item, ctx)));
- ctx.set('data', {
+ return {
title: `澎湃新闻 - ${sections[sec]}`,
item: items,
link: 'https://www.thepaper.cn',
- });
-};
+ };
+}
diff --git a/lib/routes/theverge/index.ts b/lib/routes/theverge/index.ts
index a668168fb..5027034d0 100644
--- a/lib/routes/theverge/index.ts
+++ b/lib/routes/theverge/index.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import parser from '@/utils/rss-parser';
import { load } from 'cheerio';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:hub?',
+ categories: ['traditional-media'],
+ example: '/theverge',
+ parameters: { hub: 'Hub, see below, All Posts by default' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['theverge.com/:hub', 'theverge.com/'],
+ },
+ name: 'The Verge',
+ maintainers: ['HenryQW', 'vbali'],
+ handler,
+};
+
+async function handler(ctx) {
const link = ctx.req.param('hub') ? `https://www.theverge.com/${ctx.req.param('hub')}/rss/index.xml` : 'https://www.theverge.com/rss/index.xml';
const feed = await parser.parseURL(link);
@@ -84,10 +106,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: feed.title,
link: feed.link,
description: feed.description,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/thoughtco/index.ts b/lib/routes/thoughtco/index.ts
index 07f043ebc..20097d0e6 100644
--- a/lib/routes/thoughtco/index.ts
+++ b/lib/routes/thoughtco/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -8,7 +9,25 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:category?',
+ categories: ['traditional-media'],
+ example: '/thoughtco',
+ parameters: { category: 'Category id, see below' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Category',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const { category = '' } = ctx.req.param();
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 50;
@@ -84,7 +103,7 @@ export default async (ctx) => {
const title = $('meta[property="og:title"]').prop('content');
const icon = new URL($('link[rel="apple-touch-icon-precomposed"]').prop('href'), rootUrl).href;
- ctx.set('data', {
+ return {
item: items,
title: `${author}${title.startsWith(author) ? '' : ` - ${title}`}`,
link: currentUrl,
@@ -95,5 +114,5 @@ export default async (ctx) => {
logo: icon,
subtitle: $('meta[property="og:title"]').prop('content'),
author,
- });
-};
+ };
+}
diff --git a/lib/routes/threads/index.ts b/lib/routes/threads/index.ts
index 876ebeb32..8affa95d6 100644
--- a/lib/routes/threads/index.ts
+++ b/lib/routes/threads/index.ts
@@ -1,8 +1,27 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { PROFILE_QUERY, REPLIES_QUERY, THREADS_QUERY, apiUrl, threadUrl, profileUrl, extractTokens, makeHeader, buildContent } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:user/:routeParams?',
+ categories: ['new-media'],
+ example: '/threads/zuck',
+ parameters: { user: 'Username', routeParams: 'Extra parameters, see the table below' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'User timeline',
+ maintainers: ['ninboy'],
+ handler,
+};
+
+async function handler(ctx) {
const { user, routeParams } = ctx.req.param();
const { lsd, userId } = await extractTokens(user, ctx);
@@ -66,11 +85,11 @@ export default async (ctx) => {
json.items = items;
ctx.set('json', json);
- ctx.set('data', {
+ return {
title: `${user} (@${user}) on Threads`,
link: profileUrl(user),
image: userData.hd_profile_pic_versions?.sort((a, b) => b.width - a.width)[0].url ?? userData.profile_pic_url,
description: userData.biography,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/thwiki/index.ts b/lib/routes/thwiki/index.ts
index b423a5eab..6ef216b1f 100644
--- a/lib/routes/thwiki/index.ts
+++ b/lib/routes/thwiki/index.ts
@@ -1,7 +1,30 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import dayjs from 'dayjs';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/calendar/:before?/:after?',
+ categories: ['program-update'],
+ example: '/thwiki/calendar',
+ parameters: { before: 'From how many days ago (default 30)', after: 'To how many days after (default 30)' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['thwiki.cc/', 'thwiki.cc/日程表'],
+ target: '/calendar',
+ },
+ name: 'Calendar',
+ maintainers: ['aether17'],
+ handler,
+};
+
+async function handler(ctx) {
const beforeDays = ctx.req.param('before') ? Number.parseInt(ctx.req.param('before')) : 30;
const afterDays = ctx.req.param('after') ? Number.parseInt(ctx.req.param('after')) : 30;
const before = dayjs().subtract(beforeDays, 'day').format('YYYY-MM-DD');
@@ -16,7 +39,7 @@ export default async (ctx) => {
});
const data = response.data.results;
- ctx.set('data', {
+ return {
title: 'Touhou events calendar (THBWiki)',
link: `https://calendar.thwiki.cc/`,
description: 'A Touhou related events calendar api from THBWiki',
@@ -28,5 +51,5 @@ export default async (ctx) => {
guid: item.id,
link: item.url,
})),
- });
-};
+ };
+}
diff --git a/lib/routes/tiktok/user.ts b/lib/routes/tiktok/user.ts
index 72da96c53..cc46cd2df 100644
--- a/lib/routes/tiktok/user.ts
+++ b/lib/routes/tiktok/user.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -11,7 +12,29 @@ import puppeteer from '@/utils/puppeteer';
const baseUrl = 'https://www.tiktok.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/user/:user/:iframe?',
+ categories: ['new-media'],
+ example: '/tiktok/user/@linustech/true',
+ parameters: { user: 'User ID, including @', iframe: 'Use the official iframe to embed the video, which allows you to view the video if the default option does not work. Default to `false`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: true,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['tiktok.com/:user'],
+ target: '/user/:user',
+ },
+ name: 'User',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const { user, iframe } = ctx.req.param();
const useIframe = queryToBoolean(iframe);
@@ -54,12 +77,12 @@ export default async (ctx) => {
category: item.textExtra.map((t) => `#${t.hashtagName}`),
}));
- ctx.set('data', {
+ return {
title: data.SharingMetaState.value['og:title'],
description: data.SharingMetaState.value['og:description'],
image: data.SharingMetaState.value['og:image'],
link: `${baseUrl}/${user}`,
item: items,
language: data.lang,
- });
-};
+ };
+}
diff --git a/lib/routes/timednews/news.ts b/lib/routes/timednews/news.ts
index 86d9e90f3..cb35bb15a 100644
--- a/lib/routes/timednews/news.ts
+++ b/lib/routes/timednews/news.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -52,7 +53,25 @@ const PATH_LIST = {
},
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/:type?',
+ categories: ['traditional-media'],
+ example: '/timednews/news',
+ parameters: { type: '子分类,见下表,默认为全部' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '新闻',
+ maintainers: ['linbuxiao'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type') ?? 'all';
const url = `${BASE}/${PATH_LIST[type].path}`;
const res = await got({
@@ -91,12 +110,12 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: '时刻新闻',
link: url,
description: `时刻新闻 ${PATH_LIST[type].name}`,
item: items,
- });
+ };
ctx.set('json', {
title: '时刻新闻',
@@ -104,4 +123,4 @@ export default async (ctx) => {
description: `时刻新闻 ${PATH_LIST[type].name}`,
item: items,
});
-};
+}
diff --git a/lib/routes/tingshuitz/changsha.ts b/lib/routes/tingshuitz/changsha.ts
index 3582c4946..fc935eb37 100644
--- a/lib/routes/tingshuitz/changsha.ts
+++ b/lib/routes/tingshuitz/changsha.ts
@@ -1,9 +1,28 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
const baseUrl = 'http://www.supplywater.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/changsha/:channelId?',
+ categories: ['travel'],
+ example: '/tingshuitz/changsha/78',
+ parameters: { channelId: 'N' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '长沙市',
+ maintainers: ['shansing'],
+ handler,
+};
+
+async function handler(ctx) {
const { channelId = 78 } = ctx.req.param();
const listPage = await got('http://www.supplywater.com/tstz-' + channelId + '.aspx');
const $ = load(listPage.data);
@@ -35,9 +54,9 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: `${pageName}通知 - 长沙水业集团`,
link: `${baseUrl}/fuwuzhinan.aspx`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/tingshuitz/dalian.ts b/lib/routes/tingshuitz/dalian.ts
index f1b047cda..344a103d5 100644
--- a/lib/routes/tingshuitz/dalian.ts
+++ b/lib/routes/tingshuitz/dalian.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/dalian',
+ categories: ['travel'],
+ example: '/tingshuitz/dalian',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['swj.dl.gov.cn/col/col4296/index.html', 'swj.dl.gov.cn/'],
+ },
+ name: '大连市',
+ maintainers: ['DIYgod'],
+ handler,
+};
+
+async function handler() {
const url =
'https://swj.dl.gov.cn/module/web/jpage/dataproxy.jsp?page=1&webid=28&path=https://swj.dl.gov.cn/&columnid=4296&unitid=31227&webname=%25E5%25A4%25A7%25E8%25BF%259E%25E5%25B8%2582%25E6%25B0%25B4%25E5%258A%25A1%25E5%25B1%2580&permissiontype=0';
const response = await got(url);
@@ -20,10 +42,10 @@ export default async (ctx) => {
};
});
- ctx.set('data', {
+ return {
title: '停水通知 - 大连市水务局',
link: 'https://swj.dl.gov.cn/col/col4296/index.html',
description: '停水通知 - 大连市水务局',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/tingshuitz/dongguan.ts b/lib/routes/tingshuitz/dongguan.ts
index c9ea88655..dc8874c31 100644
--- a/lib/routes/tingshuitz/dongguan.ts
+++ b/lib/routes/tingshuitz/dongguan.ts
@@ -1,8 +1,27 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/dongguan',
+ categories: ['travel'],
+ example: '/tingshuitz/dongguan',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '东莞市',
+ maintainers: ['victoriqueko'],
+ handler,
+};
+
+async function handler() {
const url = 'http://www.djsw.com.cn/news/tstz/index.html';
const response = await got({
method: 'get',
@@ -13,7 +32,7 @@ export default async (ctx) => {
const $ = load(data);
const list = $('#cntR li');
- ctx.set('data', {
+ return {
title: $('title').text() || '停水通知 - 东莞市东江水务有限公司',
link: 'http://www.djsw.com.cn/news/tstz/index.html',
description: $('title').text() || '停水通知 - 东莞市东江水务有限公司',
@@ -30,5 +49,5 @@ export default async (ctx) => {
};
})
.get(),
- });
-};
+ };
+}
diff --git a/lib/routes/tingshuitz/guangzhou.ts b/lib/routes/tingshuitz/guangzhou.ts
index f37e25cee..438ec9cf6 100644
--- a/lib/routes/tingshuitz/guangzhou.ts
+++ b/lib/routes/tingshuitz/guangzhou.ts
@@ -1,6 +1,25 @@
+import { Route } from '@/types';
import got from '@/utils/got';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/guangzhou',
+ categories: ['travel'],
+ example: '/tingshuitz/guangzhou',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '广州市',
+ maintainers: ['xyqfer'],
+ handler,
+};
+
+async function handler() {
const response = await got({
method: 'post',
url: `https://mp.weixin.qq.com/mp/homepage?__biz=MzA3MDE0NzAxMw%3D%3D&hid=15&sn=270abafdcf67ce4e2b52a049a0aa219a&scene=1&sharer_username=&clicktime=${Math.floor(
@@ -15,9 +34,9 @@ export default async (ctx) => {
description: `${title}
${digest}...`,
}));
- ctx.set('data', {
+ return {
title: '停水通知 - 广州市自来水96968',
link: 'https://mp.weixin.qq.com/mp/homepage?__biz=MzA3MDE0NzAxMw%3D%3D&hid=15&sn=270abafdcf67ce4e2b52a049a0aa219a&scene=1&sharer_username=&clicktime=&devicetype=iOS12.2&version=1700042b&lang=zh_CN&nettype=WIFI&ascene=0&fontScale=100&wx_header=1',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/tingshuitz/hangzhou.ts b/lib/routes/tingshuitz/hangzhou.ts
index 5163099f5..5df1fd93e 100644
--- a/lib/routes/tingshuitz/hangzhou.ts
+++ b/lib/routes/tingshuitz/hangzhou.ts
@@ -1,7 +1,29 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/hangzhou',
+ categories: ['travel'],
+ example: '/tingshuitz/hangzhou',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.hzwgc.com/public/stop_the_water', 'www.hzwgc.com/'],
+ },
+ name: '杭州市',
+ maintainers: ['znhocn'],
+ handler,
+};
+
+async function handler() {
// const area = ctx.req.param('area');
const url = 'http://www.hzwgc.com/public/stop_the_water/';
const response = await got({
@@ -13,7 +35,7 @@ export default async (ctx) => {
const $ = load(data);
const list = $('.datalist li');
- ctx.set('data', {
+ return {
title: $('title').text(),
link: 'http://www.hzwgc.com/public/stop_the_water/',
description: $('meta[name="description"]').attr('content') || $('title').text(),
@@ -30,5 +52,5 @@ export default async (ctx) => {
};
})
.get(),
- });
-};
+ };
+}
diff --git a/lib/routes/tingshuitz/nanjing.ts b/lib/routes/tingshuitz/nanjing.ts
index 0fa1b4598..8113160b8 100644
--- a/lib/routes/tingshuitz/nanjing.ts
+++ b/lib/routes/tingshuitz/nanjing.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
const HOME_PAGE = 'http://www.jlwater.com/';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/nanjing',
+ categories: ['travel'],
+ example: '/tingshuitz/nanjing',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['jlwater.com/portal/10000013', 'jlwater.com/'],
+ },
+ name: '南京市',
+ maintainers: ['ocleo1'],
+ handler,
+};
+
+async function handler() {
const url = `${HOME_PAGE}portal/10000013`;
const response = await got(url);
@@ -11,7 +33,7 @@ export default async (ctx) => {
const $ = load(data);
const list = $('.list-content ul li');
- ctx.set('data', {
+ return {
title: $('head title').text(),
link: url,
item: list
@@ -29,5 +51,5 @@ export default async (ctx) => {
};
})
.get(),
- });
-};
+ };
+}
diff --git a/lib/routes/tingshuitz/shenzhen.ts b/lib/routes/tingshuitz/shenzhen.ts
index c4dd877e8..6e435e3c3 100644
--- a/lib/routes/tingshuitz/shenzhen.ts
+++ b/lib/routes/tingshuitz/shenzhen.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -7,7 +8,28 @@ import timezone from '@/utils/timezone';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/shenzhen',
+ categories: ['travel'],
+ example: '/tingshuitz/shenzhen',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['sz-water.com.cn/*'],
+ },
+ name: '深圳市',
+ maintainers: ['lilPiper'],
+ handler,
+};
+
+async function handler() {
const url = 'https://szgk.sz-water.com.cn/api/wechat/op/getStopWaterNotice';
const response = await got({
method: 'get',
@@ -16,7 +38,7 @@ export default async (ctx) => {
const data = response.data.data;
- ctx.set('data', {
+ return {
title: '停水通知 - 深圳水务',
link: 'https://www.sz-water.com.cn/',
item: data.map((item) => ({
@@ -28,5 +50,5 @@ export default async (ctx) => {
link: 'https://szgk.sz-water.com.cn/wechat_web/Water_stop.html',
guid: `${item.position}${item.stopStartTime}`,
})),
- });
-};
+ };
+}
diff --git a/lib/routes/tingshuitz/wuhan.ts b/lib/routes/tingshuitz/wuhan.ts
index 2108ea016..122a5dd98 100644
--- a/lib/routes/tingshuitz/wuhan.ts
+++ b/lib/routes/tingshuitz/wuhan.ts
@@ -1,8 +1,20 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
const baseUrl = 'https://www.whwater.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/wuhan/:channelId?',
+ radar: {
+ source: ['whwater.com/IWater.shtml', 'whwater.com/'],
+ target: '/wuhan',
+ },
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const { channelId = 68 } = ctx.req.param();
const response = await got.post('https://manager.whwater.com:8900/website/article/findChannelArticle', {
form: {
@@ -24,9 +36,9 @@ export default async (ctx) => {
link: `https://${baseUrl}/IPolicyDetails.shtml?id=31&sid=${channelId}${item.articleLink}`,
}));
- ctx.set('data', {
+ return {
title: `${data.channelName}通知 - 武汉市水务集团有限公司`,
link: `${baseUrl}/IWater.shtml?id=31&sid=48`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/tingshuitz/xian.ts b/lib/routes/tingshuitz/xian.ts
index 9a76686f2..be4f18dbb 100644
--- a/lib/routes/tingshuitz/xian.ts
+++ b/lib/routes/tingshuitz/xian.ts
@@ -1,8 +1,27 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import iconv from 'iconv-lite';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/xian',
+ categories: ['travel'],
+ example: '/tingshuitz/xian',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '西安市',
+ maintainers: ['ciaranchen'],
+ handler,
+};
+
+async function handler() {
const url = 'http://www.xazls.com/tsgg/index.htm';
const response = await got(url, {
responseType: 'buffer',
@@ -12,7 +31,7 @@ export default async (ctx) => {
const $ = load(iconv.decode(data, 'gb2312'));
const list = $('#body > div.M > div.AboutUsDetail > ul > li');
- ctx.set('data', {
+ return {
title: $('title').text() || '停水通知 - 西安市自来水有限公司',
link: 'http://www.xazls.com/tsgg/index.htm',
item: list
@@ -27,5 +46,5 @@ export default async (ctx) => {
};
})
.get(),
- });
-};
+ };
+}
diff --git a/lib/routes/tingshuitz/xiaoshan.ts b/lib/routes/tingshuitz/xiaoshan.ts
index f87285b2e..6949ed8e2 100644
--- a/lib/routes/tingshuitz/xiaoshan.ts
+++ b/lib/routes/tingshuitz/xiaoshan.ts
@@ -1,7 +1,29 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/xiaoshan',
+ categories: ['travel'],
+ example: '/tingshuitz/xiaoshan',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.xswater.com/gongshui/channels/227.html', 'www.xswater.com/'],
+ },
+ name: '萧山区',
+ maintainers: ['znhocn'],
+ handler,
+};
+
+async function handler() {
// const area = ctx.req.param('area');
const url = 'https://www.xswater.com/gongshui/channels/227.html';
const response = await got({
@@ -13,7 +35,7 @@ export default async (ctx) => {
const $ = load(data);
const list = $('.ul-list li');
- ctx.set('data', {
+ return {
title: $('title').text(),
link: 'https://www.xswater.com/gongshui/channels/227.html',
description: $('meta[name="description"]').attr('content') || $('title').text(),
@@ -30,5 +52,5 @@ export default async (ctx) => {
};
})
.get(),
- });
-};
+ };
+}
diff --git a/lib/routes/tingshuitz/yangjiang.ts b/lib/routes/tingshuitz/yangjiang.ts
index 0be09b1ae..d6935c93b 100644
--- a/lib/routes/tingshuitz/yangjiang.ts
+++ b/lib/routes/tingshuitz/yangjiang.ts
@@ -1,15 +1,37 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/yangjiang',
+ categories: ['travel'],
+ example: '/tingshuitz/yangjiang',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['yjsswjt.com/zxdt_list.jsp', 'yjsswjt.com/'],
+ },
+ name: '阳江市',
+ maintainers: ['ciaranchen'],
+ handler,
+};
+
+async function handler() {
const url = 'https://www.yjsswjt.com/zxdt_list.jsp?flbz=7';
const response = await got(url);
const $ = load(response.data);
const list = $('div.list_ul_div > ul > li');
- ctx.set('data', {
+ return {
title: '停水通知 - 阳江市水务集团有限公司',
link: 'https://www.yjsswjt.com/zxdt_list.jsp?flbz=7',
item: list
@@ -25,5 +47,5 @@ export default async (ctx) => {
};
})
.get(),
- });
-};
+ };
+}
diff --git a/lib/routes/tingtingfm/program.ts b/lib/routes/tingtingfm/program.ts
index cfc635459..af74a9b0d 100644
--- a/lib/routes/tingtingfm/program.ts
+++ b/lib/routes/tingtingfm/program.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -9,7 +10,28 @@ import * as path from 'node:path';
import { parseDate } from '@/utils/parse-date';
import { getClientVal, sign } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/program/:programId',
+ categories: ['picture'],
+ example: '/tingtingfm/program/M7VJv6Jj4R',
+ parameters: { programId: '节目 ID,可以在 URL 中找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: true,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['mobile.tingtingfm.com/v3/program/:programId'],
+ },
+ name: '节目',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const programId = ctx.req.param('programId');
const apiBaseUrl = 'https://api-v3.tingtingfm.com';
const mobileBaseUrl = 'https://mobile.tingtingfm.com';
@@ -95,7 +117,7 @@ export default async (ctx) => {
enclosure_type: 'audio/x-m4a',
}));
- ctx.set('data', {
+ return {
title: `${radioInfo.title} - ${radioInfo.belong_radio}${radioInfo.belong_fm}`,
description: radioInfo.description,
link: `${mobileBaseUrl}/v3/program/${programId}`,
@@ -104,5 +126,5 @@ export default async (ctx) => {
itunes_category: radioInfo.category,
itunes_explicit: false,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/tisi/index.ts b/lib/routes/tisi/index.ts
index 1f1cba2b4..ba1e72fa6 100644
--- a/lib/routes/tisi/index.ts
+++ b/lib/routes/tisi/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -5,7 +6,25 @@ import { parseDate } from '@/utils/parse-date';
const rootUrl = 'https://www.tisi.org';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/latest',
+ categories: ['traditional-media'],
+ example: '/tisi/latest',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '最近更新',
+ maintainers: ['Fatpandac'],
+ handler,
+};
+
+async function handler() {
const url = `${rootUrl}/?page_id=11151`;
const response = await got(url);
@@ -31,9 +50,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: '腾讯研究院 - 最近更新',
link: url,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/tju/cic/index.ts b/lib/routes/tju/cic/index.ts
index 5280a3eeb..eeaf30e4e 100644
--- a/lib/routes/tju/cic/index.ts
+++ b/lib/routes/tju/cic/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -15,7 +16,25 @@ const pageType = (href) => {
return url.hostname === 'cic.tju.edu.cn' ? 'tju-cic' : 'unknown';
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/cic/:type?',
+ categories: ['forecast'],
+ example: '/tju/cic/news',
+ parameters: { type: 'default `news`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'College of Intelligence and Computing',
+ maintainers: ['SuperPung'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.params && ctx.req.param('type');
let path, subtitle;
@@ -49,7 +68,7 @@ export default async (ctx) => {
}
if (response === null) {
- ctx.set('data', {
+ return {
title: '天津大学智能与计算学部 - ' + subtitle,
link: cic_base_url + path,
description: '链接失效' + cic_base_url + path,
@@ -60,7 +79,7 @@ export default async (ctx) => {
description: `请到此处提交Issue
`,
},
],
- });
+ };
} else {
const $ = load(response.data);
const list = $('.wenzi_list_ul > li')
@@ -100,11 +119,11 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: '天津大学智能与计算学部 - ' + subtitle,
link: cic_base_url + path,
description: null,
item: items,
- });
+ };
}
-};
+}
diff --git a/lib/routes/tju/news/index.ts b/lib/routes/tju/news/index.ts
index 1b3e73301..933b67beb 100644
--- a/lib/routes/tju/news/index.ts
+++ b/lib/routes/tju/news/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -17,7 +18,25 @@ const pageType = (href) => {
return url.hostname === 'news.tju.edu.cn' ? 'tju-news' : 'unknown';
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/:type?',
+ categories: ['forecast'],
+ example: '/tju/news/focus',
+ parameters: { type: 'default `focus`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'News',
+ maintainers: ['SuperPung'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.params && ctx.req.param('type');
let path, subtitle;
@@ -59,7 +78,7 @@ export default async (ctx) => {
}
if (response === null) {
- ctx.set('data', {
+ return {
title: '天津大学新闻网 - ' + subtitle,
link: news_base_url + path,
description: '链接失效' + news_base_url + path,
@@ -70,7 +89,7 @@ export default async (ctx) => {
description: `请到此处提交Issue
`,
},
],
- });
+ };
} else {
const $ = load(response.data);
@@ -119,10 +138,10 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: '天津大学新闻网 - ' + subtitle,
link: news_base_url + path,
item: items,
- });
+ };
}
-};
+}
diff --git a/lib/routes/tju/oaa/index.ts b/lib/routes/tju/oaa/index.ts
index c06e6b151..193cd0e22 100644
--- a/lib/routes/tju/oaa/index.ts
+++ b/lib/routes/tju/oaa/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -22,7 +23,25 @@ const pageType = (href) => {
}
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/oaa/:type?',
+ categories: ['forecast'],
+ example: '/tju/oaa/news',
+ parameters: { type: 'default `news`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'The Office of Academic Affairs',
+ maintainers: ['AmosChenYQ', 'SuperPung'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.params && ctx.req.param('type');
let path, subtitle;
@@ -52,7 +71,7 @@ export default async (ctx) => {
}
if (response === null) {
- ctx.set('data', {
+ return {
title: '天津大学教务处 - ' + subtitle,
link: oaa_base_url + path,
description: '链接失效' + oaa_base_url + path,
@@ -63,7 +82,7 @@ export default async (ctx) => {
description: `请到此处提交Issue
`,
},
],
- });
+ };
} else {
const $ = load(response.data);
const list = $('.notice_l > ul > li > dl > dt')
@@ -103,11 +122,11 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: '天津大学教务处 - ' + subtitle,
link: oaa_base_url + path,
description: null,
item: items,
- });
+ };
}
-};
+}
diff --git a/lib/routes/tju/yzb/index.ts b/lib/routes/tju/yzb/index.ts
index 9552d9436..ae5a525ff 100644
--- a/lib/routes/tju/yzb/index.ts
+++ b/lib/routes/tju/yzb/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -18,7 +19,25 @@ const pageType = (href) => {
return url.hostname === 'yzb.tju.edu.cn' ? 'tju-yzb' : 'unknown';
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/yzb/:type?',
+ categories: ['forecast'],
+ example: '/tju/yzb/notice',
+ parameters: { type: 'default `notice`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Admission Office of Graduate',
+ maintainers: ['SuperPung'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.params && ctx.req.param('type');
let path, subtitle;
@@ -57,7 +76,7 @@ export default async (ctx) => {
}
if (response === null) {
- ctx.set('data', {
+ return {
title: '天津大学研究生招生网 - ' + subtitle,
link: yzb_base_url + path,
description: '链接失效' + yzb_base_url + path,
@@ -68,7 +87,7 @@ export default async (ctx) => {
description: `请到此处提交Issue
`,
},
],
- });
+ };
} else {
const $ = load(iconv.decode(response.data, 'gbk'));
const list = $('body > table:nth-child(3) > tbody > tr > td.table_left_right > table > tbody > tr:nth-child(3) > td > table > tbody > tr:nth-child(1) > td > table > tbody > tr')
@@ -109,10 +128,10 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: '天津大学研究生招生网 - ' + subtitle,
link: yzb_base_url + path,
item: items,
- });
+ };
}
-};
+}
diff --git a/lib/routes/tokeninsight/blog.ts b/lib/routes/tokeninsight/blog.ts
index d1dc5cfec..7e17edbe3 100644
--- a/lib/routes/tokeninsight/blog.ts
+++ b/lib/routes/tokeninsight/blog.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -6,7 +7,29 @@ const baseURL = 'https://www.tokeninsight.com/';
const title = 'TokenInsight';
const link = 'https://www.tokeninsight.com/';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/blog/:lang?',
+ categories: ['other'],
+ example: '/tokeninsight/blog/en',
+ parameters: { lang: 'Language, see below, Chinese by default' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['tokeninsight.com/:lang/blogs'],
+ target: '/blog/:lang',
+ },
+ name: 'Blogs',
+ maintainers: ['fuergaosi233'],
+ handler,
+};
+
+async function handler(ctx) {
const lang = ctx.req.param('lang') ?? 'zh';
const getBlogs = async () => {
@@ -45,9 +68,9 @@ export default async (ctx) => {
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 30;
const blogs = (await getBlogs()).slice(0, limit);
const list = await Promise.all(blogs.map((element) => getBlogInfomation(element)));
- ctx.set('data', {
+ return {
title: `${lang === 'zh' ? '博客' : 'Blogs'} | ${title}`,
link: `${link}${lang}/blogs`,
item: list,
- });
-};
+ };
+}
diff --git a/lib/routes/tokeninsight/bulletin.ts b/lib/routes/tokeninsight/bulletin.ts
index 35d7d5ec8..572fdc25d 100644
--- a/lib/routes/tokeninsight/bulletin.ts
+++ b/lib/routes/tokeninsight/bulletin.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -12,7 +13,29 @@ const get_articles = async () => {
return data;
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/bulletin/:lang?',
+ categories: ['other'],
+ example: '/tokeninsight/bulletin/en',
+ parameters: { lang: 'Language, see below, Chinese by default' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['tokeninsight.com/:lang/latest'],
+ target: '/bulletin/:lang',
+ },
+ name: 'Latest',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const lang = ctx.req.param('lang') ?? 'zh';
const get_article_info = async (article) => {
@@ -37,9 +60,9 @@ export default async (ctx) => {
const articles = await get_articles();
const list = await Promise.all(articles.map((element) => get_article_info(element)));
- ctx.set('data', {
+ return {
title: `${lang === 'zh' ? '快讯' : 'Latest'} | ${title}`,
link: `${link}${lang}/latest`,
item: list,
- });
-};
+ };
+}
diff --git a/lib/routes/tokeninsight/report.ts b/lib/routes/tokeninsight/report.ts
index 3636d223e..21d218fca 100644
--- a/lib/routes/tokeninsight/report.ts
+++ b/lib/routes/tokeninsight/report.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -6,7 +7,29 @@ const baseURL = 'https://www.tokeninsight.com/';
const title = 'TokenInsight';
const link = 'https://www.tokeninsight.com/';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/report/:lang?',
+ categories: ['other'],
+ example: '/tokeninsight/report/en',
+ parameters: { lang: 'Language, see below, Chinese by default' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['tokeninsight.com/:lang/report'],
+ target: '/report/:lang',
+ },
+ name: 'Research',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const lang = ctx.req.param('lang') ?? 'zh';
const getReports = async () => {
@@ -45,9 +68,9 @@ export default async (ctx) => {
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 30;
const reports = (await getReports()).slice(0, limit);
const list = await Promise.all(reports.map((element) => getReportInfomation(element)));
- ctx.set('data', {
+ return {
title: `${lang === 'zh' ? '报告' : 'Research'} | ${title}`,
link: `${link}${lang}/report`,
item: list,
- });
-};
+ };
+}
diff --git a/lib/routes/tongji/bks.ts b/lib/routes/tongji/bks.ts
index 7e5ae4db0..ec651fd7d 100644
--- a/lib/routes/tongji/bks.ts
+++ b/lib/routes/tongji/bks.ts
@@ -1,14 +1,36 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/bks',
+ categories: ['forecast'],
+ example: '/tongji/bks',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['bksy.tongji.edu.cn/'],
+ },
+ name: '本科生院通知公告',
+ maintainers: ['shiquda'],
+ handler,
+};
+
+async function handler() {
const link = 'https://bksy.tongji.edu.cn/30359/list.htm';
const response = await got(link);
const $ = load(response.data);
const list = $('.wcts-a0018 li');
- ctx.set('data', {
+ return {
title: '同济大学本科生院',
link,
description: '同济大学本科生院通知公告',
@@ -26,5 +48,5 @@ export default async (ctx) => {
pubDate: parseDate(date, 'YYYY-MM-DD'),
};
}),
- });
-};
+ };
+}
diff --git a/lib/routes/tongji/sse/notice.ts b/lib/routes/tongji/sse/notice.ts
index 945e22dcc..565284c39 100644
--- a/lib/routes/tongji/sse/notice.ts
+++ b/lib/routes/tongji/sse/notice.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
// Warning: The author still knows nothing about javascript!
@@ -9,7 +10,25 @@ import { load } from 'cheerio'; // html parser
import getArticle from './_article';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/sse/:type?',
+ categories: ['forecast'],
+ example: '/tongji/sse/xytz',
+ parameters: { type: '通知类型,默认为 `xytz`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '软件学院通知',
+ maintainers: ['sgqy'],
+ handler,
+};
+
+async function handler(ctx) {
const baseUrl = 'https://sse.tongji.edu.cn';
const type = ctx.req.param('type') || 'xytz';
const subType = ['bkstz', 'yjstz', 'jgtz', 'qttz'];
@@ -39,9 +58,9 @@ export default async (ctx) => {
const articleList = await Promise.all(detailUrls.map((item) => cache.tryGet(item.link, () => getArticle(item))));
// feed the data
- ctx.set('data', {
+ return {
title: '同济大学软件学院',
link: listUrl,
item: articleList,
- });
-};
+ };
+}
diff --git a/lib/routes/tongji/yjs.ts b/lib/routes/tongji/yjs.ts
index 753172aef..bc7f6dbfc 100644
--- a/lib/routes/tongji/yjs.ts
+++ b/lib/routes/tongji/yjs.ts
@@ -1,14 +1,36 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/yjs',
+ categories: ['forecast'],
+ example: '/tongji/yjs',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['yz.tongji.edu.cn/zsxw/ggtz.htm', 'yz.tongji.edu.cn/'],
+ },
+ name: '研究生院通知公告',
+ maintainers: ['shengmaosu'],
+ handler,
+};
+
+async function handler() {
const link = 'https://yz.tongji.edu.cn/zsxw/ggtz.htm';
const response = await got(link);
const $ = load(response.data);
const list = $('.list_main_content li');
- ctx.set('data', {
+ return {
title: '同济大学研究生院',
link,
description: '同济大学研究生院通知公告',
@@ -23,5 +45,5 @@ export default async (ctx) => {
pubDate: parseDate(item.find('span').text(), 'YYYY-MM-DD'),
};
}),
- });
-};
+ };
+}
diff --git a/lib/routes/tongli/news.ts b/lib/routes/tongli/news.ts
index 339663ea1..f9d60aef0 100644
--- a/lib/routes/tongli/news.ts
+++ b/lib/routes/tongli/news.ts
@@ -1,9 +1,28 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import * as cheerio from 'cheerio';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/:type',
+ categories: ['government'],
+ example: '/tongli/news/6',
+ parameters: { type: '分類,可以在“新聞”鏈接中找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '新聞',
+ maintainers: ['CokeMine'],
+ handler,
+};
+
+async function handler(ctx) {
const { type } = ctx.req.param();
const baseURL = 'https://www.tongli.com.tw/';
const url = `${baseURL}TNews_List.aspx`;
@@ -48,9 +67,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('.entry_title .n1').text(),
link,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/toodaylab/index.ts b/lib/routes/toodaylab/index.ts
index 9a7c328a0..09f770350 100644
--- a/lib/routes/toodaylab/index.ts
+++ b/lib/routes/toodaylab/index.ts
@@ -1,10 +1,18 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import timezone from '@/utils/timezone';
import { parseDate, parseRelativeDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:params{.+}?',
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const { params = 'posts' } = ctx.req.param();
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30;
@@ -85,7 +93,7 @@ export default async (ctx) => {
const title = $('title').text().split(/\s-/)[0];
const icon = $('link[rel="apple-touch-icon"]').last().prop('href');
- ctx.set('data', {
+ return {
item: items,
title: isHot ? title.replace(/[^|]+/, '最热 ') : title,
link: currentUrl,
@@ -96,5 +104,5 @@ export default async (ctx) => {
logo: icon,
subtitle: $('meta[name="keywords"]').prop('content'),
author: $('h3.logo a img').prop('alt'),
- });
-};
+ };
+}
diff --git a/lib/routes/tophub/index.ts b/lib/routes/tophub/index.ts
index 97280bb86..cc72840fe 100644
--- a/lib/routes/tophub/index.ts
+++ b/lib/routes/tophub/index.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { config } from '@/config';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:id',
+ categories: ['traditional-media'],
+ example: '/tophub/Om4ejxvxEN',
+ parameters: { id: '榜单id,可在 URL 中找到' },
+ features: {
+ requireConfig: true,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['tophub.today/n/:id'],
+ },
+ name: '榜单',
+ maintainers: ['LogicJake'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const link = `https://tophub.today/n/${id}`;
@@ -26,9 +48,9 @@ export default async (ctx) => {
return info;
});
- ctx.set('data', {
+ return {
title,
link,
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/tophub/list.ts b/lib/routes/tophub/list.ts
index db272cb60..b2e7ea307 100644
--- a/lib/routes/tophub/list.ts
+++ b/lib/routes/tophub/list.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -7,7 +8,28 @@ import { config } from '@/config';
import * as path from 'node:path';
import { art } from '@/utils/render';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/list/:id',
+ categories: ['traditional-media'],
+ example: '/tophub/list/Om4ejxvxEN',
+ parameters: { id: '榜单id,可在 URL 中找到' },
+ features: {
+ requireConfig: true,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['tophub.today/n/:id'],
+ },
+ name: '榜单列表',
+ maintainers: ['akynazh'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const link = `https://tophub.today/n/${id}`;
const response = await got.get(link, {
@@ -28,7 +50,7 @@ export default async (ctx) => {
const combinedTitles = items.map((item) => item.title).join('');
const renderRank = art(path.join(__dirname, 'templates/rank.art'), { items });
- ctx.set('data', {
+ return {
title,
link,
item: [
@@ -39,5 +61,5 @@ export default async (ctx) => {
guid: combinedTitles,
},
],
- });
-};
+ };
+}
diff --git a/lib/routes/topys/index.ts b/lib/routes/topys/index.ts
index f9f6d1968..4bb4fae11 100644
--- a/lib/routes/topys/index.ts
+++ b/lib/routes/topys/index.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:keyword?',
+ categories: ['traditional-media'],
+ example: '/topys',
+ parameters: { keyword: '关键字,可在对应结果页的 URL 中找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['topys.cn/search/:keyword', 'topys.cn/'],
+ },
+ name: '关键字',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const keyword = ctx.req.param('keyword') ?? '';
const rootUrl = 'https://www.topys.cn';
@@ -59,9 +81,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `${keyword ? `${keyword} - ` : ''}TOPYS`,
link: keyword ? currentUrl : `${rootUrl}/pick`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/tradingview/blog.ts b/lib/routes/tradingview/blog.ts
index 420094973..61acfa12b 100644
--- a/lib/routes/tradingview/blog.ts
+++ b/lib/routes/tradingview/blog.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -9,7 +10,14 @@ import asyncPool from 'tiny-async-pool';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/blog/:category{.+}?',
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const { category = 'en' } = ctx.req.param();
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 22;
@@ -94,7 +102,7 @@ export default async (ctx) => {
const icon = new URL($('link[rel="icon"]').prop('href'), rootUrl).href;
- ctx.set('data', {
+ return {
item: items,
title: $('title').text(),
link: currentUrl,
@@ -103,5 +111,5 @@ export default async (ctx) => {
icon,
logo: icon,
subtitle: $('h1.site-title').text(),
- });
-};
+ };
+}
diff --git a/lib/routes/tradingview/desktop.ts b/lib/routes/tradingview/desktop.ts
index 5f1d0d3db..10160b439 100644
--- a/lib/routes/tradingview/desktop.ts
+++ b/lib/routes/tradingview/desktop.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/desktop',
+ categories: ['university'],
+ example: '/tradingview/desktop',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['tradingview.com/support/solutions/43000673888-tradingview-desktop-releases-and-release-notes/'],
+ },
+ name: 'Desktop releases and release notes',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 50;
const rootUrl = 'https://www.tradingview.com';
@@ -47,7 +69,7 @@ export default async (ctx) => {
const titleSplits = title.split(/—/);
const icon = new URL($('link[rel="icon"]').prop('href'), rootUrl).href;
- ctx.set('data', {
+ return {
item: items,
title,
link: currentUrl,
@@ -57,5 +79,5 @@ export default async (ctx) => {
logo: icon,
subtitle: titleSplits[0],
author: titleSplits.pop(),
- });
-};
+ };
+}
diff --git a/lib/routes/tradingview/pine.ts b/lib/routes/tradingview/pine.ts
index 507aa32a7..a332a7196 100644
--- a/lib/routes/tradingview/pine.ts
+++ b/lib/routes/tradingview/pine.ts
@@ -1,8 +1,20 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/pine/:version?',
+ radar: {
+ source: ['tradingview.com/pine-script-docs/en/:version/Release_notes.html'],
+ target: '/pine/:version',
+ },
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const { version = 'v5' } = ctx.req.param();
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 100;
@@ -41,7 +53,7 @@ export default async (ctx) => {
const image = new URL('_images/Pine_Script_logo.svg', currentUrl).href;
const icon = new URL('favicon.ico', rootUrl).href;
- ctx.set('data', {
+ return {
item: items,
title: $('title').text(),
link: currentUrl,
@@ -50,5 +62,5 @@ export default async (ctx) => {
image,
icon,
logo: icon,
- });
-};
+ };
+}
diff --git a/lib/routes/transcriptforest/index.ts b/lib/routes/transcriptforest/index.ts
index 107eeb6b6..0ea484947 100644
--- a/lib/routes/transcriptforest/index.ts
+++ b/lib/routes/transcriptforest/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -20,7 +21,18 @@ const bakeTimestamp = (seconds) => {
return `${formattedHours}:${formattedMinutes}:${formattedSeconds}`;
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:channel?',
+ radar: {
+ source: ['www.transcriptforest.com/en/channel'],
+ target: '',
+ },
+ name: 'Unknown',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const channel = ctx.req.param('channel');
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 10;
@@ -104,7 +116,7 @@ export default async (ctx) => {
const icon = new URL($('link[rel="apple-touch-icon"]').prop('href'), rootUrl).href;
const author = title.split(/\|/)[0].trim();
- ctx.set('data', {
+ return {
item: items,
title,
link: currentUrl,
@@ -116,5 +128,5 @@ export default async (ctx) => {
author,
itunes_author: author,
allowEmpty: true,
- });
-};
+ };
+}
diff --git a/lib/routes/trending/all-trending.ts b/lib/routes/trending/all-trending.ts
index 6c9ac0ebb..2bdba50f4 100644
--- a/lib/routes/trending/all-trending.ts
+++ b/lib/routes/trending/all-trending.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -160,7 +161,25 @@ const createItem = ({ dateTime, data, count, newItemCount }, keywords, isToday)
};
// Main
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:keywords/:numberOfDays?',
+ categories: ['other'],
+ example: '/trending/唐山,打人/3',
+ parameters: { keywords: '通过逗号区隔的关键词列表', numberOfDays: '向前追溯的天数,默认为3天' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '关键词聚合追踪',
+ maintainers: ['Jkker'],
+ handler,
+};
+
+async function handler(ctx) {
// Prevent making over 100 requests per invocation
if (ctx.req.param('numberOfDays') > 14) {
throw new Error('days must be less than 14');
@@ -194,10 +213,10 @@ export default async (ctx) => {
},
];
- ctx.set('data', {
+ return {
title: `${keywordStr} | 热点聚合`,
description: `${keywordStr} | 今日头条热搜,知乎热门视频,知乎热搜榜,知乎热门话题,微博热搜榜聚合追踪`,
language: 'zh-cn',
item,
- });
-};
+ };
+}
diff --git a/lib/routes/trendingpapers/papers.ts b/lib/routes/trendingpapers/papers.ts
index 1dfd553d2..86b86fd2f 100644
--- a/lib/routes/trendingpapers/papers.ts
+++ b/lib/routes/trendingpapers/papers.ts
@@ -1,7 +1,30 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/papers/:category?/:time?/:cited?',
+ categories: ['finance'],
+ example: '/trendingpapers/papers',
+ parameters: {
+ category: 'Category of papers, can be found in URL. `All categories` by default.',
+ time: 'Time like `24 hours` to specify the duration of ranking, can be found in URL. `Since beginning` by default.',
+ cited: 'Cited or uncited papers, can be found in URL. `Cited and uncited papers` by default.',
+ },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Trending Papers on arXiv',
+ maintainers: ['CookiePieWw'],
+ handler,
+};
+
+async function handler(ctx) {
const { time = 'Since beginning', cited = 'Cited and uncited papers', category = 'All categories' } = ctx.req.param();
const rootUrl = 'https://trendingpapers.com';
@@ -33,9 +56,9 @@ export default async (ctx) => {
};
});
- ctx.set('data', {
+ return {
title: `Trending Papers on arXiv.org | ${category} | ${time} | ${cited} | `,
link: currentUrl,
item: papers,
- });
-};
+ };
+}
diff --git a/lib/routes/tribalfootball/latest.ts b/lib/routes/tribalfootball/latest.ts
index d7a1dca04..7db7a2b68 100644
--- a/lib/routes/tribalfootball/latest.ts
+++ b/lib/routes/tribalfootball/latest.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -10,7 +11,18 @@ import { parseDate } from '@/utils/parse-date';
const rssUrl = 'https://www.tribalfootball.com/rss/mediafed/general/rss.xml';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/',
+ radar: {
+ source: ['tribalfootball.com/'],
+ target: '',
+ },
+ name: 'Unknown',
+ maintainers: ['Rongronggg9'],
+ handler,
+};
+
+async function handler() {
const rss = await got(rssUrl);
const $ = load(rss.data, { xmlMode: true });
const items = $('rss > channel > item')
@@ -62,11 +74,11 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: 'Tribal Football - Latest',
description: 'Tribal Football - Football News, Soccer News, Transfers & Rumours',
link: 'https://www.tribalfootball.com/articles',
image: 'https://www.tribalfootball.com/images/tribal-logo-rss.png',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/trow/portal.ts b/lib/routes/trow/portal.ts
index edeb3796a..e5d9f22ab 100644
--- a/lib/routes/trow/portal.ts
+++ b/lib/routes/trow/portal.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/portal',
+ categories: ['blog'],
+ example: '/trow/portal',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['trow.cc/'],
+ },
+ name: '首页更新',
+ maintainers: ['shiningdracon'],
+ handler,
+};
+
+async function handler() {
let data;
const response = await got.extend({ followRedirect: false }).get({
url: `https://trow.cc`,
@@ -23,7 +45,7 @@ export default async (ctx) => {
const $ = load(data);
const list = $('#portal_content .borderwrap[style="display:show"]');
- ctx.set('data', {
+ return {
title: `The Ring of Wonder - Portal`,
link: `https://trow.cc`,
description: `The Ring of Wonder 首页更新`,
@@ -42,5 +64,5 @@ export default async (ctx) => {
};
})
.get(),
- });
-};
+ };
+}
diff --git a/lib/routes/tvb/news.ts b/lib/routes/tvb/news.ts
index 858565dba..98f4fcb6d 100644
--- a/lib/routes/tvb/news.ts
+++ b/lib/routes/tvb/news.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -45,7 +46,28 @@ const titles = {
},
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/:category?/:language?',
+ categories: ['bbs'],
+ example: '/tvb/news',
+ parameters: { category: '分类,见下表,默认为要聞', language: '语言,见下表' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['tvb.com/:language/:category', 'tvb.com/'],
+ },
+ name: '新闻',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? 'focus';
const language = ctx.req.param('language') ?? 'tc';
@@ -75,9 +97,9 @@ export default async (ctx) => {
}),
}));
- ctx.set('data', {
+ return {
title: `${response.data.meta.title} - ${titles[category][language]}`,
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/tvtropes/featured.ts b/lib/routes/tvtropes/featured.ts
index 4cd1dec79..8744b053f 100644
--- a/lib/routes/tvtropes/featured.ts
+++ b/lib/routes/tvtropes/featured.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -11,7 +12,25 @@ const categories = {
newest: 'right',
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/featured/:category?',
+ categories: ['other'],
+ example: '/tvtropes/featured/today',
+ parameters: { category: "Category, see below, Today's Featured Trope by default" },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Featured',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const { category = 'today' } = ctx.req.param();
const rootUrl = 'https://tvtropes.org';
@@ -60,7 +79,7 @@ export default async (ctx) => {
const image = new URL($('img.logo-big').prop('src'), rootUrl).href;
const icon = $('link[rel="shortcut icon"]').prop('href');
- ctx.set('data', {
+ return {
item: items,
title: `${$('title').text()} - ${item.find('span.box-title').text()}`,
link: rootUrl,
@@ -72,5 +91,5 @@ export default async (ctx) => {
subtitle: $('meta[property="og:title"]').prop('content'),
author: $('meta[property="og:site_name"]').prop('content'),
allowEmpty: true,
- });
-};
+ };
+}
diff --git a/lib/routes/twitch/live.ts b/lib/routes/twitch/live.ts
index ba62e95b5..ced918a6e 100644
--- a/lib/routes/twitch/live.ts
+++ b/lib/routes/twitch/live.ts
@@ -1,10 +1,29 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
// https://github.com/streamlink/streamlink/blob/master/src/streamlink/plugins/twitch.py#L286
const TWITCH_CLIENT_ID = 'kimne78kx3ncx6brgo4mv6wki5h1ko';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/live/:login',
+ categories: ['multimedia'],
+ example: '/twitch/live/riotgames',
+ parameters: { login: 'Twitch username' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Live',
+ maintainers: ['hoilc'],
+ handler,
+};
+
+async function handler(ctx) {
const login = ctx.req.param('login');
const response = await got({
@@ -79,10 +98,10 @@ export default async (ctx) => {
});
}
- ctx.set('data', {
+ return {
title: `Twitch - ${displayName} - Live`,
link: `https://www.twitch.tv/${login}`,
item: liveItem,
allowEmpty: true,
- });
-};
+ };
+}
diff --git a/lib/routes/twitch/schedule.ts b/lib/routes/twitch/schedule.ts
index 2fd170511..72288ec62 100644
--- a/lib/routes/twitch/schedule.ts
+++ b/lib/routes/twitch/schedule.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import got from '@/utils/got';
// https://github.com/streamlink/streamlink/blob/master/src/streamlink/plugins/twitch.py#L286
const TWITCH_CLIENT_ID = 'kimne78kx3ncx6brgo4mv6wki5h1ko';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/schedule/:login',
+ categories: ['multimedia'],
+ example: '/twitch/schedule/riotgames',
+ parameters: { login: 'Twitch username' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.twitch.tv/:login/schedule'],
+ },
+ name: 'Stream Schedule',
+ maintainers: ['hoilc'],
+ handler,
+};
+
+async function handler(ctx) {
const login = ctx.req.param('login');
const today = new Date();
@@ -67,10 +89,10 @@ export default async (ctx) => {
category: item.categories.map((item) => item.name),
}));
- ctx.set('data', {
+ return {
title: `Twitch - ${displayName} - Schedule`,
link: `https://www.twitch.tv/${login}`,
item: out,
allowEmpty: true,
- });
-};
+ };
+}
diff --git a/lib/routes/twitch/video.ts b/lib/routes/twitch/video.ts
index c434da3ec..855c98012 100644
--- a/lib/routes/twitch/video.ts
+++ b/lib/routes/twitch/video.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -10,7 +11,29 @@ const FILTER_NODE_TYPE_MAP = {
all: 'ALL_VIDEOS',
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/video/:login/:filter?',
+ categories: ['multimedia'],
+ example: '/twitch/video/riotgames/highlights',
+ parameters: { login: 'Twitch username', filter: 'Video type, Default to all' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.twitch.tv/:login/videos'],
+ target: '/video/:login',
+ },
+ name: 'Channel Video',
+ maintainers: ['hoilc'],
+ handler,
+};
+
+async function handler(ctx) {
const login = ctx.req.param('login');
const filter = ctx.req.param('filter')?.toLowerCase() || 'all';
if (!FILTER_NODE_TYPE_MAP[filter]) {
@@ -63,9 +86,9 @@ export default async (ctx) => {
category: item.game && [item.game.displayName], // item.game may be null
}));
- ctx.set('data', {
+ return {
title: `Twitch - ${displayName} - ${videoShelvesEdge.node.title}`,
link: `https://www.twitch.tv/${login}`,
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/twitter/collection.ts b/lib/routes/twitter/collection.ts
index 6f4cd3b7d..c510ffd67 100644
--- a/lib/routes/twitter/collection.ts
+++ b/lib/routes/twitter/collection.ts
@@ -1,10 +1,29 @@
+import { Route } from '@/types';
import utils from './utils';
import { config } from '@/config';
const T = {};
import { TwitterApi } from 'twitter-api-v2';
import { fallback, queryToBoolean } from '@/utils/readable-social';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/collection/:uid/:collectionId/:routeParams?',
+ categories: ['new-media'],
+ example: '/twitter/collection/DIYgod/1527857429467172864',
+ parameters: { uid: 'username, should match the generated token', collectionId: 'collection ID, can be found in URL', routeParams: 'extra parameters, see the table above' },
+ features: {
+ requireConfig: true,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Collection',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const uid = ctx.req.param('uid');
const collectionId = ctx.req.param('collectionId');
const cookie = config.twitter.tokens[uid];
@@ -37,7 +56,7 @@ export default async (ctx) => {
}
}
- ctx.set('data', {
+ return {
title: data.objects.timelines[id].name,
description: data.objects.timelines[id].description,
link: data.objects.timelines[id].collection_url,
@@ -51,5 +70,5 @@ export default async (ctx) => {
showAuthorInDesc: fallback(undefined, queryToBoolean(routeParams.get('showAuthorInDesc')), true),
}
),
- });
-};
+ };
+}
diff --git a/lib/routes/twitter/developer-api/search.ts b/lib/routes/twitter/developer-api/search.ts
index 551d10db6..3afb40b4a 100644
--- a/lib/routes/twitter/developer-api/search.ts
+++ b/lib/routes/twitter/developer-api/search.ts
@@ -11,12 +11,12 @@ export default async (ctx) => {
result_type: 'recent',
});
- ctx.set('data', {
+ return {
title: `Twitter Keyword - ${keyword}`,
link: `https://twitter.com/search?q=${encodeURIComponent(keyword)}`,
item: utils.ProcessFeed(ctx, {
data: data.statuses,
}),
allowEmpty: true,
- });
+ };
};
diff --git a/lib/routes/twitter/developer-api/user.ts b/lib/routes/twitter/developer-api/user.ts
index 8eb27f724..9153336af 100644
--- a/lib/routes/twitter/developer-api/user.ts
+++ b/lib/routes/twitter/developer-api/user.ts
@@ -25,7 +25,7 @@ export default async (ctx) => {
}
const profileImageUrl = userInfo.profile_image_url || userInfo.profile_image_url_https;
- ctx.set('data', {
+ return {
title: `Twitter @${userInfo.name}`,
link: `https://twitter.com/${screen_name}`,
image: profileImageUrl,
@@ -33,5 +33,5 @@ export default async (ctx) => {
item: utils.ProcessFeed(ctx, {
data,
}),
- });
+ };
};
diff --git a/lib/routes/twitter/followings.ts b/lib/routes/twitter/followings.ts
index 0dfc32f0e..b03877dbd 100644
--- a/lib/routes/twitter/followings.ts
+++ b/lib/routes/twitter/followings.ts
@@ -1,10 +1,29 @@
+import { Route } from '@/types';
import utils from './utils';
import { config } from '@/config';
const T = {};
import { TwitterApi } from 'twitter-api-v2';
import { fallback, queryToBoolean } from '@/utils/readable-social';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/followings/:id/:routeParams?',
+ categories: ['new-media'],
+ example: '/twitter/followings/DIYgod',
+ parameters: { id: 'username', routeParams: 'extra parameters, see the table above' },
+ features: {
+ requireConfig: true,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'User following timeline',
+ maintainers: ['DIYgod'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const cookie = config.twitter.tokens[id];
if (!cookie) {
@@ -27,7 +46,7 @@ export default async (ctx) => {
// undefined and strings like "exclude_rts_replies" is also safely parsed, so no if branch is needed
const routeParams = new URLSearchParams(ctx.req.param('routeParams'));
- ctx.set('data', {
+ return {
title: `${id} 的 Twitter 关注时间线`,
link: `https://twitter.com/${id}/`,
item: utils.ProcessFeed(
@@ -40,5 +59,5 @@ export default async (ctx) => {
showAuthorInDesc: fallback(undefined, queryToBoolean(routeParams.get('showAuthorInDesc')), true),
}
),
- });
-};
+ };
+}
diff --git a/lib/routes/twitter/keyword.ts b/lib/routes/twitter/keyword.ts
index 027c410c4..c9c3c30c6 100644
--- a/lib/routes/twitter/keyword.ts
+++ b/lib/routes/twitter/keyword.ts
@@ -1,3 +1,24 @@
+import { Route } from '@/types';
import webApiImpl from './web-api/search';
-export default async (ctx) => await webApiImpl(ctx);
+export const route: Route = {
+ path: '/keyword/:keyword/:routeParams?',
+ categories: ['new-media'],
+ example: '/twitter/keyword/RSSHub',
+ parameters: { keyword: 'keyword', routeParams: 'extra parameters, see the table above' },
+ features: {
+ requireConfig: true,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Keyword',
+ maintainers: ['DIYgod', 'yindaheng98', 'Rongronggg9'],
+ handler,
+};
+
+async function handler(ctx) {
+ return await webApiImpl(ctx);
+}
diff --git a/lib/routes/twitter/likes.ts b/lib/routes/twitter/likes.ts
index 2ca952023..ce7cce2d5 100644
--- a/lib/routes/twitter/likes.ts
+++ b/lib/routes/twitter/likes.ts
@@ -1,7 +1,26 @@
+import { Route } from '@/types';
import utils from './utils';
import { config } from '@/config';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/likes/:id/:routeParams?',
+ categories: ['new-media'],
+ example: '/twitter/likes/DIYgod',
+ parameters: { id: 'username', routeParams: 'extra parameters, see the table above' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'User likes',
+ maintainers: ['xyqfer'],
+ handler,
+};
+
+async function handler(ctx) {
if (!config.twitter || !config.twitter.consumer_key || !config.twitter.consumer_secret) {
throw new Error('Twitter RSS is disabled due to the lack of relevant config');
}
@@ -12,11 +31,11 @@ export default async (ctx) => {
tweet_mode: 'extended',
});
- ctx.set('data', {
+ return {
title: `Twitter Likes - ${id}`,
link: `https://twitter.com/${id}/likes`,
item: utils.ProcessFeed(ctx, {
data,
}),
- });
-};
+ };
+}
diff --git a/lib/routes/twitter/list.ts b/lib/routes/twitter/list.ts
index bb32e1629..8780a6b12 100644
--- a/lib/routes/twitter/list.ts
+++ b/lib/routes/twitter/list.ts
@@ -1,8 +1,27 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import utils from './utils';
import { config } from '@/config';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/list/:id/:name/:routeParams?',
+ categories: ['new-media'],
+ example: '/twitter/list/ladyleet/javascript',
+ parameters: { id: 'username', name: 'list name', routeParams: 'extra parameters, see the table above' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'List timeline',
+ maintainers: ['xyqfer'],
+ handler,
+};
+
+async function handler(ctx) {
if (!config.twitter || !config.twitter.consumer_key || !config.twitter.consumer_secret) {
throw new Error('Twitter RSS is disabled due to the lack of relevant config');
}
@@ -29,11 +48,11 @@ export default async (ctx) => {
tweet_mode: 'extended',
});
- ctx.set('data', {
+ return {
title: `Twitter List - ${id}/${name}`,
link: `https://twitter.com/${id}/lists/${name}`,
item: utils.ProcessFeed(ctx, {
data,
}),
- });
-};
+ };
+}
diff --git a/lib/routes/twitter/media.ts b/lib/routes/twitter/media.ts
index 81eba9162..1c30bba75 100644
--- a/lib/routes/twitter/media.ts
+++ b/lib/routes/twitter/media.ts
@@ -1,3 +1,24 @@
+import { Route } from '@/types';
import webApiImpl from './web-api/media';
-export default async (ctx) => await webApiImpl(ctx);
+export const route: Route = {
+ path: '/media/:id/:routeParams?',
+ categories: ['new-media'],
+ example: '/twitter/media/DIYgod',
+ parameters: { id: 'username; in particular, if starts with `+`, it will be recognized as a [unique ID](https://github.com/DIYgod/RSSHub/issues/12221), e.g. `+44196397`', routeParams: 'extra parameters, see the table above.' },
+ features: {
+ requireConfig: true,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'User media',
+ maintainers: ['yindaheng98', 'Rongronggg9'],
+ handler,
+};
+
+async function handler(ctx) {
+ return await webApiImpl(ctx);
+}
diff --git a/lib/routes/twitter/trends.ts b/lib/routes/twitter/trends.ts
index bd068e9ee..69b5e1aeb 100644
--- a/lib/routes/twitter/trends.ts
+++ b/lib/routes/twitter/trends.ts
@@ -1,7 +1,26 @@
+import { Route } from '@/types';
import utils from './utils';
import { config } from '@/config';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/trends/:woeid?',
+ categories: ['new-media'],
+ example: '/twitter/trends/23424856',
+ parameters: { woeid: 'Yahoo! Where On Earth ID. default to woeid=1 (World Wide)' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Trends',
+ maintainers: ['sakamossan'],
+ handler,
+};
+
+async function handler(ctx) {
if (!config.twitter || !config.twitter.consumer_key || !config.twitter.consumer_secret) {
throw new Error('Twitter RSS is disabled due to the lack of relevant config');
}
@@ -10,7 +29,7 @@ export default async (ctx) => {
const data = await client.v1.get('trends/place.json', { id: woeid });
const [{ trends }] = data;
- ctx.set('data', {
+ return {
title: `Twitter Trends on ${data[0].locations[0].name}`,
link: `https://twitter.com/i/trends`,
item: trends
@@ -20,5 +39,5 @@ export default async (ctx) => {
link: t.url,
description: t.name + (t.tweet_volume ? ` (${t.tweet_volume})` : ''),
})),
- });
-};
+ };
+}
diff --git a/lib/routes/twitter/tweet.ts b/lib/routes/twitter/tweet.ts
index 16d93a203..47f10d302 100644
--- a/lib/routes/twitter/tweet.ts
+++ b/lib/routes/twitter/tweet.ts
@@ -1,5 +1,28 @@
+import { Route } from '@/types';
import webApiImpl from './web-api/tweet';
-export default async (ctx) => {
- await webApiImpl(ctx);
+export const route: Route = {
+ path: '/tweet/:id/status/:status/:original?',
+ categories: ['new-media'],
+ example: '/twitter/tweet/DIYgod/status/1650844643997646852',
+ parameters: {
+ id: 'username; in particular, if starts with `+`, it will be recognized as a [unique ID](https://github.com/DIYgod/RSSHub/issues/12221), e.g. `+44196397`',
+ status: 'tweet ID',
+ original: 'extra parameters, data type of return, if the value is not `0`/`false` and `config.isPackage` is `true`, return the original data of twitter',
+ },
+ features: {
+ requireConfig: true,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Tweet Details',
+ maintainers: ['LarchLiu', 'Rongronggg9'],
+ handler,
};
+
+async function handler(ctx) {
+ return await webApiImpl(ctx);
+}
diff --git a/lib/routes/twitter/user.ts b/lib/routes/twitter/user.ts
index c3074f040..71514c3bf 100644
--- a/lib/routes/twitter/user.ts
+++ b/lib/routes/twitter/user.ts
@@ -1,3 +1,28 @@
+import { Route } from '@/types';
import webApiImpl from './web-api/user';
-export default (ctx) => webApiImpl(ctx);
+export const route: Route = {
+ path: '/user/:id/:routeParams?',
+ categories: ['new-media'],
+ example: '/twitter/user/DIYgod',
+ parameters: {
+ id: 'username; in particular, if starts with `+`, it will be recognized as a [unique ID](https://github.com/DIYgod/RSSHub/issues/12221), e.g. `+44196397`',
+ routeParams:
+ 'extra parameters, see the table above; particularly when `routeParams=exclude_replies`, replies are excluded; `routeParams=exclude_rts` excludes retweets,`routeParams=exclude_rts_replies` exclude replies and retweets; for default include all.',
+ },
+ features: {
+ requireConfig: true,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'User timeline',
+ maintainers: ['DIYgod', 'yindaheng98', 'Rongronggg9'],
+ handler,
+};
+
+async function handler(ctx) {
+ return await webApiImpl(ctx);
+}
diff --git a/lib/routes/twitter/web-api/media.ts b/lib/routes/twitter/web-api/media.ts
index 4a1ab7918..9aa8c472a 100644
--- a/lib/routes/twitter/web-api/media.ts
+++ b/lib/routes/twitter/web-api/media.ts
@@ -13,7 +13,7 @@ export default async (ctx) => {
const data = await getUserMedia(id, params);
const profileImageUrl = userInfo.profile_image_url || userInfo.profile_image_url_https;
- ctx.set('data', {
+ return {
title: `Twitter @${userInfo.name}`,
link: `https://twitter.com/${userInfo.screen_name}/media`,
image: profileImageUrl.replace(/_normal.jpg$/, '.jpg'),
@@ -21,5 +21,5 @@ export default async (ctx) => {
item: utils.ProcessFeed(ctx, {
data,
}),
- });
+ };
};
diff --git a/lib/routes/twitter/web-api/search.ts b/lib/routes/twitter/web-api/search.ts
index dc83731f1..2544e6ed9 100644
--- a/lib/routes/twitter/web-api/search.ts
+++ b/lib/routes/twitter/web-api/search.ts
@@ -7,12 +7,12 @@ export default async (ctx) => {
await initToken();
const data = await getSearch(keyword);
- ctx.set('data', {
+ return {
title: `Twitter Keyword - ${keyword}`,
link: `https://twitter.com/search?q=${encodeURIComponent(keyword)}`,
item: utils.ProcessFeed(ctx, {
data,
}),
allowEmpty: true,
- });
+ };
};
diff --git a/lib/routes/twitter/web-api/tweet.ts b/lib/routes/twitter/web-api/tweet.ts
index 081e06989..b0bcb3f3d 100644
--- a/lib/routes/twitter/web-api/tweet.ts
+++ b/lib/routes/twitter/web-api/tweet.ts
@@ -17,11 +17,11 @@ export default async (ctx) => {
const profileImageUrl = userInfo.profile_image_url || userInfo.profile_image_url_https;
const item = original && config.isPackage ? data : utils.ProcessFeed(ctx, { data });
- ctx.set('data', {
+ return {
title: `Twitter @${userInfo.name}`,
link: `https://twitter.com/${userInfo.screen_name}/status/${status}`,
image: profileImageUrl.replace(/_normal.jpg$/, '.jpg'),
description: userInfo.description,
item,
- });
+ };
};
diff --git a/lib/routes/twitter/web-api/user.ts b/lib/routes/twitter/web-api/user.ts
index 5670e59ac..7ee0d88cc 100644
--- a/lib/routes/twitter/web-api/user.ts
+++ b/lib/routes/twitter/web-api/user.ts
@@ -18,7 +18,7 @@ export default async (ctx) => {
const profileImageUrl = userInfo.profile_image_url || userInfo.profile_image_url_https;
- ctx.set('data', {
+ return {
title: `Twitter @${userInfo.name}`,
link: `https://twitter.com/${userInfo.screen_name}`,
image: profileImageUrl.replace(/_normal.jpg$/, '.jpg'),
@@ -26,5 +26,5 @@ export default async (ctx) => {
item: utils.ProcessFeed(ctx, {
data,
}),
- });
+ };
};
diff --git a/lib/routes/twreporter/newest.ts b/lib/routes/twreporter/newest.ts
index 0eea8a05d..419fa1b3a 100644
--- a/lib/routes/twreporter/newest.ts
+++ b/lib/routes/twreporter/newest.ts
@@ -1,10 +1,32 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import { load } from 'cheerio';
import got from '@/utils/got';
import fetch from './fetch-article';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/newest',
+ categories: ['traditional-media'],
+ example: '/twreporter/newest',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['twreporter.org/'],
+ },
+ name: '最新',
+ maintainers: ['emdoe'],
+ handler,
+};
+
+async function handler() {
const url = 'https://www.twreporter.org';
const res = await got(url);
const $ = load(res.data);
@@ -22,9 +44,9 @@ export default async (ctx) => {
});
})
);
- ctx.set('data', {
+ return {
title: `報導者 | 最新`,
link: url,
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/txrjy/fornumtopic.ts b/lib/routes/txrjy/fornumtopic.ts
index f1d88920d..1944d5ff9 100644
--- a/lib/routes/txrjy/fornumtopic.ts
+++ b/lib/routes/txrjy/fornumtopic.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -12,7 +13,25 @@ import * as path from 'node:path';
const rootUrl = 'https://www.txrjy.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/fornumtopic/:channel?',
+ categories: ['blog'],
+ example: '/txrjy/fornumtopic',
+ parameters: { channel: '频道的 id,见下表,默认为最新500个主题帖' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '论坛 频道',
+ maintainers: ['Fatpandac'],
+ handler,
+};
+
+async function handler(ctx) {
const channel = ctx.req.param('channel') ?? '1';
const url = `${rootUrl}/c114-listnewtopic.php?typeid=${channel}`;
@@ -68,9 +87,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `通信人家园 - 论坛 ${title}`,
link: url,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/tynu/tynu.ts b/lib/routes/tynu/tynu.ts
index 23cb7c785..a68918fcf 100644
--- a/lib/routes/tynu/tynu.ts
+++ b/lib/routes/tynu/tynu.ts
@@ -1,10 +1,22 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
const baseUrl = 'http://www.tynu.edu.cn';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/',
+ radar: {
+ source: ['tynu.edu.cn/index/tzgg.htm', 'tynu.edu.cn/index.htm', 'tynu.edu.cn/'],
+ target: '',
+ },
+ name: 'Unknown',
+ maintainers: ['2PoL'],
+ handler,
+};
+
+async function handler() {
const link = `${baseUrl}/index/tzgg.htm`;
const response = await got(link);
const data = response.data;
@@ -32,9 +44,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: '太原师范学院通知公告',
link,
item: list,
- });
-};
+ };
+}
diff --git a/lib/routes/typora/changelog-dev.ts b/lib/routes/typora/changelog-dev.ts
index 06402805b..9fa73b0d1 100644
--- a/lib/routes/typora/changelog-dev.ts
+++ b/lib/routes/typora/changelog-dev.ts
@@ -1,7 +1,30 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/changelog/dev',
+ categories: ['university'],
+ example: '/typora/changelog/dev',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['support.typora.io/'],
+ target: '/changelog',
+ },
+ name: 'Dev Release Changelog',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler() {
const currentUrl = 'https://typora.io/releases/dev';
const response = await got(currentUrl);
@@ -22,10 +45,10 @@ export default async (ctx) => {
};
});
- ctx.set('data', {
+ return {
title: `Typora Changelog - Dev`,
link: currentUrl,
description: 'Typora Changelog',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/typora/changelog.ts b/lib/routes/typora/changelog.ts
index 3b550b297..375d0c90d 100644
--- a/lib/routes/typora/changelog.ts
+++ b/lib/routes/typora/changelog.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/changelog',
+ categories: ['university'],
+ example: '/typora/changelog',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['support.typora.io/'],
+ },
+ name: 'Changelog',
+ maintainers: ['cnzgray'],
+ handler,
+};
+
+async function handler() {
const host = 'https://support.typora.io';
const { data } = await got(`${host}/store/`);
@@ -31,11 +53,11 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: 'Typora Changelog',
link: host,
description: 'Typora Changelog',
image: `${host}/assets/img/favicon-128.png`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/u3c3/index.ts b/lib/routes/u3c3/index.ts
index acd41d539..a4732d75a 100644
--- a/lib/routes/u3c3/index.ts
+++ b/lib/routes/u3c3/index.ts
@@ -1,8 +1,27 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
-export default async (ctx) => {
+export const route: Route = {
+ path: ['/search/:keyword/:preview?', '/:type?/:preview?'],
+ categories: ['picture'],
+ example: '/u9a9/search/新片速递',
+ parameters: { keyword: 'Search keyword', preview: 'Show image preview, off by default, non empty value means on' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: true,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Search',
+ maintainers: ['storytellerF'],
+ handler,
+};
+
+async function handler(ctx) {
const { type, keywoard, preview } = ctx.req.param();
// should be:
// undefined
@@ -65,10 +84,10 @@ export default async (ctx) => {
)
: list;
- ctx.set('data', {
+ return {
title,
description: title,
link: currentURL,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/u9a9/index.ts b/lib/routes/u9a9/index.ts
index 4e0783377..601bde88f 100644
--- a/lib/routes/u9a9/index.ts
+++ b/lib/routes/u9a9/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -6,7 +7,18 @@ import timezone from '@/utils/timezone';
const baseUrl = 'https://u9a9.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: ['/:preview?', '/search/:keyword/:preview?'],
+ radar: {
+ source: ['u9a9.com/'],
+ target: '',
+ },
+ name: 'Unknown',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const { preview, keyword } = ctx.req.param();
let link;
@@ -59,9 +71,9 @@ export default async (ctx) => {
)
: list;
- ctx.set('data', {
+ return {
title,
link,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/uber/blog.ts b/lib/routes/uber/blog.ts
index 379544b55..5b7cf1035 100644
--- a/lib/routes/uber/blog.ts
+++ b/lib/routes/uber/blog.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -5,7 +6,29 @@ import { parseDate } from '@/utils/parse-date';
const rootURL = 'https://www.uber.com';
const apiURL = 'https://blogapi.uber.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/blog/:maxPage?',
+ categories: ['programming'],
+ example: '/uber/blog',
+ parameters: { maxPage: 'max number of pages to retrieve, default to 1 page at most' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.uber.com/blog/pittsburgh/engineering'],
+ target: '/blog',
+ },
+ name: 'Engineering',
+ maintainers: ['hulb'],
+ handler,
+};
+
+async function handler(ctx) {
let maxPage = Number(ctx.req.param('maxPage'));
if (Number.isNaN(maxPage)) {
maxPage = 1;
@@ -50,10 +73,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `Uber Engineering Blog`,
link: rootURL + '/blog/engineering',
description: 'The technology behind Uber Engineering',
item: result.flat(),
- });
-};
+ };
+}
diff --git a/lib/routes/ucas/ai.ts b/lib/routes/ucas/ai.ts
index dda5af2be..dbbf8a692 100644
--- a/lib/routes/ucas/ai.ts
+++ b/lib/routes/ucas/ai.ts
@@ -1,15 +1,37 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/ai',
+ categories: ['forecast'],
+ example: '/ucas/ai',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['ai.ucas.ac.cn/index.php/zh-cn/tzgg', 'ai.ucas.ac.cn/'],
+ },
+ name: '人工智能学院',
+ maintainers: ['shengmaosu'],
+ handler,
+};
+
+async function handler() {
const baseUrl = 'https://ai.ucas.ac.cn';
const link = `${baseUrl}/index.php/zh-cn/tzgg`;
const response = await got(link);
const $ = load(response.data);
const list = $('.b-list li');
- ctx.set('data', {
+ return {
title: '中科院人工智能所',
link,
description: '中科院人工智能通知公告',
@@ -24,5 +46,5 @@ export default async (ctx) => {
pubDate: parseDate(item.find('.m-date').text(), 'YYYY-MM-DD'),
};
}),
- });
-};
+ };
+}
diff --git a/lib/routes/ucas/index.ts b/lib/routes/ucas/index.ts
index 607648522..6d36b8609 100644
--- a/lib/routes/ucas/index.ts
+++ b/lib/routes/ucas/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -19,7 +20,25 @@ const titleMap = {
bsh: '博士后',
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/job/:type?',
+ categories: ['forecast'],
+ example: '/ucas/job',
+ parameters: { type: '招聘类型,默认为博士后' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '招聘信息',
+ maintainers: ['Fatpandac'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type') ?? 'bsh';
const url = `${rootUrl}/gjob/login.do?method=contentList&t=zhaopin&c=${idMap[type]}`;
const response = await got.get(url);
@@ -52,9 +71,9 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: `中国科学院大学招聘 - ${titleMap[type]}`,
link: url,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/uchicago/current.ts b/lib/routes/uchicago/current.ts
index c0571cbe4..532a899ff 100644
--- a/lib/routes/uchicago/current.ts
+++ b/lib/routes/uchicago/current.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
@@ -5,7 +6,28 @@ import { getCookies, setCookies } from '@/utils/puppeteer-utils';
import logger from '@/utils/logger';
import puppeteer from '@/utils/puppeteer';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/journals/current/:journal',
+ categories: ['finance'],
+ example: '/uchicago/journals/current/jpe',
+ parameters: { journal: 'Journal id, can be found in URL. [Browse journals by title](https://www.journals.uchicago.edu/action/showPublications)' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['journals.uchicago.edu/toc/:journal/current', 'journals.uchicago.edu/journal/:journal'],
+ },
+ name: 'Current Issue',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const journal = ctx.req.param('journal');
const baseUrl = 'https://www.journals.uchicago.edu';
const link = `${baseUrl}/toc/${journal}/current`;
@@ -72,11 +94,11 @@ export default async (ctx) => {
browser.close();
- ctx.set('data', {
+ return {
title: $('head title').text(),
description: $('.jumbotron-journal-info').text(),
link,
image: $('head meta[property="og:image"]').attr('content'),
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/udn/breaking-news.ts b/lib/routes/udn/breaking-news.ts
index dcc9bd4c5..42e83d055 100644
--- a/lib/routes/udn/breaking-news.ts
+++ b/lib/routes/udn/breaking-news.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -9,7 +10,28 @@ import { load } from 'cheerio';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/breakingnews/:id',
+ categories: ['bbs'],
+ example: '/udn/news/breakingnews/99',
+ parameters: { id: '类别' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['udn.com/news/breaknews/1/:id', 'udn.com/'],
+ },
+ name: '即時新聞',
+ maintainers: ['miles170'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const link = `/news/breaknews/1/${id}#breaknews`;
const name = await getLinkName(link);
@@ -70,13 +92,13 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: `即時${name} - 聯合新聞網`,
link: `${rootUrl}${link}`,
description: 'udn.com 提供即時新聞以及豐富的政治、社會、地方、兩岸、國際、財經、數位、運動、NBA、娛樂、生活、健康、旅遊新聞,以最即時、多元的內容,滿足行動世代的需求',
item: items,
- });
-};
+ };
+}
const getLinkName = async (link) => {
const url = 'https://udn.com/news/breaknews';
diff --git a/lib/routes/udn/global/index.ts b/lib/routes/udn/global/index.ts
index f2a36ceaf..f9bb0c356 100644
--- a/lib/routes/udn/global/index.ts
+++ b/lib/routes/udn/global/index.ts
@@ -1,10 +1,32 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/global/:category?',
+ categories: ['bbs'],
+ example: '/udn/global',
+ parameters: { category: '分类,见下表,默认为首頁' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['global.udn.com/global_vision/index/:category', 'global.udn.com/'],
+ },
+ name: '轉角國際 - 首頁',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? '';
const start = category === 'hot' ? 6 : 0;
@@ -56,9 +78,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('title').text(),
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/udn/global/tag.ts b/lib/routes/udn/global/tag.ts
index 58cf685e2..15830bd02 100644
--- a/lib/routes/udn/global/tag.ts
+++ b/lib/routes/udn/global/tag.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/global/tag/:tag?',
+ categories: ['bbs'],
+ example: '/udn/global/tag/過去24小時',
+ parameters: { tag: '标签,可在对应标签页 URL 中找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['global.udn.com/search/tagging/1020/:tag', 'global.udn.com/'],
+ },
+ name: '轉角國際 - 標籤',
+ maintainers: ['emdoe', 'nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const tag = ctx.req.param('tag') ?? '過去24小時';
const rootUrl = 'https://global.udn.com';
@@ -44,9 +66,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `轉角國際 udn Global - ${tag}`,
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/uestc/auto.ts b/lib/routes/uestc/auto.ts
index ec08536c3..23db5e179 100644
--- a/lib/routes/uestc/auto.ts
+++ b/lib/routes/uestc/auto.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import puppeteer from '@/utils/puppeteer';
@@ -5,7 +6,28 @@ import puppeteer from '@/utils/puppeteer';
const baseIndexUrl = 'https://www.auto.uestc.edu.cn/index/tzgg1.htm';
const host = 'https://www.auto.uestc.edu.cn/';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/auto',
+ categories: ['forecast'],
+ example: '/uestc/auto',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: true,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['auto.uestc.edu.cn/'],
+ },
+ name: '自动化工程学院',
+ maintainers: ['talengu', 'mobyw'],
+ handler,
+};
+
+async function handler() {
const browser = await puppeteer({ stealth: true });
const page = await browser.newPage();
await page.setRequestInterception(true);
@@ -37,10 +59,10 @@ export default async (ctx) => {
})
.get();
- ctx.set('data', {
+ return {
title: '电子科技大学自动化学院通知',
link: baseIndexUrl,
description: '电子科技大学自动化工程学院通知',
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/uestc/cqe.ts b/lib/routes/uestc/cqe.ts
index 702b3fc7f..088569cd3 100644
--- a/lib/routes/uestc/cqe.ts
+++ b/lib/routes/uestc/cqe.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import puppeteer from '@/utils/puppeteer';
@@ -14,7 +15,29 @@ const mapTitle = {
tzgg: '通知公告',
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/cqe/:type?',
+ categories: ['forecast'],
+ example: '/uestc/cqe/tzgg',
+ parameters: { type: '默认为 `tzgg`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: true,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['cqe.uestc.edu.cn/'],
+ target: '/cqe',
+ },
+ name: '文化素质教育中心',
+ maintainers: ['truobel', 'mobyw'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type') || 'tzgg';
const pageUrl = mapUrl[type];
if (!pageUrl) {
@@ -52,10 +75,10 @@ export default async (ctx) => {
})
.get();
- ctx.set('data', {
+ return {
title: `大学生文化素质教育中心-${mapTitle[type]}`,
link: baseUrl,
description: '电子科技大学大学生文化素质教育中心通知',
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/uestc/gr.ts b/lib/routes/uestc/gr.ts
index ad29df19e..4ba8d92c1 100644
--- a/lib/routes/uestc/gr.ts
+++ b/lib/routes/uestc/gr.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -6,7 +7,28 @@ import { parseDate } from '@/utils/parse-date';
const baseUrl = 'https://gr.uestc.edu.cn/tongzhi/';
const baseIndexUrl = 'https://gr.uestc.edu.cn';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/gr',
+ categories: ['forecast'],
+ example: '/uestc/gr',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['gr.uestc.edu.cn/'],
+ },
+ name: '研究生院',
+ maintainers: ['huyyi', 'mobyw'],
+ handler,
+};
+
+async function handler() {
const response = await got.get(baseIndexUrl);
const $ = load(response.data);
@@ -40,10 +62,10 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: '研究生院通知',
link: baseUrl,
description: '电子科技大学研究生院通知公告',
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/uestc/jwc.ts b/lib/routes/uestc/jwc.ts
index 021c6b5bd..267d5f19a 100644
--- a/lib/routes/uestc/jwc.ts
+++ b/lib/routes/uestc/jwc.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
@@ -15,7 +16,29 @@ const map = {
office: 'list/ff80808160bcf79c0160c010a8d20020/?page=1',
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/jwc/:type?',
+ categories: ['forecast'],
+ example: '/uestc/jwc/student',
+ parameters: { type: '默认为 `important`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['jwc.uestc.edu.cn/'],
+ target: '/jwc',
+ },
+ name: '教务处',
+ maintainers: ['achjqz', 'mobyw'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type') || 'important';
const pageUrl = map[type];
if (!pageUrl) {
@@ -43,10 +66,10 @@ export default async (ctx) => {
})
.get();
- ctx.set('data', {
+ return {
title: '教务处通知',
link: baseUrl,
description: '电子科技大学教务处通知',
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/uestc/news.ts b/lib/routes/uestc/news.ts
index 22f7afd68..47141bb4c 100644
--- a/lib/routes/uestc/news.ts
+++ b/lib/routes/uestc/news.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
@@ -12,7 +13,29 @@ const map = {
announcement: '/?n=UestcNews.Front.CategoryV2.Page&CatId=68',
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/:type?',
+ categories: ['forecast'],
+ example: '/uestc/news/culture',
+ parameters: { type: '默认为 `announcement`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['news.uestc.edu.cn/'],
+ target: '/news',
+ },
+ name: '新闻中心',
+ maintainers: ['achjqz', 'mobyw'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type') || 'announcement';
const pageUrl = map[type];
if (!pageUrl) {
@@ -42,10 +65,10 @@ export default async (ctx) => {
})
.get();
- ctx.set('data', {
+ return {
title: '新闻网通知',
link: baseUrl,
description: '电子科技大学新闻网信息公告',
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/uestc/scse.ts b/lib/routes/uestc/scse.ts
index 44a76f692..57d292510 100644
--- a/lib/routes/uestc/scse.ts
+++ b/lib/routes/uestc/scse.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import dayjs from 'dayjs';
@@ -19,7 +20,28 @@ const prefixes = {
1022: '【安全工作】',
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/scse',
+ categories: ['forecast'],
+ example: '/uestc/scse',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: true,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['scse.uestc.edu.cn/'],
+ },
+ name: '计算机科学与工程学院',
+ maintainers: ['talengu', 'mobyw'],
+ handler,
+};
+
+async function handler() {
const browser = await puppeteer({ stealth: true });
const page = await browser.newPage();
await page.setRequestInterception(true);
@@ -82,10 +104,10 @@ export default async (ctx) => {
})
.get();
- ctx.set('data', {
+ return {
title: '计算机学院通知',
link: baseIndexUrl,
description: '电子科技大学计算机科学与工程学院通知',
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/uestc/sice.ts b/lib/routes/uestc/sice.ts
index 8a47ca55a..bfd672718 100644
--- a/lib/routes/uestc/sice.ts
+++ b/lib/routes/uestc/sice.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import dayjs from 'dayjs';
@@ -6,7 +7,28 @@ import puppeteer from '@/utils/puppeteer';
const baseIndexUrl = 'https://www.sice.uestc.edu.cn/index.htm';
const host = 'https://www.sice.uestc.edu.cn/';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/sice',
+ categories: ['forecast'],
+ example: '/uestc/sice',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: true,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['sice.uestc.edu.cn/'],
+ },
+ name: '信息与通信工程学院',
+ maintainers: ['huyyi', 'mobyw'],
+ handler,
+};
+
+async function handler() {
const browser = await puppeteer({ stealth: true });
const page = await browser.newPage();
await page.setRequestInterception(true);
@@ -38,10 +60,10 @@ export default async (ctx) => {
})
.get();
- ctx.set('data', {
+ return {
title: '信通学院通知',
link: baseIndexUrl,
description: '电子科技大学信息与通信工程学院通知公告',
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/uestc/sise.ts b/lib/routes/uestc/sise.ts
index 282802b9b..b65b179c4 100644
--- a/lib/routes/uestc/sise.ts
+++ b/lib/routes/uestc/sise.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import dayjs from 'dayjs';
@@ -29,7 +30,29 @@ const mapTitle = {
9: "Int'I",
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/sise/:type?',
+ categories: ['forecast'],
+ example: '/uestc/sise/1',
+ parameters: { type: '默认为 `1`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: true,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['sise.uestc.edu.cn/'],
+ target: '/sise',
+ },
+ name: '信息与软件工程学院',
+ maintainers: ['Yadomin', 'mobyw'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type') || 1;
const divId = mapId[type];
if (!divId) {
@@ -72,10 +95,10 @@ export default async (ctx) => {
})
.get();
- ctx.set('data', {
+ return {
title: `信软学院通知-${mapTitle[type]}`,
link: baseUrl,
description: '电子科技大学信息与软件工程学院通知',
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/uibe/hr.ts b/lib/routes/uibe/hr.ts
index 7b120f446..eeb27a91d 100644
--- a/lib/routes/uibe/hr.ts
+++ b/lib/routes/uibe/hr.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/hr/:category?/:type?',
+ categories: ['forecast'],
+ example: '/uibe/hr',
+ parameters: { category: '分类,可在对应页 URL 中找到,默认为通知公告', type: '类型,可在对应页 URL 中找到,默认为空' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['hr.uibe.edu.cn/:category/:type', 'hr.uibe.edu.cn/:category', 'hr.uibe.edu.cn/'],
+ },
+ name: '人力资源处',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? 'tzgg';
const type = ctx.req.param('type') ?? '';
@@ -51,9 +73,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `${$('.picTit').text()} - 对外经济贸易大学人力资源处`,
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/ulapia/index.ts b/lib/routes/ulapia/index.ts
index 0f3220c7b..4dc7a9187 100644
--- a/lib/routes/ulapia/index.ts
+++ b/lib/routes/ulapia/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
@@ -22,7 +23,25 @@ const selectorMap = {
ipo_research: 'div.col-md-6',
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/reports/:category?',
+ categories: ['other'],
+ example: '/ulapia/reports/stock_research',
+ parameters: { category: '频道类型,默认为券商晨报(今日晨报)' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '频道',
+ maintainers: ['Fatpandac'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? 'brokerage_news';
const url = `${rootUrl}/reports/${category}`;
@@ -39,9 +58,9 @@ export default async (ctx) => {
}))
.get();
- ctx.set('data', {
+ return {
title: ` ulapia - ${titleMap[category]}`,
link: url,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/ulapia/research.ts b/lib/routes/ulapia/research.ts
index deb81d563..44d9efeff 100644
--- a/lib/routes/ulapia/research.ts
+++ b/lib/routes/ulapia/research.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -7,7 +8,28 @@ const rootUrl = 'http://www.ulapia.com';
const researchList = ['stock_research', 'industry_research', 'strategy_research', 'macro_research', 'ipo_research'];
-export default async (ctx) => {
+export const route: Route = {
+ path: '/research/latest',
+ categories: ['other'],
+ example: '/ulapia/research/latest',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.ulapia.com/'],
+ },
+ name: '最新研报',
+ maintainers: [],
+ handler,
+};
+
+async function handler() {
const items = await Promise.all(
researchList.map((item) => {
const url = `${rootUrl}/reports/${item}`;
@@ -31,9 +53,9 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: 'Ulapia - 最新研报',
link: rootUrl,
item: items.flat(),
- });
-};
+ };
+}
diff --git a/lib/routes/uniqlo/new.ts b/lib/routes/uniqlo/new.ts
index 13d2522f9..d67a9de44 100644
--- a/lib/routes/uniqlo/new.ts
+++ b/lib/routes/uniqlo/new.ts
@@ -1,6 +1,25 @@
+import { Route } from '@/types';
import got from '@/utils/got';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/new/:country/:category',
+ categories: ['game'],
+ example: '/uniqlo/new/sg/men',
+ parameters: { country: 'currently only supports sg, us, jp', category: 'supports `men` `women`, `kids`, `baby`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'New Arrivals',
+ maintainers: ['DIYgod'],
+ handler,
+};
+
+async function handler(ctx) {
const { country, category } = ctx.req.param();
const map = {
@@ -54,9 +73,9 @@ export default async (ctx) => {
}${item.images.sub.map((image) => ``).join('')}`,
}));
- ctx.set('data', {
+ return {
title: `Uniqlo ${category} new arrivals in ${country}`,
link: `https://www.uniqlo.com/${country}/${map[country].lang || 'en'}/feature/new/${category}`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/unraid/community-apps.ts b/lib/routes/unraid/community-apps.ts
index 6f6567d34..411a09e31 100644
--- a/lib/routes/unraid/community-apps.ts
+++ b/lib/routes/unraid/community-apps.ts
@@ -1,10 +1,32 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
const appFeedUrl = 'https://raw.githubusercontent.com/Squidly271/AppFeed/master/applicationFeed.json';
const defaultLimit = 20;
-export default async (ctx) => {
+export const route: Route = {
+ path: '/community-apps',
+ categories: ['university'],
+ example: '/unraid/community-apps',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['unraid.net/community/apps'],
+ },
+ name: 'Community Apps',
+ maintainers: ['KTachibanaM'],
+ handler,
+};
+
+async function handler(ctx) {
const { data } = await got(appFeedUrl);
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : defaultLimit;
@@ -19,7 +41,7 @@ export default async (ctx) => {
appList.sort((a, b) => b._pubDate - a._pubDate);
const returnedAppList = appList.slice(0, limit);
- ctx.set('data', {
+ return {
title: 'Unraid Community Apps',
link: 'https://unraid.net/community/apps',
image: 'https://craftassets.unraid.net/static/favicon/favicon.ico?v=1.0',
@@ -31,5 +53,5 @@ export default async (ctx) => {
category: app.CategoryList,
upvotes: app.stars,
})),
- });
-};
+ };
+}
diff --git a/lib/routes/unusualwhales/news.ts b/lib/routes/unusualwhales/news.ts
index b646af474..71e572ab2 100644
--- a/lib/routes/unusualwhales/news.ts
+++ b/lib/routes/unusualwhales/news.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
const apiBase = 'https://phx.unusualwhales.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news',
+ categories: ['other'],
+ example: '/unusualwhales/news',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['unusualwhales.com/news', 'unusualwhales.com/'],
+ },
+ name: 'News Flow',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler() {
const { data } = await got(`${apiBase}/api/fj_articles`);
const items = data.map((item) => ({
@@ -13,12 +35,12 @@ export default async (ctx) => {
pubDate: parseDate(item.publish_date),
}));
- ctx.set('data', {
+ return {
title: 'Flow - News',
description: 'Explore unusual options, options flow, dark pools, short activity, and stock activity on unusualwhales.com. Unusual whales has a full news service available!',
link: 'https://unusualwhales.com/news-feed',
image: 'https://unusualwhales.com/android-icon-192x192.png',
language: 'en-US',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/upc/jsj.ts b/lib/routes/upc/jsj.ts
index 2b9f250ac..02cf74969 100644
--- a/lib/routes/upc/jsj.ts
+++ b/lib/routes/upc/jsj.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
// 计算机科学与技术学院:http://computer.upc.edu.cn/
// - 学院新闻:http://computer.upc.edu.cn/6277/list.htm
@@ -25,7 +26,25 @@ const HEAD = {
notice: '通知公告',
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/jsj/:type',
+ categories: ['forecast'],
+ example: '/upc/jsj/news',
+ parameters: { type: '分类,见下表' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '计算机科学与技术学院',
+ maintainers: ['Veagau'],
+ handler,
+};
+
+async function handler(ctx) {
const baseUrl = 'https://computer.upc.edu.cn';
const type = ctx.req.param('type');
const link = `${baseUrl}/${MAP[type]}/list.htm`;
@@ -69,10 +88,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: HEAD[type] + `-计算机科学与技术学院`,
link,
description: HEAD[type] + `-计算机科学与技术学院`,
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/upc/main.ts b/lib/routes/upc/main.ts
index e917f1c34..32e9ce018 100644
--- a/lib/routes/upc/main.ts
+++ b/lib/routes/upc/main.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
// 学校官网:http://www.upc.edu.cn/
import got from '@/utils/got';
@@ -16,7 +17,25 @@ const HEAD = {
scholar: '学术动态',
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/main/:type',
+ categories: ['forecast'],
+ example: '/upc/main/notice',
+ parameters: { type: '分类,见下表' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '主页',
+ maintainers: ['Veagau'],
+ handler,
+};
+
+async function handler(ctx) {
const baseUrl = 'https://news.upc.edu.cn';
const type = ctx.req.param('type');
const link = `${baseUrl}/${MAP[type]}.htm`;
@@ -64,10 +83,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: HEAD[type] + `-中国石油大学(华东)`,
link,
description: HEAD[type] + `-中国石油大学(华东)`,
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/upc/yjs.ts b/lib/routes/upc/yjs.ts
index 722e542dc..079130a2a 100644
--- a/lib/routes/upc/yjs.ts
+++ b/lib/routes/upc/yjs.ts
@@ -1,15 +1,37 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/yjs',
+ categories: ['forecast'],
+ example: '/upc/yjs',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['zs.gs.upc.edu.cn/sszs/list.htm', 'zs.gs.upc.edu.cn/'],
+ },
+ name: '研究生院通知公告',
+ maintainers: ['shengmaosu'],
+ handler,
+};
+
+async function handler() {
const baseUrl = 'http://zs.gs.upc.edu.cn';
const link = `${baseUrl}/sszs/list.htm`;
const response = await got(link);
const $ = load(response.data);
const list = $('.list tr');
- ctx.set('data', {
+ return {
title: '中国石油大学研究生院',
link,
description: '中国石油大学研究生院通知公告',
@@ -24,5 +46,5 @@ export default async (ctx) => {
pubDate: parseDate(item.find('div[style]').text()),
};
}),
- });
-};
+ };
+}
diff --git a/lib/routes/uptimerobot/rss.ts b/lib/routes/uptimerobot/rss.ts
index 90c9b4f6f..8833bb3e6 100644
--- a/lib/routes/uptimerobot/rss.ts
+++ b/lib/routes/uptimerobot/rss.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -53,7 +54,32 @@ class Monitor {
const rootURL = 'https://rss.uptimerobot.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/rss/:id/:routeParams?',
+ categories: ['travel'],
+ example: '/uptimerobot/rss/u358785-e4323652448755805d668f1a66506f2f',
+ parameters: {
+ id: 'the last part of your RSS URL (e.g. `u358785-e4323652448755805d668f1a66506f2f` for `https://rss.uptimerobot.com/u358785-e4323652448755805d668f1a66506f2f`)',
+ routeParams: 'extra parameters, see the table below',
+ },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['rss.uptimerobot.com/:id'],
+ target: '/rss/:id',
+ },
+ name: 'RSS',
+ maintainers: ['Rongronggg9'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const routeParams = Object.fromEntries(new URLSearchParams(ctx.req.param('routeParams')));
const showID = fallback(undefined, queryToBoolean(routeParams.showID), true);
@@ -119,11 +145,11 @@ export default async (ctx) => {
};
});
- ctx.set('data', {
+ return {
title: 'Uptime Robot - RSS (enhanced)',
description: rss.description,
link: rssUrl,
item: items,
image: 'https://uptimerobot.com/favicon.ico',
- });
-};
+ };
+}
diff --git a/lib/routes/uraaka-joshi/uraaka-joshi-user.ts b/lib/routes/uraaka-joshi/uraaka-joshi-user.ts
index d984ee317..d8dedf696 100644
--- a/lib/routes/uraaka-joshi/uraaka-joshi-user.ts
+++ b/lib/routes/uraaka-joshi/uraaka-joshi-user.ts
@@ -1,10 +1,32 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import { config } from '@/config';
import puppeteer from '@/utils/puppeteer';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:id',
+ categories: ['other'],
+ example: '/uraaka-joshi/_rrwq',
+ parameters: { id: 'User ID' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: true,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['uraaka-joshi.com/:id'],
+ },
+ name: 'User',
+ maintainers: ['SettingDust', 'Halcao'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const link = `https://www.uraaka-joshi.com/user/${id}`;
@@ -98,10 +120,10 @@ export default async (ctx) => {
};
});
- ctx.set('data', {
+ return {
title: $('title').text(),
description: $('meta[name="description"]').attr('content'),
link,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/uraaka-joshi/uraaka-joshi.ts b/lib/routes/uraaka-joshi/uraaka-joshi.ts
index 50280bdb9..746130320 100644
--- a/lib/routes/uraaka-joshi/uraaka-joshi.ts
+++ b/lib/routes/uraaka-joshi/uraaka-joshi.ts
@@ -1,8 +1,20 @@
+import { Route } from '@/types';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import puppeteer from '@/utils/puppeteer';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/',
+ radar: {
+ source: ['uraaka-joshi.com/'],
+ target: '',
+ },
+ name: 'Unknown',
+ maintainers: ['SettingDust', 'Halcao'],
+ handler,
+};
+
+async function handler() {
const link = `https://www.uraaka-joshi.com/`;
const title = `裏垢女子まとめ`;
@@ -39,7 +51,7 @@ export default async (ctx) => {
const $ = load(html);
const list = $('.grid-cell');
- ctx.set('data', {
+ return {
title,
link,
item:
@@ -86,5 +98,5 @@ export default async (ctx) => {
};
})
.get(),
- });
-};
+ };
+}
diff --git a/lib/routes/urbandictionary/random.ts b/lib/routes/urbandictionary/random.ts
index 7ee5081fa..8fea2411e 100644
--- a/lib/routes/urbandictionary/random.ts
+++ b/lib/routes/urbandictionary/random.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -6,7 +7,28 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/random',
+ categories: ['other'],
+ example: '/urbandictionary/random',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['urbandictionary.com/random.php', 'urbandictionary.com/'],
+ },
+ name: 'Random words',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler() {
const baseUrl = 'https://www.urbandictionary.com';
const { data } = await got('https://api.urbandictionary.com/v0/random');
@@ -19,9 +41,9 @@ export default async (ctx) => {
author: item.author,
}));
- ctx.set('data', {
+ return {
title: 'Urban Dictionary: Random words',
link: `${baseUrl}/random.php`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/usenix/usenix.ts b/lib/routes/usenix/usenix.ts
index a7eceb69d..83ee82ccc 100644
--- a/lib/routes/usenix/usenix.ts
+++ b/lib/routes/usenix/usenix.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -6,7 +7,28 @@ import { parseDate } from '@/utils/parse-date';
const seasons = ['spring', 'summer', 'fall', 'winter'];
-export default async (ctx) => {
+export const route: Route = {
+ path: '/usenix-security-sympoium',
+ categories: ['finance'],
+ example: '/usenix/usenix-security-sympoium',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['usenix.org/conferences/all', 'usenix.org/conferences', 'usenix.org/'],
+ },
+ name: 'Security Symposia',
+ maintainers: ['ZeddYu'],
+ handler,
+};
+
+async function handler() {
const last = new Date().getFullYear() + 1;
const urlList = Array.from({ length: last - 2020 }, (_, v) => `${url}/conference/usenixsecurity${v + 20}`).flatMap((url) => seasons.map((season) => `${url}/${season}-accepted-papers`));
const responses = await got.all(
@@ -49,11 +71,11 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: 'USENIX',
link: url,
description: 'USENIX Security Symposium Accpeted Papers',
allowEmpty: true,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/usepanda/index.ts b/lib/routes/usepanda/index.ts
index d611f4723..90c9168be 100644
--- a/lib/routes/usepanda/index.ts
+++ b/lib/routes/usepanda/index.ts
@@ -1,7 +1,26 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/feeds/:id',
+ categories: ['other'],
+ example: '/usepanda/feeds/5718e53e7a84fb1901e059cc',
+ parameters: { id: 'Feed ID' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Feeds',
+ maintainers: ['lyrl'],
+ handler,
+};
+
+async function handler(ctx) {
const feedId = ctx.req.param('id');
const limit = ctx.req.query('limit') ?? 30; // 默认30条
@@ -18,9 +37,9 @@ export default async (ctx) => {
description: item.description,
}));
- ctx.set('data', {
+ return {
title: 'Panda Feeds',
link: 'https://usepanda.com/',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/ustb/tj/news.ts b/lib/routes/ustb/tj/news.ts
index 8a663cb30..034d8b209 100644
--- a/lib/routes/ustb/tj/news.ts
+++ b/lib/routes/ustb/tj/news.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
@@ -30,7 +31,25 @@ function getNews(data) {
}));
}
-export default async (ctx) => {
+export const route: Route = {
+ path: '/tj/news/:type?',
+ categories: ['forecast'],
+ example: '/ustb/tj/news/all',
+ parameters: { type: '默认为 `all`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '天津学院',
+ maintainers: ['henbf'],
+ handler,
+};
+
+async function handler(ctx) {
let type = ctx.req.param('type') || 'all';
if (!Object.keys(maps).includes(type)) {
type = 'all';
@@ -58,4 +77,4 @@ export default async (ctx) => {
}
ctx.set('data', responseData);
-};
+}
diff --git a/lib/routes/ustb/yjsy/news.ts b/lib/routes/ustb/yjsy/news.ts
index 03289fafc..1f4ada37a 100644
--- a/lib/routes/ustb/yjsy/news.ts
+++ b/lib/routes/ustb/yjsy/news.ts
@@ -1,10 +1,32 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import iconv from 'iconv-lite';
import { parseDate } from '@/utils/parse-date';
// import logger from '@/utils/logger';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/yjsy/news/:type',
+ categories: ['forecast'],
+ example: '/ustb/yjsy/news/all',
+ parameters: { type: '文章类别' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['gs.ustb.edu.cn/:type'],
+ },
+ name: '研究生院',
+ maintainers: ['DA1Y1'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type');
const struct = {
all: {
@@ -401,7 +423,7 @@ export default async (ctx) => {
// logger.info("list:" + list);
// 处理返回
- ctx.set('data', {
+ return {
title: struct[type].name,
link: struct[type].link,
description: '北京科技大学研究生院',
@@ -453,5 +475,5 @@ export default async (ctx) => {
};
})
.get(),
- });
-};
+ };
+}
diff --git a/lib/routes/ustb/yzxc/tzgg.ts b/lib/routes/ustb/yzxc/tzgg.ts
index 41734e73a..62f28374b 100644
--- a/lib/routes/ustb/yzxc/tzgg.ts
+++ b/lib/routes/ustb/yzxc/tzgg.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -6,7 +7,28 @@ import { parseDate } from '@/utils/parse-date';
const host = 'https://yzxc.ustb.edu.cn';
const url = `${host}/tzgg/index.htm`;
-export default async (ctx) => {
+export const route: Route = {
+ path: '/yzxc/tzgg',
+ categories: ['forecast'],
+ example: '/ustb/yzxc/tzgg',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['yzxc.ustb.edu.cn/'],
+ },
+ name: '研究生招生信息网',
+ maintainers: ['yanbot-team'],
+ handler,
+};
+
+async function handler() {
const response = await got(url);
const $ = load(response.data);
const list = $('.page_content .ul-inline .box');
@@ -41,10 +63,10 @@ export default async (ctx) => {
});
})
);
- ctx.set('data', {
+ return {
title: '北京科技大学研究生招生信息网 - 通知公告',
link: url,
description: '北京科技大学研究生招生信息网 - 通知公告',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/ustc/eeis.ts b/lib/routes/ustc/eeis.ts
index 984757b32..bda73798c 100644
--- a/lib/routes/ustc/eeis.ts
+++ b/lib/routes/ustc/eeis.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -11,7 +12,29 @@ const map = new Map([
const host = 'https://eeis.ustc.edu.cn';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/eeis/:type?',
+ categories: ['forecast'],
+ example: '/ustc/eeis/tzgg',
+ parameters: { type: '分类,见下表,默认为通知公告' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['eeis.ustc.edu.cn/'],
+ target: '/eeis',
+ },
+ name: '电子工程与信息科学系',
+ maintainers: ['jasongzy'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type') ?? 'tzgg';
const info = map.get(type);
if (!info) {
@@ -53,9 +76,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: info.title,
link: `${host}/${id}/list.htm`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/ustc/gs.ts b/lib/routes/ustc/gs.ts
index e2e0c920c..55fa4e7a4 100644
--- a/lib/routes/ustc/gs.ts
+++ b/lib/routes/ustc/gs.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -11,7 +12,29 @@ const map = new Map([
const host = 'https://gradschool.ustc.edu.cn';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/gs/:type?',
+ categories: ['forecast'],
+ example: '/ustc/gs/tzgg',
+ parameters: { type: '分类,见下表,默认为通知公告' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['gradschool.ustc.edu.cn/'],
+ target: '/gs',
+ },
+ name: '研究生院',
+ maintainers: ['jasongzy'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type') ?? 'tzgg';
const info = map.get(type);
if (!info) {
@@ -52,9 +75,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: info.title,
link: `${host}/column/${id}`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/ustc/index.ts b/lib/routes/ustc/index.ts
index 8caa00bac..42da86776 100644
--- a/lib/routes/ustc/index.ts
+++ b/lib/routes/ustc/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -26,7 +27,29 @@ const notice_type = {
// });
// }
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/:type?',
+ categories: ['forecast'],
+ example: '/ustc/news/gl',
+ parameters: { type: '分类,默认为管理类' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['ustc.edu.cn/'],
+ target: '/news',
+ },
+ name: '官网通知公告',
+ maintainers: ['hang333', 'jasongzy'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type') ?? 'gl';
// 发起 HTTP GET 请求
const response = await got({
@@ -70,10 +93,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: notice_type[type].title,
description: notice_type[type].title,
link: notice_type[type].url,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/ustc/job.ts b/lib/routes/ustc/job.ts
index f739d7553..9ad18346b 100644
--- a/lib/routes/ustc/job.ts
+++ b/lib/routes/ustc/job.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -9,7 +10,29 @@ const titles = {
joblist2: '招聘公告',
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/job/:category?',
+ categories: ['forecast'],
+ example: '/ustc/job',
+ parameters: { category: '分类,见下表,默认为招聘公告' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['job.ustc.edu.cn/'],
+ target: '/job',
+ },
+ name: '就业信息网',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? 'joblist2';
const rand = 0.012_345_678_901_234_56;
@@ -186,9 +209,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `${titles[category]} - 中国科学技术大学就业信息网`,
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/ustc/jwc.ts b/lib/routes/ustc/jwc.ts
index 310818c93..35b1d13bd 100644
--- a/lib/routes/ustc/jwc.ts
+++ b/lib/routes/ustc/jwc.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -6,7 +7,29 @@ import timezone from '@/utils/timezone';
const noticeUrl = 'https://www.teach.ustc.edu.cn/category/notice';
const noticeType = { teaching: '教学', info: '信息', exam: '考试', exchange: '交流' };
-export default async (ctx) => {
+export const route: Route = {
+ path: '/jwc/:type?',
+ categories: ['forecast'],
+ example: '/ustc/jwc/info',
+ parameters: { type: '分类,默认显示所有种类' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.teach.ustc.edu.cn/'],
+ target: '/jwc',
+ },
+ name: '教务处通知新闻',
+ maintainers: ['hang333'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type') ?? '';
// 发起 HTTP GET 请求
const response = await got({
@@ -48,10 +71,10 @@ export default async (ctx) => {
const desc = type === '' ? '中国科学技术大学教务处 - 通知新闻' : `中国科学技术大学教务处 - ${noticeType[type]}类通知`;
- ctx.set('data', {
+ return {
title: desc,
description: desc,
link: `${noticeUrl}${type === '' ? '' : '-' + type}`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/ustc/sist.ts b/lib/routes/ustc/sist.ts
index 80a06dea1..d572da425 100644
--- a/lib/routes/ustc/sist.ts
+++ b/lib/routes/ustc/sist.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -11,7 +12,29 @@ const map = new Map([
const host = 'https://sist.ustc.edu.cn';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/sist/:type?',
+ categories: ['forecast'],
+ example: '/ustc/sist/tzgg',
+ parameters: { type: '分类,见下表,默认为通知公告' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['sist.ustc.edu.cn/'],
+ target: '/sist',
+ },
+ name: '信息科学技术学院',
+ maintainers: ['jasongzy'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type') ?? 'tzgg';
const info = map.get(type);
if (!info) {
@@ -53,9 +76,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: info.title,
link: `${host}/${id}/list.htm`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/usts/jwch.ts b/lib/routes/usts/jwch.ts
index 3591c544d..ecaf1c723 100644
--- a/lib/routes/usts/jwch.ts
+++ b/lib/routes/usts/jwch.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -6,7 +7,25 @@ import timezone from '@/utils/timezone';
const rootURL = 'http://jwch.usts.edu.cn/index';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/jwch/:type?',
+ categories: ['forecast'],
+ example: '/usts/jwch',
+ parameters: { type: '类型,默认为教务动态' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '教务处',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type') ?? 'jwdt';
const url = `${rootURL}/${type}.htm`;
const response = await got(url);
@@ -46,9 +65,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `苏州科技大学 教务处 - ${title}`,
link: url,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/utgd/category.ts b/lib/routes/utgd/category.ts
index dd9e1eba5..813a2493f 100644
--- a/lib/routes/utgd/category.ts
+++ b/lib/routes/utgd/category.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -12,7 +13,29 @@ const md = MarkdownIt({
html: true,
});
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:category?',
+ categories: ['traditional-media'],
+ example: '/utgd/method',
+ parameters: { category: '分类,可在对应分类页的 URL 中找到,默认为方法' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['utgd.net/category/s/:category', 'utgd.net/'],
+ target: '/:category',
+ },
+ name: '分类',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? 'method';
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 20;
@@ -64,11 +87,11 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `UNTAG - ${data.category_name}`,
link: currentUrl,
item: items,
image: data.category_image,
description: data.category_description,
- });
-};
+ };
+}
diff --git a/lib/routes/utgd/timeline.ts b/lib/routes/utgd/timeline.ts
index 1e8bf6611..b077a8494 100644
--- a/lib/routes/utgd/timeline.ts
+++ b/lib/routes/utgd/timeline.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -12,7 +13,28 @@ const md = MarkdownIt({
html: true,
});
-export default async (ctx) => {
+export const route: Route = {
+ path: '/timeline',
+ categories: ['traditional-media'],
+ example: '/utgd/timeline',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['utgd.net/'],
+ },
+ name: '时间线',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 20;
const rootUrl = 'https://utgd.net';
@@ -52,9 +74,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: 'UNTAG',
link: rootUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/utgd/topic.ts b/lib/routes/utgd/topic.ts
index 776fa4dd2..6b43b59e9 100644
--- a/lib/routes/utgd/topic.ts
+++ b/lib/routes/utgd/topic.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -12,7 +13,29 @@ const md = MarkdownIt({
html: true,
});
-export default async (ctx) => {
+export const route: Route = {
+ path: '/topic/:topic?',
+ categories: ['traditional-media'],
+ example: '/utgd/topic/在线阅读专栏',
+ parameters: { topic: '专题,默认为在线阅读专栏' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['utgd.net/topic', 'utgd.net/'],
+ target: '/topic/:topic',
+ },
+ name: '专题',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const topic = ctx.req.param('topic') ?? '在线阅读专栏';
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 20;
@@ -67,10 +90,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `UNTAG - ${topicItem.title}`,
link: currentUrl,
item: items,
description: topicItem.summary,
- });
-};
+ };
+}
diff --git a/lib/routes/uw/gix/news.ts b/lib/routes/uw/gix/news.ts
index 063f6c56a..f7405e7fc 100644
--- a/lib/routes/uw/gix/news.ts
+++ b/lib/routes/uw/gix/news.ts
@@ -1,10 +1,32 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
const gixBaseURL = 'https://gixnetwork.org';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/gix/news/:category',
+ categories: ['forecast'],
+ example: '/uw/gix/news/blog',
+ parameters: { category: 'Blog Type' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['gixnetwork.org/news/:category'],
+ },
+ name: 'Global Innovation Exchange News',
+ maintainers: ['dykderrick'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category');
let newsURL = gixBaseURL + '/news';
@@ -60,9 +82,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: feedTitle,
link: newsURL,
item: itemContent,
- });
-};
+ };
+}
diff --git a/lib/routes/v1tx/index.ts b/lib/routes/v1tx/index.ts
index a4c3e38fa..feba8b222 100644
--- a/lib/routes/v1tx/index.ts
+++ b/lib/routes/v1tx/index.ts
@@ -1,9 +1,21 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/',
+ radar: {
+ source: ['v1tx.com/'],
+ target: '',
+ },
+ name: 'Unknown',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler() {
const baseUrl = 'https://www.v1tx.com';
const { data: response } = await got(baseUrl);
@@ -46,11 +58,11 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('head title').text(),
description: $('head meta[name="description"]').attr('content'),
link: baseUrl,
image: `${baseUrl}/wp-content/uploads/2018/10/cropped-Favicon.webp`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/v2ex/post.ts b/lib/routes/v2ex/post.ts
index 04860a977..4a86cd40d 100644
--- a/lib/routes/v2ex/post.ts
+++ b/lib/routes/v2ex/post.ts
@@ -1,7 +1,29 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/post/:postid',
+ categories: ['blog'],
+ example: '/v2ex/post/584403',
+ parameters: { postid: '帖子ID,在 URL 可以找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['v2ex.com/t/:postid'],
+ },
+ name: '帖子',
+ maintainers: ['kt286'],
+ handler,
+};
+
+async function handler(ctx) {
const postid = ctx.req.param('postid');
const pageUrl = `https://www.v2ex.com/t/${postid}`;
@@ -19,7 +41,7 @@ export default async (ctx) => {
const topic = topicResponse[0];
- ctx.set('data', {
+ return {
title: `V2EX-${topic.title}`,
link: pageUrl,
description: topic.content,
@@ -31,5 +53,5 @@ export default async (ctx) => {
pubDate: parseDate(item.created, 'X'),
})),
allowEmpty: true,
- });
-};
+ };
+}
diff --git a/lib/routes/v2ex/tab.ts b/lib/routes/v2ex/tab.ts
index 75848d7e8..6a8276d9f 100644
--- a/lib/routes/v2ex/tab.ts
+++ b/lib/routes/v2ex/tab.ts
@@ -1,8 +1,27 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/tab/:tabid',
+ categories: ['blog'],
+ example: '/v2ex/tab/hot',
+ parameters: { tabid: 'tab标签ID,在 URL 可以找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '标签',
+ maintainers: ['liyefox'],
+ handler,
+};
+
+async function handler(ctx) {
const tabid = ctx.req.param('tabid');
const host = 'https://v2ex.com';
const pageUrl = `${host}/?tab=${tabid}`;
@@ -48,10 +67,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `V2EX-${tabid}`,
link: pageUrl,
description: `V2EX-tab-${tabid}`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/v2ex/topics.ts b/lib/routes/v2ex/topics.ts
index 31db8d06e..7454eb58a 100644
--- a/lib/routes/v2ex/topics.ts
+++ b/lib/routes/v2ex/topics.ts
@@ -1,7 +1,26 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/topics/:type',
+ categories: ['blog'],
+ example: '/v2ex/topics/latest',
+ parameters: { type: 'hot 或 latest' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '最热 / 最新主题',
+ maintainers: ['WhiteWorld'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type');
const { data } = await got(`https://www.v2ex.com/api/topics/${type}.json`);
@@ -13,7 +32,7 @@ export default async (ctx) => {
title = '最新主题';
}
- ctx.set('data', {
+ return {
title: `V2EX-${title}`,
link: 'https://www.v2ex.com/',
description: `V2EX-${title}`,
@@ -26,5 +45,5 @@ export default async (ctx) => {
author: item.member.username,
comments: item.replies,
})),
- });
-};
+ };
+}
diff --git a/lib/routes/v2rayshare/index.ts b/lib/routes/v2rayshare/index.ts
index d993b5be3..f52bcd063 100644
--- a/lib/routes/v2rayshare/index.ts
+++ b/lib/routes/v2rayshare/index.ts
@@ -1,7 +1,19 @@
+import { Route } from '@/types';
import got from '@/utils/got'; // 自订的 got
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/',
+ radar: {
+ source: ['v2rayshare.com/'],
+ target: '',
+ },
+ name: 'Unknown',
+ maintainers: ['77taibai'],
+ handler,
+};
+
+async function handler() {
const { data: response } = await got('https://v2rayshare.com/wp-json/wp/v2/posts/?per_page=10');
const items = response.map((item) => ({
@@ -13,10 +25,10 @@ export default async (ctx) => {
description: item.content.rendered,
}));
- ctx.set('data', {
+ return {
title: 'V2rayShare',
link: 'https://v2rayshare.com/',
description: '免费节点分享网站',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/vcb-s/category.ts b/lib/routes/vcb-s/category.ts
index ba64bae5d..4dcd817cd 100644
--- a/lib/routes/vcb-s/category.ts
+++ b/lib/routes/vcb-s/category.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -11,7 +12,28 @@ const rootUrl = 'https://vcb-s.com';
const cateAPIUrl = `${rootUrl}/wp-json/wp/v2/categories`;
const postsAPIUrl = `${rootUrl}/wp-json/wp/v2/posts`;
-export default async (ctx) => {
+export const route: Route = {
+ path: '/category/:cate',
+ categories: ['program-update'],
+ example: '/vcb-s/category/works',
+ parameters: { cate: '分类' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['vcb-s.com/archives/category/:cate'],
+ },
+ name: '分类文章',
+ maintainers: ['cxfksword'],
+ handler,
+};
+
+async function handler(ctx) {
const cate = ctx.req.param('cate');
const limit = ctx.req.query('limit') ?? 7;
@@ -47,9 +69,9 @@ export default async (ctx) => {
};
});
- ctx.set('data', {
+ return {
title: `${category.name} | VCB-Studio`,
link: `${rootUrl}/archives/category/${category.slug}`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/vcb-s/index.ts b/lib/routes/vcb-s/index.ts
index 94742a69e..71db5a067 100644
--- a/lib/routes/vcb-s/index.ts
+++ b/lib/routes/vcb-s/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -9,7 +10,18 @@ import * as path from 'node:path';
const rootUrl = 'https://vcb-s.com';
const postsAPIUrl = `${rootUrl}/wp-json/wp/v2/posts`;
-export default async (ctx) => {
+export const route: Route = {
+ path: '/',
+ radar: {
+ source: ['vcb-s.com/'],
+ target: '',
+ },
+ name: 'Unknown',
+ maintainers: ['cxfksword'],
+ handler,
+};
+
+async function handler(ctx) {
const limit = ctx.req.query('limit') ?? 7;
const url = `${postsAPIUrl}?per_page=${limit}&_embed`;
@@ -34,9 +46,9 @@ export default async (ctx) => {
};
});
- ctx.set('data', {
+ return {
title: 'VCB-Studio - 大家一起实现的故事!',
link: rootUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/verfghbw/press.ts b/lib/routes/verfghbw/press.ts
index 4e8e64271..a366c794f 100644
--- a/lib/routes/verfghbw/press.ts
+++ b/lib/routes/verfghbw/press.ts
@@ -1,8 +1,31 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/press/:keyword?',
+ categories: ['study'],
+ example: '/verfghbw/press',
+ parameters: { keyword: 'Keyword' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['verfgh.baden-wuerttemberg.de/de/presse-und-service/pressemitteilungen/'],
+ target: '/press',
+ },
+ name: 'Press releases',
+ maintainers: ['quinn-dev'],
+ handler,
+};
+
+async function handler(ctx) {
const keyword = ctx.req.param('keyword');
const rootUrl = 'https://verfgh.baden-wuerttemberg.de';
@@ -49,10 +72,10 @@ export default async (ctx) => {
})
.get();
- ctx.set('data', {
+ return {
title: 'Verfassungsgerichtshof Baden-Württemberg - Pressemitteilungen',
link: request.url,
description: 'Pressemitteilungen des Verfassungsgerichtshof für das Land Baden-Württemberg',
item: list,
- });
-};
+ };
+}
diff --git a/lib/routes/vimeo/category.ts b/lib/routes/vimeo/category.ts
index 1c2f37e9e..baa4aa6a4 100644
--- a/lib/routes/vimeo/category.ts
+++ b/lib/routes/vimeo/category.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -8,7 +9,28 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/category/:category/:staffpicks?',
+ categories: ['new-media'],
+ example: '/vimeo/category/documentary/staffpicks',
+ parameters: {
+ category: 'Category name can get from url like `documentary` in [https://vimeo.com/categories/documentary/videos](https://vimeo.com/categories/documentary/videos) ',
+ staffpicks: 'type `staffpicks` to sort with staffpicks',
+ },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Category',
+ maintainers: ['MisteryMonster'],
+ handler,
+};
+
+async function handler(ctx) {
const { category, staffpicks } = ctx.req.param();
const categoryparams = category;
@@ -50,7 +72,7 @@ export default async (ctx) => {
return description('meta[name="description"]').attr('content');
});
- ctx.set('data', {
+ return {
title: `${feedtitle} | Vimeo category`,
link: feedlink + (staffpicks ? feedlinkstaffpicks : ''),
description: feedDescription,
@@ -64,5 +86,5 @@ export default async (ctx) => {
link: item.link,
author: item.user.name,
})),
- });
-};
+ };
+}
diff --git a/lib/routes/vimeo/channel.ts b/lib/routes/vimeo/channel.ts
index 089763d8e..8c0d4393f 100644
--- a/lib/routes/vimeo/channel.ts
+++ b/lib/routes/vimeo/channel.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -8,7 +9,28 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/channel/:channel',
+ categories: ['new-media'],
+ example: '/vimeo/channel/bestoftheyear',
+ parameters: { channel: 'channel name can get from url like `bestoftheyear` in [https://vimeo.com/channels/bestoftheyear/videos](https://vimeo.com/channels/bestoftheyear/videos) .' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['vimeo.com/channels/:channel', 'vimeo.com/channels/:channel/videos', 'vimeo.com/channels/:channel/videos/:sort/:format'],
+ },
+ name: 'Channel',
+ maintainers: ['MisteryMonster'],
+ handler,
+};
+
+async function handler(ctx) {
const channel = ctx.req.param('channel');
const url = `https://vimeo.com/channels/${channel}/videos`;
const page1 = await got({
@@ -52,7 +74,7 @@ export default async (ctx) => {
});
})
);
- ctx.set('data', {
+ return {
title: `${channel} | Vimeo channel`,
link: url,
item: list
@@ -72,5 +94,5 @@ export default async (ctx) => {
};
})
.get(),
- });
-};
+ };
+}
diff --git a/lib/routes/vimeo/usr-videos.ts b/lib/routes/vimeo/usr-videos.ts
index 4b660b9a9..3a40ceb9a 100644
--- a/lib/routes/vimeo/usr-videos.ts
+++ b/lib/routes/vimeo/usr-videos.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -6,7 +7,28 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/user/:username/:cat?',
+ categories: ['new-media'],
+ example: '/vimeo/user/filmsupply/picks',
+ parameters: {
+ username: 'In this example [https://vimeo.com/filmsupply](https://vimeo.com/filmsupply) is `filmsupply`',
+ cat: "deafult for all latest videos, others categories in this example such as `Docmentary`, `Narrative`, `Drama`. Set `picks` for promote orders, just orderd like web page. When `picks` added, published date won't show up",
+ },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'User Profile',
+ maintainers: ['MisteryMonster'],
+ handler,
+};
+
+async function handler(ctx) {
const tokenresponse = await got({
method: 'get',
url: 'https://vimeo.com/_rv/viewer',
@@ -56,7 +78,7 @@ export default async (ctx) => {
});
const vimeojs = picked ? contentresponse.data.data[0].videos.data : contentresponse.data.data;
- ctx.set('data', {
+ return {
title: `${profilesjs.name} ${catword ? cat.replace('|', '/') : ''} ${picked ? 'picks' : ''} | Vimeo `,
link: profilesjs.link,
description: profilesjs.bio,
@@ -75,5 +97,5 @@ export default async (ctx) => {
author: profilesjs.name,
};
}),
- });
-};
+ };
+}
diff --git a/lib/routes/vocus/publication.ts b/lib/routes/vocus/publication.ts
index a3fb89221..f459e30f1 100644
--- a/lib/routes/vocus/publication.ts
+++ b/lib/routes/vocus/publication.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { processList, ProcessFeed, baseUrl, apiUrl } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/publication/:id',
+ categories: ['new-media'],
+ example: '/vocus/publication/bass',
+ parameters: { id: '出版專題 id,可在出版專題主页的 URL 找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['vocus.cc/:id/home', 'vocus.cc/:id/introduce'],
+ },
+ name: '出版專題',
+ maintainers: ['Maecenas'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const link = `${baseUrl}/${id}/home`;
@@ -34,10 +56,10 @@ export default async (ctx) => {
const items = await ProcessFeed(list, cache.tryGet);
- ctx.set('data', {
+ return {
title: `${publicationData.title} - 文章列表|方格子 vocus`,
link,
description: publicationData.abstract,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/vocus/user.ts b/lib/routes/vocus/user.ts
index 0647e53db..bbc0d7196 100644
--- a/lib/routes/vocus/user.ts
+++ b/lib/routes/vocus/user.ts
@@ -1,8 +1,27 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { processList, ProcessFeed, baseUrl, apiUrl } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/user/:id',
+ categories: ['new-media'],
+ example: '/vocus/user/tsetyan',
+ parameters: { id: '用户 id,可在用户主页的 URL 找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '用户个人文章',
+ maintainers: ['LogicJake'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const link = `${baseUrl}/user/@${id}`;
const userData = await cache.tryGet(`vocus:user:${id}`, async () => {
@@ -34,11 +53,11 @@ export default async (ctx) => {
const items = await ProcessFeed(list, cache.tryGet);
- ctx.set('data', {
+ return {
title: `${userData.fullname}|方格子 vocus`,
link,
description: userData.intro,
image: userData.avatarUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/vom/featured.ts b/lib/routes/vom/featured.ts
index 733f28440..3f977d4fc 100644
--- a/lib/routes/vom/featured.ts
+++ b/lib/routes/vom/featured.ts
@@ -1,10 +1,33 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
const baseUrl = 'http://www.vom.mn';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/featured/:lang?',
+ categories: ['bbs'],
+ example: '/vom/featured',
+ parameters: { lang: 'Language, see the table below, `mn` by default' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['vom.mn/:lang', 'vom.mn/'],
+ target: '/featured/:lang',
+ },
+ name: 'News',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const { lang = 'mn' } = ctx.req.param();
const { data: response } = await got(`${baseUrl}/${lang}`);
@@ -43,9 +66,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('meta[name=description]').attr('content'),
image: 'http://www.vom.mn/dist/images/vom-logo.png',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/wallhaven/index.ts b/lib/routes/wallhaven/index.ts
index db080d2cf..7d70ab800 100644
--- a/lib/routes/wallhaven/index.ts
+++ b/lib/routes/wallhaven/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -5,7 +6,28 @@ import { parseDate } from '@/utils/parse-date';
const rootUrl = 'https://wallhaven.cc';
-export default async (ctx) => {
+export const route: Route = {
+ path: ['/search/:filter?/:needDetails?', '/:filter?/:needDetails?'],
+ categories: ['anime'],
+ example: '/wallhaven/search/categories=110&purity=110&sorting=date_added&order=desc',
+ parameters: { filter: 'Filter, empty by default', needDetails: 'Need Details, `true`/`yes` as yes, no by default' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['wallhaven.cc/'],
+ },
+ name: 'Search',
+ maintainers: ['nczitzk', 'Fatpandac'],
+ handler,
+};
+
+async function handler(ctx) {
const filter = ctx.req.param('filter') ?? 'latest';
const needDetails = /t|y/i.test(ctx.req.param('needDetails') ?? 'false');
const url = `${rootUrl}/${filter.indexOf('=') > 0 ? `search?${filter.replaceAll(/page=\d+/g, 'page=1')}` : filter}`;
@@ -48,9 +70,9 @@ export default async (ctx) => {
);
}
- ctx.set('data', {
+ return {
title: $('title').text(),
link: url,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/wallpaperhub/index.ts b/lib/routes/wallpaperhub/index.ts
index 4ed9d7bc3..0780cef77 100644
--- a/lib/routes/wallpaperhub/index.ts
+++ b/lib/routes/wallpaperhub/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -6,7 +7,18 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/',
+ radar: {
+ source: ['wallpaperhub.app/wallpaperhub', 'wallpaperhub.app/'],
+ target: '',
+ },
+ name: 'Unknown',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler() {
const link = 'https://wallpaperhub.app/api/v1/wallpapers/?limit=20&page=&query=&width=&height=&tags=';
const response = await got({
method: 'get',
@@ -23,9 +35,9 @@ export default async (ctx) => {
link: `https://wallpaperhub.app/wallpapers/${item.entity.id}`,
}));
- ctx.set('data', {
+ return {
title: 'WallpaperHub',
link: 'https://wallpaperhub.app/wallpapers',
item: list,
- });
-};
+ };
+}
diff --git a/lib/routes/wallstreetcn/hot.ts b/lib/routes/wallstreetcn/hot.ts
index 1005a5cbb..e4f8a7222 100644
--- a/lib/routes/wallstreetcn/hot.ts
+++ b/lib/routes/wallstreetcn/hot.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/hot/:period?',
+ categories: ['bbs'],
+ example: '/wallstreetcn/hot',
+ parameters: { period: '时期,可选 `day` 即 当日 或 `week` 即 当周,默认为当日' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['wallstreetcn.com/'],
+ },
+ name: '最热文章',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const period = ctx.req.param('period') ?? 'day';
const rootUrl = 'https://wallstreetcn.com';
@@ -47,11 +69,11 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: '华尔街见闻 - 最热文章',
link: rootUrl,
item: items,
itunes_author: '华尔街见闻',
image: 'https://static-alpha-wscn.awtmt.com/wscn-static/qrcode.jpg',
- });
-};
+ };
+}
diff --git a/lib/routes/wallstreetcn/live.ts b/lib/routes/wallstreetcn/live.ts
index 26594523f..9005f082d 100644
--- a/lib/routes/wallstreetcn/live.ts
+++ b/lib/routes/wallstreetcn/live.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -16,7 +17,29 @@ const titles = {
financing: '理财',
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/live/:category?/:score?',
+ categories: ['bbs'],
+ example: '/wallstreetcn/live',
+ parameters: { category: '快讯分类,默认`global`,见下表', score: '快讯重要度,默认`1`全部快讯,可设置为`2`只看重要的' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['wallstreetcn.com/live/:category', 'wallstreetcn.com/'],
+ target: '/live/:category?',
+ },
+ name: '实时快讯',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? 'global';
const score = ctx.req.param('score') ?? 1;
@@ -44,9 +67,9 @@ export default async (ctx) => {
}),
}));
- ctx.set('data', {
+ return {
title: `华尔街见闻 - 实时快讯 - ${titles[category]}`,
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/wallstreetcn/news.ts b/lib/routes/wallstreetcn/news.ts
index fa0d3bce2..62d425379 100644
--- a/lib/routes/wallstreetcn/news.ts
+++ b/lib/routes/wallstreetcn/news.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -16,7 +17,17 @@ const titles = {
medicine: '医药',
};
-export default async (ctx) => {
+export const route: Route = {
+ path: ['/news/:category?', '/:category?'],
+ radar: {
+ source: ['wallstreetcn.com/news/:category', 'wallstreetcn.com/'],
+ },
+ name: 'Unknown',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? 'global';
const rootUrl = 'https://wallstreetcn.com';
@@ -67,11 +78,11 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `华尔街见闻 - 资讯 - ${titles[category]}`,
link: currentUrl,
item: items,
itunes_author: '华尔街见闻',
image: 'https://static-alpha-wscn.awtmt.com/wscn-static/qrcode.jpg',
- });
-};
+ };
+}
diff --git a/lib/routes/wanqu/news.ts b/lib/routes/wanqu/news.ts
index f6529f48e..f8cb6a385 100644
--- a/lib/routes/wanqu/news.ts
+++ b/lib/routes/wanqu/news.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news',
+ categories: ['traditional-media'],
+ example: '/wanqu/news',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['wanqu.co/'],
+ },
+ name: '最新推荐',
+ maintainers: ['Fatpandac'],
+ handler,
+};
+
+async function handler() {
const rootUrl = 'https://www.wanqu.co';
const currentUrl = rootUrl;
@@ -19,9 +41,9 @@ export default async (ctx) => {
pubDate: parseDate($(item).find('i.text-helper-color.mr-4').text().trim()),
}));
- ctx.set('data', {
+ return {
title: '湾区日报 - 最新推荐',
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/warthunder/news.ts b/lib/routes/warthunder/news.ts
index b798e35bf..81359cc12 100644
--- a/lib/routes/warthunder/news.ts
+++ b/lib/routes/warthunder/news.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -9,7 +10,28 @@ import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
const renderDescription = (desc) => art(path.join(__dirname, 'templates/description.art'), desc);
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news',
+ categories: ['reading'],
+ example: '/warthunder/news',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['warthunder.com/en/news', 'warthunder.com/'],
+ },
+ name: 'News',
+ maintainers: ['axojhf'],
+ handler,
+};
+
+async function handler() {
const rootUrl = 'https://warthunder.com/en/news/';
const response = await got(rootUrl);
@@ -44,9 +66,9 @@ export default async (ctx) => {
})
.get();
- ctx.set('data', {
+ return {
title: 'War Thunder News',
link: 'https://warthunder.com/en/news/',
item: pageFace,
- });
-};
+ };
+}
diff --git a/lib/routes/wdc/download.ts b/lib/routes/wdc/download.ts
index f78e62c7f..e55563263 100644
--- a/lib/routes/wdc/download.ts
+++ b/lib/routes/wdc/download.ts
@@ -1,8 +1,27 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/download/:id?',
+ categories: ['university'],
+ example: '/wdc/download/279',
+ parameters: { id: 'Software id, can be found in URL, 279 as Western Digital Dashboard by default' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Download',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id') ?? '279';
const rootUrl = 'https://support.wdc.com';
@@ -29,9 +48,9 @@ export default async (ctx) => {
},
];
- ctx.set('data', {
+ return {
title: `${$('#WD_lblSelectedName').text()} | WD Support`,
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/web3caff/index.ts b/lib/routes/web3caff/index.ts
index b1c735d97..bbac60380 100644
--- a/lib/routes/web3caff/index.ts
+++ b/lib/routes/web3caff/index.ts
@@ -1,10 +1,18 @@
+import { Route } from '@/types';
import { getSubPath } from '@/utils/common-utils';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '*',
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const params = getSubPath(ctx) === '/' ? '' : getSubPath(ctx);
const rootUrl = 'https://web3caff.com';
@@ -57,9 +65,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('title').text(),
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/wechat/announce.ts b/lib/routes/wechat/announce.ts
index 289a8b596..49f2cc6a3 100644
--- a/lib/routes/wechat/announce.ts
+++ b/lib/routes/wechat/announce.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/announce',
+ categories: ['design'],
+ example: '/wechat/announce',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['mp.weixin.qq.com/cgi-bin/announce'],
+ },
+ name: '公众平台系统公告栏目',
+ maintainers: ['xyqfer'],
+ handler,
+};
+
+async function handler() {
const { data: htmlString } = await got({
method: 'get',
url: 'https://mp.weixin.qq.com/cgi-bin/announce?action=getannouncementlist&lang=zh_CN',
@@ -25,9 +47,9 @@ export default async (ctx) => {
});
});
- ctx.set('data', {
+ return {
title: '微信公众平台-系统公告栏目',
link: 'https://mp.weixin.qq.com/cgi-bin/announce?action=getannouncementlist&lang=zh_CN',
item: announceList,
- });
-};
+ };
+}
diff --git a/lib/routes/wechat/ce.ts b/lib/routes/wechat/ce.ts
index 9757d4049..f22c07209 100644
--- a/lib/routes/wechat/ce.ts
+++ b/lib/routes/wechat/ce.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import parser from '@/utils/rss-parser';
import got from '@/utils/got';
@@ -9,7 +10,28 @@ import { parseDate } from '@/utils/parse-date';
// mark the UA as a desktop UA with "(X11; Linux x86_64)"
const UA = 'Mozilla/5.0 (X11; Linux x86_64) RSS Reader';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/ce/:id',
+ categories: ['traditional-media'],
+ example: '/wechat/ce/595a5b14d7164e53908f1606',
+ parameters: { id: '公众号 id,在 [CareerEngine](https://search.careerengine.us/) 搜索公众号,通过 URL 中找到对应的公众号 id' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['cimidata.com/a/:id'],
+ },
+ name: '公众号(CareerEngine 来源)',
+ maintainers: ['HenryQW'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const feed = await parser.parseString(
@@ -61,10 +83,10 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: `微信公众号 - ${feed.title}`,
link: `https://posts.careerengine.us/author/${id}/posts`,
description: feed.description,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/wechat/data258.ts b/lib/routes/wechat/data258.ts
index f7ea11069..2c5664497 100644
--- a/lib/routes/wechat/data258.ts
+++ b/lib/routes/wechat/data258.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -18,7 +19,17 @@ const parsePage = ($item, hyperlinkSelector, timeSelector) => {
};
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/data258/:id?',
+ radar: {
+ source: ['mp.data258.com/', 'mp.data258.com/article/category/:id'],
+ },
+ name: 'Unknown',
+ maintainers: ['Rongronggg9'],
+ handler,
+};
+
+async function handler(ctx) {
// !!! here we must use a lock to prevent other requests to break the anti-anti-crawler workarounds !!!
if ((await cache.get('data258:lock', false)) === '1') {
throw new Error('Another request is in progress, please try again later.');
@@ -121,13 +132,13 @@ export default async (ctx) => {
await Promise.all(items.map((item) => finishArticleItem(item, !!categoryPage)));
- ctx.set('data', {
+ return {
title,
link: pageUrl,
description,
item: items,
- });
-};
+ };
+}
// TODO: login? the valid time for cookies seems to be short, and abusing account will probably get banned...
// TODO: fetch full article for the official RSS feed? unless someone who is VIP contributes their RSS feed for test...
diff --git a/lib/routes/wechat/ershcimi.ts b/lib/routes/wechat/ershcimi.ts
index 253e09eb2..852859ccf 100644
--- a/lib/routes/wechat/ershcimi.ts
+++ b/lib/routes/wechat/ershcimi.ts
@@ -1,10 +1,29 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { finishArticleItem } from '@/utils/wechat-mp';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/ershicimi/:id',
+ categories: ['traditional-media'],
+ example: '/wechat/ershicimi/813oxJOl',
+ parameters: { id: '公众号id,打开公众号页,在 URL 中找到 id' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '公众号(二十次幂来源)',
+ maintainers: ['sanmmm'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const rootUrl = 'https://www.cimidata.com';
@@ -26,10 +45,10 @@ export default async (ctx) => {
await Promise.all(items.map((item) => finishArticleItem(item)));
- ctx.set('data', {
+ return {
title: `微信公众号 - ${$('span.name').text()}`,
link: url,
description: $('div.Profile-sideColumnItemValue').text(),
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/wechat/feeddd.ts b/lib/routes/wechat/feeddd.ts
index efc25cd7e..d71c01a4f 100644
--- a/lib/routes/wechat/feeddd.ts
+++ b/lib/routes/wechat/feeddd.ts
@@ -29,13 +29,13 @@ export default async (ctx) => {
items = await Promise.all(items.map((item) => finishArticleItem(item)));
- ctx.set('data', {
+ return {
title: response.data.title,
link: response.data.feed_url,
description: response.data.title,
item: items,
allowEmpty: true,
- });
+ };
ctx.set('json', {
title: response.data.title,
diff --git a/lib/routes/wechat/feeds.ts b/lib/routes/wechat/feeds.ts
index 695358d9f..e709d9d7a 100644
--- a/lib/routes/wechat/feeds.ts
+++ b/lib/routes/wechat/feeds.ts
@@ -14,10 +14,10 @@ export default async (ctx) => {
}));
await Promise.all(items.map((item) => finishArticleItem(item)));
- ctx.set('data', {
+ return {
title: feed.title,
link,
description: feed.description,
item: items,
- });
+ };
};
diff --git a/lib/routes/wechat/mp.ts b/lib/routes/wechat/mp.ts
index 51e15ff6c..cdb1a1363 100755
--- a/lib/routes/wechat/mp.ts
+++ b/lib/routes/wechat/mp.ts
@@ -1,9 +1,28 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import dayjs from 'dayjs';
import { finishArticleItem } from '@/utils/wechat-mp';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/mp/homepage/:biz/:hid/:cid?',
+ categories: ['traditional-media'],
+ example: '/wechat/mp/homepage/MzA3MDM3NjE5NQ==/16',
+ parameters: { biz: '公众号id', hid: '分页id', cid: '页内栏目' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '公众号栏目 (非推送 & 历史消息)',
+ maintainers: ['MisteryMonster'],
+ handler,
+};
+
+async function handler(ctx) {
const { biz, hid, cid } = ctx.req.param();
let cidurl = '';
if (cid) {
@@ -35,7 +54,7 @@ export default async (ctx) => {
return finishArticleItem(single);
})
);
- ctx.set('data', {
+ return {
// 源标题,差一个cid相关的标题!
title: mptitle,
link: `https://mp.weixin.qq.com/mp/homepage?__biz=${biz}${hidurl}${cidurl}`,
@@ -55,5 +74,5 @@ export default async (ctx) => {
author: articledata[index].author,
pubDate: dayjs.unix(item.sendtime).format(),
})),
- });
-};
+ };
+}
diff --git a/lib/routes/wechat/msgalbum.ts b/lib/routes/wechat/msgalbum.ts
index ea758ef01..8d0c9dd33 100644
--- a/lib/routes/wechat/msgalbum.ts
+++ b/lib/routes/wechat/msgalbum.ts
@@ -1,9 +1,28 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import dayjs from 'dayjs';
import { finishArticleItem } from '@/utils/wechat-mp';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/mp/msgalbum/:biz/:aid',
+ categories: ['traditional-media'],
+ example: '/wechat/mp/msgalbum/MzA3MDM3NjE5NQ==/1375870284640911361',
+ parameters: { biz: '公众号id', aid: 'Tag id' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '公众号文章话题 Tag',
+ maintainers: ['MisteryMonster'],
+ handler,
+};
+
+async function handler(ctx) {
const { biz, aid } = ctx.req.param();
const aidurl = `&album_id=${aid}`;
@@ -26,7 +45,7 @@ export default async (ctx) => {
return finishArticleItem(single);
})
);
- ctx.set('data', {
+ return {
title: mptitle,
link: `https://mp.weixin.qq.com/mp/appmsgalbum?__biz=${biz}&action=getalbum${aidurl}`,
item: list.map((item, index) => ({
@@ -37,5 +56,5 @@ export default async (ctx) => {
author: articledata[index].author,
pubDate: dayjs.unix($(item).find('.js_article_create_time').text()).format(),
})),
- });
-};
+ };
+}
diff --git a/lib/routes/wechat/sogou.ts b/lib/routes/wechat/sogou.ts
index dfd173eb3..3995dc73c 100644
--- a/lib/routes/wechat/sogou.ts
+++ b/lib/routes/wechat/sogou.ts
@@ -1,8 +1,27 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
const host = 'https://weixin.sogou.com';
import { finishArticleItem } from '@/utils/wechat-mp';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/sogou/:id',
+ categories: ['traditional-media'],
+ example: '/wechat/sogou/qimao0908',
+ parameters: { id: '公众号 id, 打开 weixin.sogou.com 并搜索相应公众号, 在 URL 中找到 id' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '公众号(搜狗来源)',
+ maintainers: ['EthanWng97'],
+ handler,
+};
+
+async function handler(ctx) {
const wechatId = ctx.req.param('id');
let url = `${host}/weixin`;
let response = await got({
@@ -49,10 +68,10 @@ export default async (ctx) => {
await finishArticleItem(item);
- ctx.set('data', {
+ return {
title: `${title} 的微信公众号`,
link: url,
description: `${title} 的微信公众号`,
item: [item],
- });
-};
+ };
+}
diff --git a/lib/routes/wechat/tgchannel.ts b/lib/routes/wechat/tgchannel.ts
index 8fedef9a9..bcc683001 100644
--- a/lib/routes/wechat/tgchannel.ts
+++ b/lib/routes/wechat/tgchannel.ts
@@ -1,8 +1,27 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { finishArticleItem } from '@/utils/wechat-mp';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/tgchannel/:id/:mpName?/:searchQueryType?',
+ categories: ['traditional-media'],
+ example: '/wechat/tgchannel/lifeweek',
+ parameters: { id: '公众号绑定频道 id', mpName: '欲筛选的公众号全名(URL-encoded,精确匹配),在频道订阅了多个公众号时可选用', searchQueryType: '搜索查询类型,见下表' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '公众号(Telegram 频道来源)',
+ maintainers: ['LogicJake', 'Rongronggg9'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const mpName = ctx.req.param('mpName') ?? '';
let searchQueryType = ctx.req.param('searchQueryType') ?? '0';
@@ -137,10 +156,10 @@ export default async (ctx) => {
);
out.reverse();
- ctx.set('data', {
+ return {
title: $('.tgme_channel_info_header_title').text(),
link: `https://t.me/s/${id}`,
item: out.filter(Boolean),
allowEmpty: !!mpName,
- });
-};
+ };
+}
diff --git a/lib/routes/wechat/uread.ts b/lib/routes/wechat/uread.ts
index 6f466d907..2195634e9 100644
--- a/lib/routes/wechat/uread.ts
+++ b/lib/routes/wechat/uread.ts
@@ -1,8 +1,27 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/uread/:userid',
+ categories: ['traditional-media'],
+ example: '/wechat/uread/shensing',
+ parameters: { userid: '公众号的微信号, 可在 微信-公众号-更多资料 中找到。并不是所有的都支持,能不能用随缘' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '公众号(优读来源)',
+ maintainers: ['kt286'],
+ handler,
+};
+
+async function handler(ctx) {
const userid = ctx.req.param('userid');
const url = `http://119.29.146.143:8080/reading/subscription/account/recent?uid=${userid}`;
const response = await got({
@@ -44,9 +63,9 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: `优读 - ${userid}`,
link: 'https://uread.ai/',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/wechat/wechat2rss.ts b/lib/routes/wechat/wechat2rss.ts
index 794d995b6..e6185a175 100644
--- a/lib/routes/wechat/wechat2rss.ts
+++ b/lib/routes/wechat/wechat2rss.ts
@@ -1,8 +1,27 @@
+import { Route } from '@/types';
import parser from '@/utils/rss-parser';
import { parseDate } from '@/utils/parse-date';
import { finishArticleItem } from '@/utils/wechat-mp';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/wechat2rss/:id',
+ categories: ['traditional-media'],
+ example: '/wechat/wechat2rss/5b925323244e9737c39285596c53e3a2f4a30774',
+ parameters: { id: '公众号 id,打开 `https://wechat2rss.xlab.app/posts/list/`,在 URL 中找到 id;注意不是公众号页的 id,而是订阅的 id' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '公众号(Wechat2RSS 来源)',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const baseUrl = 'https://wechat2rss.xlab.app';
@@ -18,11 +37,11 @@ export default async (ctx) => {
items = await Promise.all(items.map((item) => finishArticleItem(item)));
- ctx.set('data', {
+ return {
title,
link,
description,
image: image.url,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/wechat/wxnmh.ts b/lib/routes/wechat/wxnmh.ts
index 07ba3bb28..494207ce4 100644
--- a/lib/routes/wechat/wxnmh.ts
+++ b/lib/routes/wechat/wxnmh.ts
@@ -36,10 +36,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `${name} - 微信公众号`,
link: 'https://www.wxnmh.com/',
description,
item,
- });
+ };
};
diff --git a/lib/routes/weekendhk/posts.ts b/lib/routes/weekendhk/posts.ts
index ade3396c3..f48f38d59 100644
--- a/lib/routes/weekendhk/posts.ts
+++ b/lib/routes/weekendhk/posts.ts
@@ -1,7 +1,19 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/',
+ radar: {
+ source: ['weekendhk.com/'],
+ target: '',
+ },
+ name: 'Unknown',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const baseUrl = 'https://www.weekendhk.com';
const response = await got(`${baseUrl}/wp-json/wp/v2/posts`, {
searchParams: {
@@ -18,12 +30,12 @@ export default async (ctx) => {
author: item.creator_editor,
}));
- ctx.set('data', {
+ return {
title: '新假期周刊',
description: '新假期周刊網站為全港最強吃喝玩樂搵節目平台。網羅世界各地最詳盡旅遊潮流資訊;最新鮮熱辣本地飲食情報;最好玩周末玩樂節目,即時瞓身報導,全天候為你update。',
link: baseUrl,
language: 'zh-HK',
image: `${baseUrl}/wp-content/themes/bucket/theme-content/images/196x196.png`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/weibo/friends.ts b/lib/routes/weibo/friends.ts
index dc6c81478..cc7b9ada5 100644
--- a/lib/routes/weibo/friends.ts
+++ b/lib/routes/weibo/friends.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import querystring from 'querystring';
import got from '@/utils/got';
@@ -5,7 +6,29 @@ import { config } from '@/config';
import weiboUtils from './utils';
import { fallback, queryToBoolean } from '@/utils/readable-social';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/friends/:routeParams?',
+ categories: ['new-media'],
+ example: '/weibo/friends',
+ parameters: { routeParams: '额外参数;请参阅上面的说明和表格' },
+ features: {
+ requireConfig: true,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['weibo.com/'],
+ target: '/friends',
+ },
+ name: '最新关注时间线',
+ maintainers: ['CaoMeiYouRen'],
+ handler,
+};
+
+async function handler(ctx) {
if (!config.weibo.cookies) {
throw new Error('Weibo Friends Timeline is not available due to the absense of [Weibo Cookies]. Check relevant config tutorial');
}
@@ -123,4 +146,4 @@ export default async (ctx) => {
item: resultItems,
})
);
-};
+}
diff --git a/lib/routes/weibo/group.ts b/lib/routes/weibo/group.ts
index 9e6a3a051..6812f9a59 100644
--- a/lib/routes/weibo/group.ts
+++ b/lib/routes/weibo/group.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import querystring from 'querystring';
import got from '@/utils/got';
@@ -5,7 +6,25 @@ import { config } from '@/config';
import weiboUtils from './utils';
import { fallback, queryToBoolean } from '@/utils/readable-social';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/group/:gid/:gname?/:routeParams?',
+ categories: ['new-media'],
+ example: '/weibo/group/4541216424989965',
+ parameters: { gid: '分组id, 在网页版分组地址栏末尾`?gid=`处获取', gname: '分组显示名称; 默认为: `微博分组`', routeParams: '额外参数;请参阅上面的说明和表格' },
+ features: {
+ requireConfig: true,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '自定义分组',
+ maintainers: ['monologconnor', 'Rongronggg9'],
+ handler,
+};
+
+async function handler(ctx) {
if (!config.weibo.cookies) {
throw new Error('Weibo Group Timeline is not available due to the absense of [Weibo Cookies]. Check relevant config tutorial');
}
@@ -86,4 +105,4 @@ export default async (ctx) => {
item: resultItems,
})
);
-};
+}
diff --git a/lib/routes/weibo/keyword.ts b/lib/routes/weibo/keyword.ts
index 1bd4a6e78..fff458359 100644
--- a/lib/routes/weibo/keyword.ts
+++ b/lib/routes/weibo/keyword.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import querystring from 'querystring';
import got from '@/utils/got';
@@ -6,7 +7,25 @@ import timezone from '@/utils/timezone';
import { fallback, queryToBoolean } from '@/utils/readable-social';
import { config } from '@/config';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/keyword/:keyword/:routeParams?',
+ categories: ['new-media'],
+ example: '/weibo/keyword/DIYgod',
+ parameters: { keyword: '你想订阅的微博关键词', routeParams: '额外参数;请参阅上面的说明和表格' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '关键词',
+ maintainers: ['DIYgod', 'Rongronggg9'],
+ handler,
+};
+
+async function handler(ctx) {
const keyword = ctx.req.param('keyword');
const data = await cache.tryGet(
@@ -47,4 +66,4 @@ export default async (ctx) => {
}),
})
);
-};
+}
diff --git a/lib/routes/weibo/oasis/user.ts b/lib/routes/weibo/oasis/user.ts
index a0189c158..7e9c7fc7c 100644
--- a/lib/routes/weibo/oasis/user.ts
+++ b/lib/routes/weibo/oasis/user.ts
@@ -1,7 +1,30 @@
+import { Route } from '@/types';
import buildData from '@/utils/common-config';
import weiboUtils from '../utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/oasis/user/:userid',
+ categories: ['new-media'],
+ example: '/weibo/oasis/user/1990895721',
+ parameters: { userid: '用户 id, 可在用户主页 URL 中找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['m.weibo.cn/u/:uid', 'm.weibo.cn/profile/:uid'],
+ target: '/user/:uid',
+ },
+ name: '用户',
+ maintainers: ['kt286'],
+ handler,
+};
+
+async function handler(ctx) {
const userid = ctx.req.param('userid');
const link = `https://oasis.weibo.cn/v1/h5/share?uid=${userid}`;
ctx.set(
@@ -21,4 +44,4 @@ export default async (ctx) => {
})
)
);
-};
+}
diff --git a/lib/routes/weibo/search/hot.ts b/lib/routes/weibo/search/hot.ts
index ad4c34de4..765f543da 100644
--- a/lib/routes/weibo/search/hot.ts
+++ b/lib/routes/weibo/search/hot.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -13,7 +14,28 @@ import * as path from 'node:path';
let wpic = 'false';
let fullpic = 'false';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/search/hot/:fulltext?',
+ categories: ['new-media'],
+ example: '/weibo/search/hot',
+ parameters: { fulltext: 'N' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['s.weibo.com/top/summary'],
+ },
+ name: '热搜榜',
+ maintainers: ['xyqfer', 'shinemoon'],
+ handler,
+};
+
+async function handler(ctx) {
wpic = ctx.req.query('pic') ?? 'false';
fullpic = ctx.req.query('fullpic') ?? 'false';
const {
@@ -67,14 +89,14 @@ export default async (ctx) => {
}
// Update ctx
- ctx.set('data', {
+ return {
title: '微博热搜榜',
link: 'https://s.weibo.com/top/summary?cate=realtimehot',
description: '实时热点,每分钟更新一次',
item: resultItems,
- });
+ };
// ctx.set('data', weiboUtils.sinaimgTvax(ctx.state.data)); // no image in the route
-};
+}
async function fetchContent(url) {
// Fetch the subpageinof
diff --git a/lib/routes/weibo/super-index.ts b/lib/routes/weibo/super-index.ts
index cef66ba59..d7b41c575 100644
--- a/lib/routes/weibo/super-index.ts
+++ b/lib/routes/weibo/super-index.ts
@@ -1,10 +1,33 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import weiboUtils from './utils';
import queryString from 'query-string';
import { config } from '@/config';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/super_index/:id/:type?/:routeParams?',
+ categories: ['new-media'],
+ example: '/weibo/super_index/1008084989d223732bf6f02f75ea30efad58a9/sort_time',
+ parameters: { id: '超话ID', type: '类型:见下表', routeParams: '额外参数;请参阅上面的说明和表格' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['weibo.com/p/:id/super_index'],
+ target: '/super_index/:id',
+ },
+ name: '超话',
+ maintainers: ['zengxs', 'Rongronggg9'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const type = ctx.req.param('type') ?? 'feed';
@@ -54,4 +77,4 @@ export default async (ctx) => {
item: resultItems,
})
);
-};
+}
diff --git a/lib/routes/weibo/timeline.ts b/lib/routes/weibo/timeline.ts
index 7171ab50e..2a70d7b8d 100644
--- a/lib/routes/weibo/timeline.ts
+++ b/lib/routes/weibo/timeline.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import querystring from 'querystring';
import got from '@/utils/got';
@@ -6,7 +7,25 @@ import { config } from '@/config';
import weiboUtils from './utils';
import { fallback, queryToBoolean } from '@/utils/readable-social';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/timeline/:uid/:feature?/:routeParams?',
+ categories: ['new-media'],
+ example: '/weibo/timeline/3306934123',
+ parameters: { uid: '用户的uid', feature: '过滤类型ID,0:全部、1:原创、2:图片、3:视频、4:音乐,默认为0。', routeParams: '额外参数;请参阅上面的说明和表格' },
+ features: {
+ requireConfig: true,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '个人时间线',
+ maintainers: ['zytomorrow', 'DIYgod', 'Rongronggg9'],
+ handler,
+};
+
+async function handler(ctx) {
const uid = ctx.req.param('uid');
const feature = ctx.req.param('feature') || 0;
const routeParams = ctx.req.param('routeParams') || undefined;
@@ -155,4 +174,4 @@ export default async (ctx) => {
});
ctx.redirect(`https://api.weibo.com/oauth2/authorize?client_id=${app_key}&redirect_uri=${redirect_url}${routeParams ? `&state=${feature}/${routeParams.replaceAll('&', '%26')}` : ''}`);
}
-};
+}
diff --git a/lib/routes/weibo/user.ts b/lib/routes/weibo/user.ts
index abbef06d9..c93a5f966 100644
--- a/lib/routes/weibo/user.ts
+++ b/lib/routes/weibo/user.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import querystring from 'querystring';
import got from '@/utils/got';
@@ -7,7 +8,29 @@ import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
import { fallback, queryToBoolean } from '@/utils/readable-social';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/user/:uid/:routeParams?',
+ categories: ['new-media'],
+ example: '/weibo/user/1195230310',
+ parameters: { uid: '用户 id, 博主主页打开控制台执行 `$CONFIG.oid` 获取', routeParams: '额外参数;请参阅上面的说明和表格;特别地,当 `routeParams=1` 时开启微博视频显示' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['m.weibo.cn/u/:uid', 'm.weibo.cn/profile/:uid'],
+ target: '/user/:uid',
+ },
+ name: '博主',
+ maintainers: ['DIYgod', 'iplusx', 'Rongronggg9'],
+ handler,
+};
+
+async function handler(ctx) {
const uid = ctx.req.param('uid');
let displayVideo = '1';
let displayArticle = '0';
@@ -149,4 +172,4 @@ export default async (ctx) => {
item: resultItems,
})
);
-};
+}
diff --git a/lib/routes/wenku8/chapter.ts b/lib/routes/wenku8/chapter.ts
index ba6da82a7..323c66213 100644
--- a/lib/routes/wenku8/chapter.ts
+++ b/lib/routes/wenku8/chapter.ts
@@ -1,8 +1,27 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import iconv from 'iconv-lite';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/chapter/:id',
+ categories: ['government'],
+ example: '/wenku8/chapter/74',
+ parameters: { id: '小说 id, 可在对应小说页 URL 中找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '章节',
+ maintainers: ['zsakvo'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const index = Number.parseInt(id / 1000);
@@ -27,9 +46,9 @@ export default async (ctx) => {
});
});
- ctx.set('data', {
+ return {
title: `轻小说文库 ${name}`,
link: `https://www.wenku8.net/book/${id}.htm`,
item: chapter_item,
- });
-};
+ };
+}
diff --git a/lib/routes/wenku8/index.ts b/lib/routes/wenku8/index.ts
index f1b3104e0..7d6580892 100644
--- a/lib/routes/wenku8/index.ts
+++ b/lib/routes/wenku8/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import iconv from 'iconv-lite';
@@ -21,7 +22,14 @@ const cateTitleMap = {
articlelist: '轻小说列表',
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:category?',
+ name: 'Unknown',
+ maintainers: ['Fatpandac'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? 'lastupdate';
const response = await got({
method: 'get',
@@ -44,9 +52,9 @@ export default async (ctx) => {
}))
.get();
- ctx.set('data', {
+ return {
title: `轻小说文库 - ${cateTitleMap[category]}`,
link: cateUrlMap[category],
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/wenku8/volume.ts b/lib/routes/wenku8/volume.ts
index 0054d78fd..582511295 100644
--- a/lib/routes/wenku8/volume.ts
+++ b/lib/routes/wenku8/volume.ts
@@ -1,9 +1,28 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { decode } from 'iconv-lite';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/volume/:id',
+ categories: ['government'],
+ example: '/wenku8/volume/1163',
+ parameters: { id: '小说 id, 可在对应小说页 URL 中找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '最新卷',
+ maintainers: ['huangliangshusheng'],
+ handler,
+};
+
+async function handler(ctx) {
const aid = Number.parseInt(ctx.req.param('id'));
const link = `https://www.wenku8.net/novel/${Math.floor(aid / 1000)}/${aid}/index.htm`;
const $ = load(await get(link));
@@ -17,7 +36,7 @@ export default async (ctx) => {
.toArray()
.map((a) => ({ link: a.attribs.href }));
- ctx.set('data', {
+ return {
title: `轻小说文库 ${$('#title').text()} 最新卷`,
link,
item: await cache.tryGet(volumeUrl, async () =>
@@ -35,7 +54,7 @@ export default async (ctx) => {
.filter((chapter) => chapter.description)
.toReversed()
),
- });
-};
+ };
+}
const get = async (url: string) => decode(await got(url).buffer(), 'gbk');
diff --git a/lib/routes/wfdf/news.ts b/lib/routes/wfdf/news.ts
index b4fa0a92d..80aabc3ce 100644
--- a/lib/routes/wfdf/news.ts
+++ b/lib/routes/wfdf/news.ts
@@ -1,7 +1,29 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news',
+ categories: ['other'],
+ example: '/wfdf/news',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['wfdf.sport/news/', 'wfdf.sport/'],
+ },
+ name: 'News',
+ maintainers: ['HankChow'],
+ handler,
+};
+
+async function handler(ctx) {
const baseUrl = 'https://wfdf.sport';
const { data: response } = await got(`${baseUrl}/wp-json/wp/v2/posts`, {
searchParams: {
@@ -19,11 +41,11 @@ export default async (ctx) => {
author: item._embedded.author[0].name,
}));
- ctx.set('data', {
+ return {
title: 'WFDF News',
link: `${baseUrl}/news/`,
image: `${baseUrl}/favicon.ico`,
description: 'WFDF 新闻',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/wfu/jwc.ts b/lib/routes/wfu/jwc.ts
index 8f83d201b..328bfa28a 100644
--- a/lib/routes/wfu/jwc.ts
+++ b/lib/routes/wfu/jwc.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -6,7 +7,28 @@ import { load } from 'cheerio';
const baseUrl = 'https://jwc.wfu.edu.cn/3742/list.htm';
const sizeTitle = '潍坊学院教务处新闻';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/jwc',
+ categories: ['forecast'],
+ example: '/wfu/jwc',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['jwc.wfu.edu.cn/'],
+ },
+ name: '教务处通知',
+ maintainers: ['cccht'],
+ handler,
+};
+
+async function handler() {
const response = await got({
method: 'get',
url: baseUrl,
@@ -45,10 +67,10 @@ export default async (ctx) => {
.get()
);
- ctx.set('data', {
+ return {
title: sizeTitle,
link: baseUrl,
description: '潍坊学院教务处通知(通知为文件需下载)',
item: result,
- });
-};
+ };
+}
diff --git a/lib/routes/wfu/news.ts b/lib/routes/wfu/news.ts
index 4a6202543..be7382802 100644
--- a/lib/routes/wfu/news.ts
+++ b/lib/routes/wfu/news.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -45,7 +46,29 @@ async function loadContent(link) {
return { description };
}
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/:type?',
+ categories: ['forecast'],
+ example: '/wfu/news/wyyw',
+ parameters: { type: '分类,默认为 `wyyw`,具体参数见下表' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['news.wfu.edu.cn/'],
+ target: '/news',
+ },
+ name: '新闻',
+ maintainers: ['cccht'],
+ handler,
+};
+
+async function handler(ctx) {
// 默认 潍院要闻 然后获取列表页面
const type = ctx.req.param('type') ?? 'wyyw';
const listPageUrl = baseUrl + catrgoryMap[type][0];
@@ -88,10 +111,10 @@ export default async (ctx) => {
.get()
);
- ctx.set('data', {
+ return {
title: catrgoryMap[type][1] + sizeTitle,
link: baseUrl,
description: catrgoryMap[type][1] + sizeTitle,
item: result,
- });
-};
+ };
+}
diff --git a/lib/routes/whitehouse/briefing-room.ts b/lib/routes/whitehouse/briefing-room.ts
index 7e5965a67..8e0e5f720 100644
--- a/lib/routes/whitehouse/briefing-room.ts
+++ b/lib/routes/whitehouse/briefing-room.ts
@@ -1,9 +1,32 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/briefing-room/:category?',
+ categories: ['study'],
+ example: '/whitehouse/briefing-room',
+ parameters: { category: 'Category, see below, all by default' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['whitehouse.gov/briefing-room/:category', 'whitehouse.gov/'],
+ target: '/briefing-room/:category',
+ },
+ name: 'Briefing Room',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? '';
const rootUrl = 'https://www.whitehouse.gov';
@@ -43,9 +66,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('title').text(),
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/whitehouse/ostp.ts b/lib/routes/whitehouse/ostp.ts
index 25f8028f9..4249232bc 100644
--- a/lib/routes/whitehouse/ostp.ts
+++ b/lib/routes/whitehouse/ostp.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/ostp',
+ categories: ['study'],
+ example: '/whitehouse/ostp',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['whitehouse.gov/ostp', 'whitehouse.gov/'],
+ },
+ name: 'Office of Science and Technology Policy',
+ maintainers: ['LyleLee'],
+ handler,
+};
+
+async function handler() {
const rootUrl = 'https://www.whitehouse.gov';
const currentUrl = `${rootUrl}/ostp/news-updates`;
const response = await got({
@@ -42,10 +64,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: 'Whitehouse OSTP',
link: currentUrl,
description: 'Whitehouse Office of Science and Technology Policy',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/who/news-room.ts b/lib/routes/who/news-room.ts
index 5d4d2dd43..3ec2f4819 100644
--- a/lib/routes/who/news-room.ts
+++ b/lib/routes/who/news-room.ts
@@ -1,9 +1,32 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news-room/:category?/:language?',
+ categories: ['study'],
+ example: '/who/news-room/feature-stories',
+ parameters: { category: 'Category, see below, Feature stories by default', language: 'Language, see below, English by default' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['who.int/news-room/:type'],
+ target: '/news-room/:type',
+ },
+ name: 'Newsroom',
+ maintainers: ['LogicJake', 'nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? 'feature-stories';
const language = ctx.req.param('language') ?? '';
@@ -68,9 +91,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `${$('meta[property="og:title"]').attr('content')} - WHO`,
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/who/news.ts b/lib/routes/who/news.ts
index f0513be82..a1de863f0 100644
--- a/lib/routes/who/news.ts
+++ b/lib/routes/who/news.ts
@@ -1,8 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/:language?',
+ categories: ['study'],
+ example: '/who/news',
+ parameters: { language: 'Language, see below, English by default' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['who.int/news'],
+ target: '/news',
+ },
+ name: 'News',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const language = ctx.req.param('language') ?? 'en';
const rootUrl = 'https://www.who.int';
@@ -35,9 +58,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: 'News - WHO',
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/who/speeches.ts b/lib/routes/who/speeches.ts
index d3cde5781..3373cac10 100644
--- a/lib/routes/who/speeches.ts
+++ b/lib/routes/who/speeches.ts
@@ -1,9 +1,32 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/speeches/:language?',
+ categories: ['study'],
+ example: '/who/speeches',
+ parameters: { language: 'Language, see below, English by default' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['who.int/director-general/speeches'],
+ target: '/speeches',
+ },
+ name: 'Speeches',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const language = ctx.req.param('language') || 'en';
const rootUrl = 'https://www.who.int';
@@ -38,9 +61,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: 'Speeches - WHO',
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/whu/cs.ts b/lib/routes/whu/cs.ts
index 70db9fdae..90ffabae5 100644
--- a/lib/routes/whu/cs.ts
+++ b/lib/routes/whu/cs.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -6,7 +7,25 @@ import { load } from 'cheerio';
const baseUrl = 'https://cs.whu.edu.cn';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/cs/:type',
+ categories: ['forecast'],
+ example: '/whu/cs/2',
+ parameters: { type: '公告类型,详见表格' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '计算机学院公告',
+ maintainers: ['ttyfly'],
+ handler,
+};
+
+async function handler(ctx) {
const type = Number.parseInt(ctx.req.param('type'));
let link;
@@ -79,9 +98,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('title').first().text(),
link,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/whu/gs/index.ts b/lib/routes/whu/gs/index.ts
index 0e8dbbd45..e65e12a27 100644
--- a/lib/routes/whu/gs/index.ts
+++ b/lib/routes/whu/gs/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -15,7 +16,29 @@ const gsIndexMap = new Map([
[8, 'tzgg/zh.htm'],
]);
-export default async (ctx) => {
+export const route: Route = {
+ path: '/gs/:type?',
+ categories: ['forecast'],
+ example: '/whu/gs/0',
+ parameters: { type: '分类,默认为 `0`,具体参数见下表' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['gs.whu.edu.cn/index.htm', 'gs.whu.edu.cn/'],
+ target: '/gs',
+ },
+ name: '研究生院',
+ maintainers: ['Delreyaa'],
+ handler,
+};
+
+async function handler(ctx) {
const host = 'https://gs.whu.edu.cn/';
const type = (ctx.params && Number.parseInt(ctx.req.param('type'))) || 0;
const response = await got(host + gsIndexMap.get(type));
@@ -77,10 +100,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `武汉大学研究生院 - ${feed_title}`,
link: host + gsIndexMap.get(type),
description: '武大研究生院',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/whu/hyxt.ts b/lib/routes/whu/hyxt.ts
index 9949aeb15..f89fc8f0d 100644
--- a/lib/routes/whu/hyxt.ts
+++ b/lib/routes/whu/hyxt.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -5,7 +6,14 @@ import { parseDate } from '@/utils/parse-date';
import { domain, processMeta, getMeta, processItems } from './util';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/hyxt/:category{.+}?',
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const { category = 'tzgg' } = ctx.req.param();
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30;
@@ -37,7 +45,7 @@ export default async (ctx) => {
const siteName = getMeta(meta, 'SiteName');
const columnName = getMeta(meta, 'ColumnName');
- ctx.set('data', {
+ return {
item: items,
title: `${siteName} - ${columnName}`,
link: currentUrl,
@@ -47,5 +55,5 @@ export default async (ctx) => {
subtitle: columnName,
author: siteName,
allowEmpty: true,
- });
-};
+ };
+}
diff --git a/lib/routes/whu/news.ts b/lib/routes/whu/news.ts
index 3830248c0..c328599bc 100644
--- a/lib/routes/whu/news.ts
+++ b/lib/routes/whu/news.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -10,7 +11,14 @@ import * as path from 'node:path';
import { domain, processMeta, getMeta, processItems } from './util';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/:category{.+}?',
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const { category = 'wdzx/wdyw' } = ctx.req.param();
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 10;
@@ -58,7 +66,7 @@ export default async (ctx) => {
const icon = new URL($('link[rel="shortcut icon"]').prop('href'), rootUrl).href;
- ctx.set('data', {
+ return {
item: items,
title: `${siteName} - ${columnName}`,
link: currentUrl,
@@ -70,5 +78,5 @@ export default async (ctx) => {
subtitle: columnName,
author: siteName,
allowEmpty: true,
- });
-};
+ };
+}
diff --git a/lib/routes/wikinews/index.ts b/lib/routes/wikinews/index.ts
index 2a68d4091..628714d0b 100644
--- a/lib/routes/wikinews/index.ts
+++ b/lib/routes/wikinews/index.ts
@@ -1,10 +1,32 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
const currentURL = 'https://zh.wikinews.org/wiki/Special:%E6%96%B0%E9%97%BB%E8%AE%A2%E9%98%85';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/latest',
+ categories: ['traditional-media'],
+ example: '/wikinews/latest',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['zh.wikinews.org/wiki/Special:新闻订阅'],
+ },
+ name: '最新新闻',
+ maintainers: ['KotoriK'],
+ handler,
+};
+
+async function handler() {
const resp = await got(currentURL);
const $ = load(resp.data);
const urls = $('url')
@@ -35,9 +57,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: '最新新闻 - 维基新闻',
link: currentURL,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/winstall/update.ts b/lib/routes/winstall/update.ts
index f943d2616..5e8ef6924 100644
--- a/lib/routes/winstall/update.ts
+++ b/lib/routes/winstall/update.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -8,7 +9,28 @@ import { config } from '@/config';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:appId',
+ categories: ['university'],
+ example: '/winstall/Mozilla.Firefox',
+ parameters: { appId: 'Application ID' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['winstall.app/apps/:appId'],
+ },
+ name: 'Apps Update',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const baseUrl = 'https://winstall.app';
const appId = ctx.req.param('appId');
@@ -36,12 +58,12 @@ export default async (ctx) => {
guid: `winstall:${appId}:${item.version}`,
}));
- ctx.set('data', {
+ return {
title: `${app.name} - winstall`,
description: app.desc,
link: `${baseUrl}/apps/${appId}`,
image: `https://api.winstall.app/icons/next/${appId}.webp`,
language: 'en',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/wise/pair.ts b/lib/routes/wise/pair.ts
index 183c17fe3..997ce40a1 100644
--- a/lib/routes/wise/pair.ts
+++ b/lib/routes/wise/pair.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -11,7 +12,25 @@ import { art } from '@/utils/render';
import * as path from 'node:path';
const renderDesc = (content) => art(path.join(__dirname, 'templates/description.art'), content);
-export default async (ctx) => {
+export const route: Route = {
+ path: '/pair/:source/:target',
+ categories: ['other'],
+ example: '/wise/pair/GBP/USD',
+ parameters: { source: 'Base currency abbreviation', target: 'Quote currency abbreviation' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'FX Pair Yesterday',
+ maintainers: ['HenryQW'],
+ handler,
+};
+
+async function handler(ctx) {
let yesterday = dayjs().subtract(1, 'day');
const dayBefore = yesterday.subtract(1, 'day').format('YYYY-MM-DD');
yesterday = yesterday.format('YYYY-MM-DD');
@@ -60,10 +79,10 @@ export default async (ctx) => {
};
});
- ctx.set('data', {
+ return {
title: `${source} to ${target} by Wise`,
link,
description: `Exchange Rate from Wise`,
item: [single],
- });
-};
+ };
+}
diff --git a/lib/routes/wizfile/index.ts b/lib/routes/wizfile/index.ts
index 798f17cf3..e8c61033a 100644
--- a/lib/routes/wizfile/index.ts
+++ b/lib/routes/wizfile/index.ts
@@ -1,10 +1,32 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
const rootUrl = 'https://antibody-software.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/updates',
+ categories: ['university'],
+ example: '/wizfile/updates',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['antibody-software.com/wizfile/download'],
+ },
+ name: 'Version History',
+ maintainers: ['Fatpandac'],
+ handler,
+};
+
+async function handler() {
const currentUrl = `${rootUrl}/wizfile/download`;
const response = await got(currentUrl);
@@ -33,9 +55,9 @@ export default async (ctx) => {
})
.get();
- ctx.set('data', {
+ return {
title: `WziFile - 更新日志`,
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/wmc-bj/publish.ts b/lib/routes/wmc-bj/publish.ts
index 771028688..ca62d1b08 100644
--- a/lib/routes/wmc-bj/publish.ts
+++ b/lib/routes/wmc-bj/publish.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -8,7 +9,14 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/publish/:category{.+}?',
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const { category = 'CRA-Reanalysis/2m-Temperature/6-hour/index.html' } = ctx.req.param();
const rootUrl = 'http://www.wmc-bj.net';
@@ -46,7 +54,7 @@ export default async (ctx) => {
const image = 'http://image.nmc.cn/static/wmc/img/logo-cma.png';
const icon = $('link[rel="shortcut icon"]').prop('href');
- ctx.set('data', {
+ return {
item: items,
title,
link: currentUrl,
@@ -57,5 +65,5 @@ export default async (ctx) => {
subtitle: categories.join(' > '),
author,
allowEmpty: true,
- });
-};
+ };
+}
diff --git a/lib/routes/wmpvp/index.ts b/lib/routes/wmpvp/index.ts
index 4b07c7f1c..3d5ad09ee 100644
--- a/lib/routes/wmpvp/index.ts
+++ b/lib/routes/wmpvp/index.ts
@@ -1,7 +1,26 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/:type',
+ categories: ['reading'],
+ example: '/wmpvp/news/1',
+ parameters: { type: '资讯分类,见下表' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '资讯列表',
+ maintainers: ['tssujt'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type');
const response = await got({
@@ -26,9 +45,9 @@ export default async (ctx) => {
};
});
- ctx.set('data', {
+ return {
title: `完美世界电竞`,
link: `https://news.wmpvp.com/`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/wnacg/index.ts b/lib/routes/wnacg/index.ts
index 7269f828e..2dc502134 100644
--- a/lib/routes/wnacg/index.ts
+++ b/lib/routes/wnacg/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -29,7 +30,18 @@ const categories = {
const baseUrl = 'https://www.wnacg.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: ['/', '/category/:cid', '/tag/:tag'],
+ radar: {
+ source: ['wnacg.org/albums.html', 'wnacg.org/'],
+ target: '',
+ },
+ name: 'Unknown',
+ maintainers: ['KenMizz'],
+ handler,
+};
+
+async function handler(ctx) {
const { cid, tag } = ctx.req.param();
if (cid && !Object.keys(categories).includes(cid)) {
throw new Error('此分类不存在');
@@ -100,9 +112,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('head title').text(),
link: url,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/worldjournal/index.ts b/lib/routes/worldjournal/index.ts
index 93e6cb063..b1c10c181 100644
--- a/lib/routes/worldjournal/index.ts
+++ b/lib/routes/worldjournal/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -6,7 +7,18 @@ import timezone from '@/utils/timezone';
const baseUrl = 'https://www.worldjournal.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:path{.+}?',
+ radar: {
+ source: ['worldjournal.com/wj/*path'],
+ target: '/:path',
+ },
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const { path = 'cate/breaking' } = ctx.req.param();
const { data: response } = await got(`${baseUrl}/wj/${path}`);
@@ -51,11 +63,11 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('head title').text(),
description: $('head meta[name="description"]').attr('content'),
image: 'https://www.worldjournal.com/static/img/icons/icon-144x144.png',
link: `${baseUrl}/wj/${path}`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/woshipm/popular.ts b/lib/routes/woshipm/popular.ts
index 0f53248eb..11ee384e9 100644
--- a/lib/routes/woshipm/popular.ts
+++ b/lib/routes/woshipm/popular.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -9,7 +10,29 @@ const rangeMap = {
monthly: '月榜',
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/popular/:range?',
+ categories: ['traditional-media'],
+ example: '/woshipm/popular',
+ parameters: { range: '时间,见下表,默认为 `daily`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['woshipm.com/'],
+ target: '/popular',
+ },
+ name: '热门文章',
+ maintainers: ['WenryXu'],
+ handler,
+};
+
+async function handler(ctx) {
const { range = 'daily' } = ctx.req.param();
const { data: response } = await got(`${baseUrl}/api2/app/article/popular/${range}`);
@@ -26,9 +49,9 @@ export default async (ctx) => {
const result = await Promise.all(list.map((item) => parseArticle(item, cache.tryGet)));
- ctx.set('data', {
+ return {
title: `热门文章 - ${rangeMap[range]} - 人人都是产品经理`,
link: baseUrl,
item: result,
- });
-};
+ };
+}
diff --git a/lib/routes/woshipm/user-article.ts b/lib/routes/woshipm/user-article.ts
index 9e3ef2f50..5cbef6c2c 100644
--- a/lib/routes/woshipm/user-article.ts
+++ b/lib/routes/woshipm/user-article.ts
@@ -1,10 +1,32 @@
+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 { baseUrl, parseArticle } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/user_article/:id',
+ categories: ['traditional-media'],
+ example: '/woshipm/user_article/324696',
+ parameters: { id: '用户 id' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['woshipm.com/u/:id'],
+ },
+ name: '用户文章',
+ maintainers: ['LogicJake'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 12;
const link = `${baseUrl}/u/${id}`;
@@ -28,11 +50,11 @@ export default async (ctx) => {
const items = await Promise.all(list.map((item) => parseArticle(item, cache.tryGet)));
- ctx.set('data', {
+ return {
title: `${name}的文章-人人都是产品经理`,
description: $('.author--meta .description').text(),
image: $('.author--meta .avatar').attr('src').split('!')[0],
link,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/woshipm/wen.ts b/lib/routes/woshipm/wen.ts
index 8f325747c..1568926b3 100644
--- a/lib/routes/woshipm/wen.ts
+++ b/lib/routes/woshipm/wen.ts
@@ -1,10 +1,32 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
const baseUrl = 'https://wen.woshipm.com';
import { parseRelativeDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/wen',
+ categories: ['traditional-media'],
+ example: '/woshipm/wen',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['wen.woshipm.com/'],
+ },
+ name: '天天问',
+ maintainers: ['WenryXu'],
+ handler,
+};
+
+async function handler() {
const response = await got(`${baseUrl}/m/main/indexNewData.html`);
const $ = load(response.data);
const postList = $('.article-list-item')
@@ -29,9 +51,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: '天天问 - 人人都是产品经理',
link: baseUrl,
item: result,
- });
-};
+ };
+}
diff --git a/lib/routes/wsj/news.ts b/lib/routes/wsj/news.ts
index 65abc47a7..696da6d5b 100644
--- a/lib/routes/wsj/news.ts
+++ b/lib/routes/wsj/news.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { asyncPoolAll, parseArticle } from './utils';
@@ -6,7 +7,25 @@ const hostMap = {
'zh-cn': 'https://cn.wsj.com/zh-hans',
'zh-tw': 'https://cn.wsj.com/zh-hant',
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:lang/:category?',
+ categories: ['bbs'],
+ example: '/wsj/en-us/opinion',
+ parameters: { lang: 'Language, `en-us`, `zh-cn`, `zh-tw`', category: 'Category. See below' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'News',
+ maintainers: ['oppilate'],
+ handler,
+};
+
+async function handler(ctx) {
const lang = ctx.req.param('lang');
const category = ctx.req.param('category') || '';
const host = hostMap[lang];
@@ -42,10 +61,10 @@ export default async (ctx) => {
});
const items = await asyncPoolAll(10, list, (item) => parseArticle(item));
- ctx.set('data', {
+ return {
title: `WSJ${subTitle}`,
link: url,
description: `WSJ${subTitle}`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/wsyu/news.ts b/lib/routes/wsyu/news.ts
index ce488fe7e..f4305e62c 100644
--- a/lib/routes/wsyu/news.ts
+++ b/lib/routes/wsyu/news.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -20,7 +21,25 @@ const typeMap = {
},
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/:type?',
+ categories: ['forecast'],
+ example: '/wsyu/news/xxyw',
+ parameters: { type: '分类,默认为 `xxyw`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '新闻中心',
+ maintainers: ['Derekmini'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type') || 'xxyw';
const link = baseUrl + typeMap[type].url;
const response = await got({
@@ -78,9 +97,9 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: '武昌首义学院-' + typeMap[type].name,
link,
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/wtu/index.ts b/lib/routes/wtu/index.ts
index 1f10f65cc..a7ce55719 100644
--- a/lib/routes/wtu/index.ts
+++ b/lib/routes/wtu/index.ts
@@ -1,8 +1,27 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:type',
+ categories: ['forecast'],
+ example: '/wtu/2',
+ parameters: { type: '公告类型,详见表格' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '信息门户公告',
+ maintainers: ['loyio'],
+ handler,
+};
+
+async function handler(ctx) {
const apiUrl = 'https://ehall.wtu.edu.cn/wtu/api/queryBulletinListByConditional.do?pageNum=1&pageSize=20&columnId=';
const listUrl = 'https://ehall.wtu.edu.cn/new/list.html?type=';
const psgUrl = 'https://ehall.wtu.edu.cn/new/detail-word.html?';
@@ -23,7 +42,7 @@ export default async (ctx) => {
});
const resJson = res.data.bulletinList;
- ctx.set('data', {
+ return {
title: `${title} - 武汉纺织大学信息门户`,
link: `${listUrl}${type}`,
description: `${title} - 武汉纺织大学信息门户`,
@@ -33,5 +52,5 @@ export default async (ctx) => {
link: item.GOTO_URL ?? `${psgUrl}type=${type}?bulletinId=${item.WID}`,
author: item.PUBLISH_USER_DEPT_NAME,
})),
- });
-};
+ };
+}
diff --git a/lib/routes/wtu/job.ts b/lib/routes/wtu/job.ts
index 44d1e9fd7..c40d5fad5 100644
--- a/lib/routes/wtu/job.ts
+++ b/lib/routes/wtu/job.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -34,7 +35,28 @@ function decodeData(str) {
return content.toString('utf8').substring(substr2Num);
}
-export default async (ctx) => {
+export const route: Route = {
+ path: '/job/:type',
+ categories: ['forecast'],
+ example: '/wtu/job/xxtz',
+ parameters: { type: '信息类型' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['wtu.91wllm.com/news/index/tag/:type'],
+ },
+ name: '就业信息',
+ maintainers: ['ticks-tan'],
+ handler,
+};
+
+async function handler(ctx) {
// 获取参数 type
const type = ctx.req.param('type');
const mapItem = typeMap.get(type);
@@ -72,10 +94,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: msgTitle,
link,
description: msgTitle,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/wyzxwk/article.ts b/lib/routes/wyzxwk/article.ts
index ebbd2cb47..e078aa204 100644
--- a/lib/routes/wyzxwk/article.ts
+++ b/lib/routes/wyzxwk/article.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/article/:id?',
+ categories: ['traditional-media'],
+ example: '/wyzxwk/article/shushe',
+ parameters: { id: '栏目 id,可在栏目页 URL 中找到,默认为时代观察' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['wyzxwk.com/Article/:id', 'wyzxwk.com/'],
+ },
+ name: '栏目',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id') ?? 'shidai';
const rootUrl = 'http://www.wyzxwk.com';
@@ -57,9 +79,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `${$('title').text().split(' - ')[0]} - 乌有之乡网刊`,
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/wzu/news.ts b/lib/routes/wzu/news.ts
index 80d58fde6..5a6aca24a 100644
--- a/lib/routes/wzu/news.ts
+++ b/lib/routes/wzu/news.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -49,7 +50,14 @@ async function loadContent(link) {
return $('div[id^=vsb_content]').html();
}
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/:type?',
+ name: 'Unknown',
+ maintainers: ['Chandler-Lu'],
+ handler,
+};
+
+async function handler(ctx) {
// 获取路由 Tag
const routeTag = Number.parseInt(ctx.req.param('type')) || 0;
// 设定新闻标题及 Url
@@ -61,7 +69,7 @@ export default async (ctx) => {
const $ = load(response.data);
const list = $('#News-sidebar-b-nav').find('li');
- ctx.set('data', {
+ return {
title: newsTitle,
link: newsLink,
description: '温州大学' + ' - ' + newsTitle,
@@ -81,5 +89,5 @@ export default async (ctx) => {
};
})
.get(),
- });
-};
+ };
+}
diff --git a/lib/routes/x-mol/news.ts b/lib/routes/x-mol/news.ts
index 1b34ca81c..91876205c 100644
--- a/lib/routes/x-mol/news.ts
+++ b/lib/routes/x-mol/news.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -10,7 +11,29 @@ import timezone from '@/utils/timezone';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/:tag?',
+ categories: ['journal'],
+ example: '/x-mol/news/3',
+ parameters: { tag: 'Tag number, can be obtained from news list URL. Empty value means news index.' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['x-mol.com/news/index'],
+ target: '/news',
+ },
+ name: 'News',
+ maintainers: ['cssxsh'],
+ handler,
+};
+
+async function handler(ctx) {
const tag = ctx.req.param('tag');
const urlPath = tag ? `news/tag/${tag}` : 'news/index';
const link = new URL(urlPath, utils.host).href;
@@ -58,10 +81,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('title').text(),
link: response.url,
description: $('meta[name="description"]').attr('content'),
item,
- });
-};
+ };
+}
diff --git a/lib/routes/x-mol/paper.ts b/lib/routes/x-mol/paper.ts
index 0c3077746..5cc897a58 100644
--- a/lib/routes/x-mol/paper.ts
+++ b/lib/routes/x-mol/paper.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -6,7 +7,25 @@ import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
import asyncPool from 'tiny-async-pool';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/paper/:type/:magazine',
+ categories: ['finance'],
+ example: '/x-mol/paper/0/9',
+ parameters: { type: 'type', magazine: 'magazine' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Journal',
+ maintainers: ['cssxsh'],
+ handler,
+};
+
+async function handler(ctx) {
const { type, magazine } = ctx.req.param();
const path = `paper/${type}/${magazine}`;
const link = new URL(path, utils.host).href;
@@ -65,10 +84,10 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: $('title').text(),
link: response.url,
description: $('meta[name="description"]').attr('content'),
item,
- });
-};
+ };
+}
diff --git a/lib/routes/x6d/index.ts b/lib/routes/x6d/index.ts
index 940e4c782..8e8e8ed8a 100644
--- a/lib/routes/x6d/index.ts
+++ b/lib/routes/x6d/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -6,7 +7,25 @@ import timezone from '@/utils/timezone';
const baseUrl = 'https://xd.x6d.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:id?',
+ categories: ['traditional-media'],
+ example: '/x6d/34',
+ parameters: { id: '分类 id,可在对应分类页面的 URL 中找到,默认为首页最近更新' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '分类',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const { id = 'latest' } = ctx.req.param();
const currentUrl = id === 'latest' ? baseUrl : `${baseUrl}/html/${id}.html`;
@@ -49,9 +68,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `小刀娱乐网 - ${$('title').text().split('-')[0]}`,
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/xaufe/jiaowu.ts b/lib/routes/xaufe/jiaowu.ts
index f586887be..5eec01087 100644
--- a/lib/routes/xaufe/jiaowu.ts
+++ b/lib/routes/xaufe/jiaowu.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -16,7 +17,25 @@ const categories = {
},
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/jiaowu/:category?',
+ categories: ['forecast'],
+ example: '/xaufe/jiaowu/tzgg',
+ parameters: { category: '分类,默认为通知公告' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '教务处',
+ maintainers: ['shaokeyibb'],
+ handler,
+};
+
+async function handler(ctx) {
const pCategory = ctx.req.param('category');
const category = categories[pCategory] || categories.tzgg;
@@ -44,7 +63,7 @@ export default async (ctx) => {
};
});
- ctx.set('data', {
+ return {
title: `${category.title}-${rootMeta.title}`,
link: rootMeta.url + category.url,
description: `${category.title}-${rootMeta.title}`,
@@ -66,5 +85,5 @@ export default async (ctx) => {
})
)
),
- });
-};
+ };
+}
diff --git a/lib/routes/xaut/index.ts b/lib/routes/xaut/index.ts
index 4feddb3b7..3aefdc0e4 100644
--- a/lib/routes/xaut/index.ts
+++ b/lib/routes/xaut/index.ts
@@ -1,9 +1,28 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { load } from 'cheerio';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/index/:category?',
+ categories: ['forecast'],
+ example: '/xaut/index/tzgg',
+ parameters: { category: '通知类别,默认为通知公告' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '学校主页',
+ maintainers: ['mocusez'],
+ handler,
+};
+
+async function handler(ctx) {
let category = ctx.req.param('category');
const dic_html = { tzgg: 'tzgg.htm', xyyw: 'xyyw.htm', mtbd: 'mtbd1.htm', xshd: 'xshd.htm' };
const dic_title = { tzgg: '通知公告', xyyw: '校园要闻', mtbd: '媒体播报', xshd: '学术活动' };
@@ -44,7 +63,7 @@ export default async (ctx) => {
})
.get();
- ctx.set('data', {
+ return {
// 源标题
title: '西安理工大学官网-' + dic_title[category],
// 源链接
@@ -69,5 +88,5 @@ export default async (ctx) => {
})
)
),
- });
-};
+ };
+}
diff --git a/lib/routes/xaut/jwc.ts b/lib/routes/xaut/jwc.ts
index 44de7e5a5..4d87ca9f3 100644
--- a/lib/routes/xaut/jwc.ts
+++ b/lib/routes/xaut/jwc.ts
@@ -1,9 +1,28 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { load } from 'cheerio';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/jwc/:category?',
+ categories: ['forecast'],
+ example: '/xaut/jwc/tzgg',
+ parameters: { category: '通知类别,默认为通知公告' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '教务处',
+ maintainers: ['mocusez'],
+ handler,
+};
+
+async function handler(ctx) {
let category = ctx.req.param('category');
const rootUrl = 'http://jwc.xaut.edu.cn/';
const dic_html = { tzgg: 'tzgg.htm', xwdt: 'xwdt.htm', gzzd: 'gzzd.htm', jggs: 'xkjs/jggs.htm', jsjg: 'xkjs/jsjg.htm', jsxx: 'xkjs/jsxx.htm', gkgs: 'gkgs.htm' };
@@ -37,7 +56,7 @@ export default async (ctx) => {
})
.get();
- ctx.set('data', {
+ return {
// 源标题
title: '西安理工大学教务处-' + dic_title[category],
// 源链接
@@ -62,5 +81,5 @@ export default async (ctx) => {
})
)
),
- });
-};
+ };
+}
diff --git a/lib/routes/xaut/rsc.ts b/lib/routes/xaut/rsc.ts
index e08f37b42..53fbcf299 100644
--- a/lib/routes/xaut/rsc.ts
+++ b/lib/routes/xaut/rsc.ts
@@ -1,9 +1,28 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { load } from 'cheerio';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/rsc/:category?',
+ categories: ['forecast'],
+ example: '/xaut/rsc/tzgg',
+ parameters: { category: '通知类别,默认为通知公告' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '人事处',
+ maintainers: ['mocusez', 'light0926'],
+ handler,
+};
+
+async function handler(ctx) {
let category = ctx.req.param('category');
// 这里的category是个形参
const dic_html = { tzgg: 'tzgg.htm', gzdt: 'gzdt.htm' };
@@ -36,7 +55,7 @@ export default async (ctx) => {
};
})
.get();
- ctx.set('data', {
+ return {
// 源标题
title: '西安理工大学人事处-' + dic_title[category],
// 源链接
@@ -64,5 +83,5 @@ export default async (ctx) => {
})
)
),
- });
-};
+ };
+}
diff --git a/lib/routes/xboxfan/news.ts b/lib/routes/xboxfan/news.ts
index 02fda23b0..d3e63dae9 100644
--- a/lib/routes/xboxfan/news.ts
+++ b/lib/routes/xboxfan/news.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseRelativeDate } from '@/utils/parse-date';
import md5 from '@/utils/md5';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news',
+ categories: ['reading'],
+ example: '/xboxfan/news',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['xboxfan.com/'],
+ },
+ name: '资讯',
+ maintainers: ['XXY233'],
+ handler,
+};
+
+async function handler() {
const baseUrl = 'https://xboxfan.com/';
const { data: response } = await got(baseUrl);
const $ = load(response);
@@ -37,9 +59,9 @@ export default async (ctx) => {
return data;
});
- ctx.set('data', {
+ return {
title: '盒心光环·资讯',
link: 'https://xboxfan.com/',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/xiaohongshu/board.ts b/lib/routes/xiaohongshu/board.ts
index ef4c85a18..0a0c2c387 100644
--- a/lib/routes/xiaohongshu/board.ts
+++ b/lib/routes/xiaohongshu/board.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
import { getBoard } from './util';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/board/:board_id',
+ categories: ['new-media'],
+ example: '/xiaohongshu/board/5db6f79200000000020032df',
+ parameters: { board_id: '专辑 ID' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: true,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['xiaohongshu.com/board/:board_id'],
+ },
+ name: '专辑',
+ maintainers: ['lotosbin'],
+ handler,
+};
+
+async function handler(ctx) {
const url = `https://www.xiaohongshu.com/board/${ctx.req.param('board_id')}`;
const main = await getBoard(url, cache);
@@ -21,11 +43,11 @@ export default async (ctx) => {
pubDate: timezone(parseDate(item.time), 8),
}));
- ctx.set('data', {
+ return {
title,
link: url,
image,
item: resultItem,
description,
- });
-};
+ };
+}
diff --git a/lib/routes/xiaohongshu/notes.ts b/lib/routes/xiaohongshu/notes.ts
index 1d9e2093e..03cfc5fdc 100644
--- a/lib/routes/xiaohongshu/notes.ts
+++ b/lib/routes/xiaohongshu/notes.ts
@@ -1,17 +1,29 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import { getNotes, formatText, formatNote } from './util';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/user/:user_id/notes/fulltext',
+ radar: {
+ source: ['xiaohongshu.com/user/profile/:user_id'],
+ target: '/user/:user_id/notes',
+ },
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const userId = ctx.req.param('user_id');
const url = `https://www.xiaohongshu.com/user/profile/${userId}`;
const { user, notes } = await getNotes(url, cache);
- ctx.set('data', {
+ return {
title: `${user.nickname} - 笔记 • 小红书 / RED`,
description: formatText(user.desc),
image: user.imageb || user.images,
link: url,
item: notes.map((item) => formatNote(url, item)),
- });
-};
+ };
+}
diff --git a/lib/routes/xiaohongshu/user.ts b/lib/routes/xiaohongshu/user.ts
index e67ea2dda..dc60ae916 100644
--- a/lib/routes/xiaohongshu/user.ts
+++ b/lib/routes/xiaohongshu/user.ts
@@ -1,7 +1,15 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import { getUser } from './util';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/user/:user_id/:category',
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const userId = ctx.req.param('user_id');
const category = ctx.req.param('category');
const url = `https://www.xiaohongshu.com/user/profile/${userId}`;
@@ -45,11 +53,11 @@ export default async (ctx) => {
}));
};
- ctx.set('data', {
+ return {
title,
description,
image,
link: url,
item: category === 'notes' ? renderNote(notes) : renderCollect(collect),
- });
-};
+ };
+}
diff --git a/lib/routes/xiaomiyoupin/crowdfunding.ts b/lib/routes/xiaomiyoupin/crowdfunding.ts
index 7a01dc9d5..a0b6916d5 100644
--- a/lib/routes/xiaomiyoupin/crowdfunding.ts
+++ b/lib/routes/xiaomiyoupin/crowdfunding.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -6,7 +7,28 @@ import { art } from '@/utils/render';
import * as path from 'node:path';
const base_url = 'https://m.xiaomiyoupin.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/crowdfunding',
+ categories: ['game'],
+ example: '/xiaomiyoupin/crowdfunding',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['xiaomiyoupin.com/'],
+ },
+ name: '小米有品众筹',
+ maintainers: ['bigfei'],
+ handler,
+};
+
+async function handler() {
const resp = await got('https://home.mi.com/lasagne/page/5');
const site_url = resp.data.redirect.location;
@@ -37,10 +59,10 @@ export default async (ctx) => {
pubDate: new Date(goods.fist_release_time * 1000).toUTCString(),
};
});
- ctx.set('data', {
+ return {
title: '小米有品众筹',
link: site_url,
description: '小米有品众筹',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/xiaomiyoupin/latest.ts b/lib/routes/xiaomiyoupin/latest.ts
index 6e63e1025..89fc43d90 100644
--- a/lib/routes/xiaomiyoupin/latest.ts
+++ b/lib/routes/xiaomiyoupin/latest.ts
@@ -1,15 +1,37 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseModule, parseFloorItem } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/latest',
+ categories: ['game'],
+ example: '/xiaomiyoupin/latest',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['xiaomiyoupin.com/'],
+ },
+ name: '小米有品每日上新',
+ maintainers: ['xyqfer', 'DIYgod', 'bigfei'],
+ handler,
+};
+
+async function handler() {
const response = await got('https://m.xiaomiyoupin.com/homepage/main/v1005');
const floors = parseModule(response.data.data.homepage.floors, 'product_hot');
const items = parseFloorItem(floors);
- ctx.set('data', {
+ return {
title: '小米有品每日上新',
link: 'https://m.xiaomiyoupin.com/w/newproduct?pageid=1605',
description: '小米有品每日上新',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/xiaote/index.ts b/lib/routes/xiaote/index.ts
index 5832bf322..6d0a6137a 100644
--- a/lib/routes/xiaote/index.ts
+++ b/lib/routes/xiaote/index.ts
@@ -1,7 +1,29 @@
+import { Route } from '@/types';
import got from '@/utils/got'; // 自订的 got
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news',
+ categories: ['blog'],
+ example: '/xiaote/news',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['xiaote.com/'],
+ },
+ name: '首页帖子',
+ maintainers: ['wxsimon'],
+ handler,
+};
+
+async function handler() {
const { data } = await got.post('https://lcen.xiaote.net//api/graphql/', {
json: {
query: `query($startCursor: Int) {
@@ -22,7 +44,7 @@ export default async (ctx) => {
},
});
- ctx.set('data', {
+ return {
title: '小特社区',
// 源链接
link: 'https://xiaote.com/',
@@ -44,5 +66,5 @@ export default async (ctx) => {
author: item.user.nickname,
};
}),
- });
-};
+ };
+}
diff --git a/lib/routes/xiaoyuzhou/pickup.ts b/lib/routes/xiaoyuzhou/pickup.ts
index 8b2b98df5..5d6ded74b 100644
--- a/lib/routes/xiaoyuzhou/pickup.ts
+++ b/lib/routes/xiaoyuzhou/pickup.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { config } from '@/config';
@@ -78,14 +79,25 @@ const ProcessFeed = async () => {
});
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/',
+ radar: {
+ source: ['xiaoyuzhoufm.com/'],
+ target: '',
+ },
+ name: 'Unknown',
+ maintainers: ['prnake', 'Maecenas'],
+ handler,
+};
+
+async function handler() {
let resultItems = await cache.tryGet(XIAOYUZHOU_ITEMS, () => ProcessFeed());
if (!isToday(resultItems[0].pubDate)) {
// force refresh cache
resultItems = await ProcessFeed();
cache.set(XIAOYUZHOU_ITEMS, resultItems);
}
- ctx.set('data', {
+ return {
title: '小宇宙 - 发现',
link: 'https://www.xiaoyuzhoufm.com/',
description: '小宇宙的编辑精选',
@@ -93,5 +105,5 @@ export default async (ctx) => {
itunes_author: '小宇宙',
itunes_category: 'Society & Culture',
item: resultItems,
- });
-};
+ };
+}
diff --git a/lib/routes/xiaoyuzhou/podcast.ts b/lib/routes/xiaoyuzhou/podcast.ts
index 63e027c8d..20e08bcef 100644
--- a/lib/routes/xiaoyuzhou/podcast.ts
+++ b/lib/routes/xiaoyuzhou/podcast.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/podcast/:id',
+ categories: ['picture'],
+ example: '/xiaoyuzhou/podcast/6021f949a789fca4eff4492c',
+ parameters: { id: '播客id,可以在小宇宙播客的 URL 中找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['xiaoyuzhoufm.com/podcast/:id'],
+ },
+ name: '播客',
+ maintainers: ['hondajojo', 'jtsang4'],
+ handler,
+};
+
+async function handler(ctx) {
const link = `https://www.xiaoyuzhoufm.com/podcast/${ctx.req.param('id')}`;
const response = await got({
method: 'get',
@@ -24,7 +46,7 @@ export default async (ctx) => {
itunes_item_image: (item.image || item.podcast?.image)?.smallPicUrl,
}));
- ctx.set('data', {
+ return {
title: page_data.props.pageProps.podcast.title,
link: `https://www.xiaoyuzhoufm.com/podcast/${page_data.props.pageProps.podcast.pid}`,
itunes_author: page_data.props.pageProps.podcast.author,
@@ -32,5 +54,5 @@ export default async (ctx) => {
image: page_data.props.pageProps.podcast.image.smallPicUrl,
item: episodes,
description: page_data.props.pageProps.podcast.description,
- });
-};
+ };
+}
diff --git a/lib/routes/xiaozhuanlan/column.ts b/lib/routes/xiaozhuanlan/column.ts
index b2563a7ca..96fa200c2 100644
--- a/lib/routes/xiaozhuanlan/column.ts
+++ b/lib/routes/xiaozhuanlan/column.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -8,7 +9,28 @@ const md = MarkdownIt({
});
const baseUrl = 'https://xiaozhuanlan.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/column/:id',
+ categories: ['traditional-media'],
+ example: '/xiaozhuanlan/column/olddriver-selection',
+ parameters: { id: '专栏 ID,可在专栏页 URL 中找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['xiaozhuanlan.com/:id'],
+ },
+ name: '专栏',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const response = await got(`${baseUrl}/${id}`);
@@ -45,10 +67,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('head title').text().trim(),
link: `${baseUrl}/${id}`,
description: $('meta[name=description]').attr('content'),
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/xidian/jwc.ts b/lib/routes/xidian/jwc.ts
index 6665a8137..0487813b8 100644
--- a/lib/routes/xidian/jwc.ts
+++ b/lib/routes/xidian/jwc.ts
@@ -1,10 +1,29 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
const baseUrl = 'https://jwc.xidian.edu.cn';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/jwc/:category?',
+ categories: ['forecast'],
+ example: '/xidian/jwc/tzgg',
+ parameters: { category: '通知类别,默认为通知公告' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '教务处',
+ maintainers: ['ShadowySpirits'],
+ handler,
+};
+
+async function handler(ctx) {
const { category = 'tzgg' } = ctx.req.param();
const url = `${baseUrl}/${category}.htm`;
const response = await got(url, {
@@ -47,9 +66,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('title').text(),
link: url,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/ximalaya/album.ts b/lib/routes/ximalaya/album.ts
index 23106e055..f44f6ec72 100644
--- a/lib/routes/ximalaya/album.ts
+++ b/lib/routes/ximalaya/album.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -80,7 +81,25 @@ function judgeTrue(str, ...validStrings) {
return false;
}
-export default async (ctx) => {
+export const route: Route = {
+ path: ['/:type/:id/:all?', '/:type/:id/:all/:shownote?'],
+ categories: ['picture'],
+ example: '/ximalaya/album/299146',
+ parameters: { type: '专辑类型, 通常可以使用 `album`,可在对应专辑页面的 URL 中找到', id: '专辑 id, 可在对应专辑页面的 URL 中找到', all: '是否需要获取全部节目,填入 `1`、`true`、`all` 视为获取所有节目,填入其他则不获取。' },
+ features: {
+ requireConfig: true,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: true,
+ supportScihub: false,
+ },
+ name: '专辑(不输出 ShowNote)',
+ maintainers: ['lengthmin', 'jjeejj', 'prnake'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type'); // 专辑分类
const id = ctx.req.param('id'); // 专辑id
const shouldAll = judgeTrue(ctx.req.param('all'), 'all');
@@ -209,7 +228,7 @@ export default async (ctx) => {
return resultItem;
});
- ctx.set('data', {
+ return {
title: albumTitle,
link: baseUrl + albumUrl,
description: albumIntro,
@@ -217,5 +236,5 @@ export default async (ctx) => {
itunes_author: author,
itunes_category: categoryDict[albumCategory] || albumCategory,
item: resultItems,
- });
-};
+ };
+}
diff --git a/lib/routes/xinpianchang/index.ts b/lib/routes/xinpianchang/index.ts
index b6e73fd44..d0c11088e 100644
--- a/lib/routes/xinpianchang/index.ts
+++ b/lib/routes/xinpianchang/index.ts
@@ -1,7 +1,26 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import { rootUrl, getData, processItems } from './util';
-export default async (ctx) => {
+export const route: Route = {
+ path: ['/discover/:params?', '/:params?'],
+ categories: ['traditional-media'],
+ example: '/xinpianchang/discover',
+ parameters: { params: '参数,可在对应分类页 URL 中找到,默认为 `article-0-0-all-all-0-0-score` ,即全部' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '发现',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const { params = 'article-0-0-all-all-0-0-score' } = ctx.req.param();
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 60;
@@ -13,8 +32,8 @@ export default async (ctx) => {
items = await processItems(items.slice(0, limit), cache.tryGet);
- ctx.set('data', {
+ return {
...data,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/xinpianchang/rank.ts b/lib/routes/xinpianchang/rank.ts
index ba4575c6e..f78111b9e 100644
--- a/lib/routes/xinpianchang/rank.ts
+++ b/lib/routes/xinpianchang/rank.ts
@@ -1,9 +1,28 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { rootUrl, getData, processItems } from './util';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/rank/:category?',
+ categories: ['traditional-media'],
+ example: '/xinpianchang/rank',
+ parameters: { category: '分类 id,可在对应排行榜页 URL 中找到,见下表,默认为 `all` ,即总榜' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '排行榜',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const { category = 'all' } = ctx.req.param();
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 60;
@@ -27,8 +46,8 @@ export default async (ctx) => {
items = await processItems(items.slice(0, limit), cache.tryGet);
- ctx.set('data', {
+ return {
...data,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/xjtu/2yuan/news.ts b/lib/routes/xjtu/2yuan/news.ts
index 3608e6fe6..76ff2da84 100644
--- a/lib/routes/xjtu/2yuan/news.ts
+++ b/lib/routes/xjtu/2yuan/news.ts
@@ -1,10 +1,29 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/2yuan/news/:id?',
+ categories: ['forecast'],
+ example: '/xjtu/2yuan/news',
+ parameters: { id: '编号,见下表,默认为通知公告' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '第二附属医院新闻',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id') ?? '110';
const rootUrl = 'http://2yuan.xjtu.edu.cn';
@@ -54,9 +73,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('title').text(),
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/xjtu/dean.ts b/lib/routes/xjtu/dean.ts
index 68a160465..f1bb553b2 100644
--- a/lib/routes/xjtu/dean.ts
+++ b/lib/routes/xjtu/dean.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -29,7 +30,14 @@ const parseContent = (htmlString) => {
};
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/dean/:subpath{.+}',
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const subpath = ctx.req.param('subpath');
const url = `http://dean.xjtu.edu.cn/${subpath.replaceAll('.htm', '')}.htm`;
@@ -76,9 +84,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `西安交大教务处 - ${subname}`,
link: url,
item: out.filter((item) => item !== ''),
- });
-};
+ };
+}
diff --git a/lib/routes/xjtu/dyyy/index.ts b/lib/routes/xjtu/dyyy/index.ts
index 8855117d7..ea21e6215 100644
--- a/lib/routes/xjtu/dyyy/index.ts
+++ b/lib/routes/xjtu/dyyy/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -5,7 +6,14 @@ import { parseDate } from '@/utils/parse-date';
const baseUrl = 'http://www.dyyy.xjtu.edu.cn';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/dyyy/:path{.+}',
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const path = ctx.req.param('path');
const response = await got(`${baseUrl}/${path}.htm`);
@@ -37,9 +45,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('head title').text(),
link: `${baseUrl}/${path}.htm`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/xjtu/ee.ts b/lib/routes/xjtu/ee.ts
index c20c4068a..b3beba6d3 100644
--- a/lib/routes/xjtu/ee.ts
+++ b/lib/routes/xjtu/ee.ts
@@ -1,10 +1,32 @@
+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';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/ee/:id?',
+ categories: ['forecast'],
+ example: '/xjtu/ee/1114',
+ parameters: { id: '栏目id,默认请求`1124`,可在 URL 中找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['ee.xjtu.edu.cn/'],
+ },
+ name: '电气学院',
+ maintainers: ['DylanXie123'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id') ?? '1124';
const rootUrl = `http://ee.xjtu.edu.cn/list.jsp?urltype=tree.TreeTempUrl&wbtreeid=${id}`;
const baseUrl = 'http://ee.xjtu.edu.cn';
@@ -27,7 +49,7 @@ export default async (ctx) => {
})
.get();
- ctx.set('data', {
+ return {
title: `西安交通大学电气学院 - ${feed_title}`,
link: baseUrl,
item: await Promise.all(
@@ -41,5 +63,5 @@ export default async (ctx) => {
})
)
),
- });
-};
+ };
+}
diff --git a/lib/routes/xjtu/gs/tzgg.ts b/lib/routes/xjtu/gs/tzgg.ts
index a7708c010..95b67a3c0 100644
--- a/lib/routes/xjtu/gs/tzgg.ts
+++ b/lib/routes/xjtu/gs/tzgg.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/gs/tzgg',
+ categories: ['forecast'],
+ example: '/xjtu/gs/tzgg',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['gs.xjtu.edu.cn/'],
+ },
+ name: '研究生院通知公告',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler() {
const rootUrl = 'http://gs.xjtu.edu.cn/tzgg.htm';
const response = await got({
method: 'get',
@@ -23,7 +45,7 @@ export default async (ctx) => {
})
.get();
- ctx.set('data', {
+ return {
title: '西安交通大学研究生院 - 通知公告',
link: rootUrl,
item: await Promise.all(
@@ -36,5 +58,5 @@ export default async (ctx) => {
})
)
),
- });
-};
+ };
+}
diff --git a/lib/routes/xjtu/international.ts b/lib/routes/xjtu/international.ts
index d248c874a..449cc4b98 100644
--- a/lib/routes/xjtu/international.ts
+++ b/lib/routes/xjtu/international.ts
@@ -1,9 +1,17 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/international/:subpath{.+}',
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const subpath = ctx.req.param('subpath');
const base = 'http://international.xjtu.edu.cn';
@@ -43,10 +51,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `西安交通大学国际处 - ${name}`,
link: url,
description: `西安交通大学国际处 - ${name}`,
item,
- });
-};
+ };
+}
diff --git a/lib/routes/xjtu/job.ts b/lib/routes/xjtu/job.ts
index 9b66b0f2a..9ccd953ec 100644
--- a/lib/routes/xjtu/job.ts
+++ b/lib/routes/xjtu/job.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -18,7 +19,25 @@ const arr = {
jysx: '就业实习',
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/job/:subpath?',
+ categories: ['forecast'],
+ example: '/xjtu/job/zxgg',
+ parameters: { subpath: '栏目类型,默认请求`zxgg`,详见下方表格' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '就业创业中心',
+ maintainers: ['DylanXie123'],
+ handler,
+};
+
+async function handler(ctx) {
const subpath = ctx.req.param('subpath') ?? 'zxgg';
const getTzgg = await got.post(`${baseUrl}/xsfw/sys/jyxtgktapp/modules/jywzManage/getTzgg.do`, {
form: {
@@ -77,9 +96,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `西安交通大学学生就业创业信息网 - ${arr[subpath]}`,
link: baseUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/xjtu/std.ts b/lib/routes/xjtu/std.ts
index a9303bd97..6daf8de99 100644
--- a/lib/routes/xjtu/std.ts
+++ b/lib/routes/xjtu/std.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -9,7 +10,25 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/std/:category?',
+ categories: ['forecast'],
+ example: '/xjtu/std/zytz',
+ parameters: { category: '分类,见下表,默认为通知公告' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '科技在线',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? '';
const rootUrl = 'http://std.xjtu.edu.cn';
@@ -54,9 +73,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('title').text(),
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/xkb/index.ts b/lib/routes/xkb/index.ts
index f5a86fa83..55a25e6c0 100644
--- a/lib/routes/xkb/index.ts
+++ b/lib/routes/xkb/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -8,7 +9,25 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:channel',
+ categories: ['bbs'],
+ example: '/xkb/350',
+ parameters: { channel: '栏目 ID,点击对应栏目后在地址栏找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '新闻',
+ maintainers: ['TimWu007'],
+ handler,
+};
+
+async function handler(ctx) {
const channel = ctx.req.param('channel') ?? 350;
const currentUrl = `https://www.xkb.com.cn/xkbapp/fundapi/article/api/articles?chnlId=${channel}&visibility=1&page=0&size=20&keyword=`;
@@ -50,9 +69,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `新快报新快网 - ${chnlName}`,
link: `https://www.xkb.com.cn/home?id=${channel}`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/xmanhua/index.ts b/lib/routes/xmanhua/index.ts
index e02f4d65a..90f91b9bc 100644
--- a/lib/routes/xmanhua/index.ts
+++ b/lib/routes/xmanhua/index.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:uid',
+ categories: ['program-update'],
+ example: '/xmanhua/73xm',
+ parameters: { uid: '漫画 id,在浏览器中可见,例如鬼灭之刃对应的 id 为 `73xm`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['xmanhua.com/:uid'],
+ },
+ name: '最新动态',
+ maintainers: ['Ye11'],
+ handler,
+};
+
+async function handler(ctx) {
const uid = ctx.req.param('uid');
const host = 'https://xmanhua.com';
const url = `https://xmanhua.com/${uid}/`;
@@ -54,11 +76,11 @@ export default async (ctx) => {
.get();
const name = $('body > div.detail-info-1 > div > div > p.detail-info-title').text();
const description_ = finished ? '已完结' : '连载中';
- ctx.set('data', {
+ return {
title: `x漫画 ${name}`,
link: `https://xmanhua.com/${uid}`,
description: description_,
allowEmpty: true,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/xmnn/epaper.ts b/lib/routes/xmnn/epaper.ts
index 5c75893d8..4bdbf7cd6 100644
--- a/lib/routes/xmnn/epaper.ts
+++ b/lib/routes/xmnn/epaper.ts
@@ -1,10 +1,33 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/epaper/:id?',
+ categories: ['bbs'],
+ example: '/xmnn/epaper/xmrb',
+ parameters: { id: '报纸 id,见下表,默认为 `xmrb`,即厦门日报' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['epaper.xmnn.cn/:id'],
+ target: '/epaper/:id',
+ },
+ name: '数字媒体',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id') ?? 'xmrb';
const rootUrl = 'https://epaper.xmnn.cn';
@@ -78,9 +101,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title,
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/xmnn/news.ts b/lib/routes/xmnn/news.ts
index 6dbeff90e..018a78812 100644
--- a/lib/routes/xmnn/news.ts
+++ b/lib/routes/xmnn/news.ts
@@ -1,10 +1,18 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/:category{.+}?',
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const { category = 'xmxw' } = ctx.req.param();
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30;
@@ -52,7 +60,7 @@ export default async (ctx) => {
const title = $('title').text();
const icon = new URL($('link[rel="icon"]').prop('href'), rootUrl).href;
- ctx.set('data', {
+ return {
item: items,
title,
link: currentUrl,
@@ -62,5 +70,5 @@ export default async (ctx) => {
logo: icon,
subtitle: $('div.h').text(),
author: title.split(/_/).pop(),
- });
-};
+ };
+}
diff --git a/lib/routes/xmut/jwc/bkjw.ts b/lib/routes/xmut/jwc/bkjw.ts
index 2aae4fbcf..06b55e198 100644
--- a/lib/routes/xmut/jwc/bkjw.ts
+++ b/lib/routes/xmut/jwc/bkjw.ts
@@ -1,10 +1,18 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
const xmut = 'https://jwc.xmut.edu.cn';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/jwc/bkjw/:category?',
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const { category = 'jwxt' } = ctx.req.param();
const url = `${xmut}/index/tzgg/${category}.htm`;
const res = await got(url, {
@@ -55,9 +63,9 @@ export default async (ctx) => {
})
)
);
- ctx.set('data', {
+ return {
title: $('title').text(),
link: url,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/xmut/jwc/yjs.ts b/lib/routes/xmut/jwc/yjs.ts
index 9a6d054de..2dc81d257 100644
--- a/lib/routes/xmut/jwc/yjs.ts
+++ b/lib/routes/xmut/jwc/yjs.ts
@@ -1,10 +1,18 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
const xmut = 'https://yjs.xmut.edu.cn';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/jwc/yjjw/:category?',
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const { category = 'yjsc' } = ctx.req.param();
const url = `${xmut}/index/${category}.htm`;
const res = await got(url, {
@@ -44,9 +52,9 @@ export default async (ctx) => {
})
)
);
- ctx.set('data', {
+ return {
title: $('title').text(),
link: url,
item: itemPromises,
- });
-};
+ };
+}
diff --git a/lib/routes/xsijishe/forum.ts b/lib/routes/xsijishe/forum.ts
index a4d8a44ad..af1b8ab05 100644
--- a/lib/routes/xsijishe/forum.ts
+++ b/lib/routes/xsijishe/forum.ts
@@ -1,10 +1,29 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
const baseUrl = 'https://xsijishe.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/forum/:fid',
+ categories: ['blog'],
+ example: '/xsijishe/forum/51',
+ parameters: { fid: '子论坛 id' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '论坛',
+ maintainers: ['akynazh'],
+ handler,
+};
+
+async function handler(ctx) {
const fid = ctx.req.param('fid');
const url = `${baseUrl}/forum-${fid}-1.html`;
const resp = await got(url);
@@ -48,10 +67,10 @@ export default async (ctx) => {
})
)
);
- ctx.set('data', {
+ return {
title: `司机社${forumCategory}论坛`,
link: url,
description: `司机社${forumCategory}论坛`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/xsijishe/rank.ts b/lib/routes/xsijishe/rank.ts
index c486e78bb..895e93b31 100644
--- a/lib/routes/xsijishe/rank.ts
+++ b/lib/routes/xsijishe/rank.ts
@@ -1,9 +1,28 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
const baseUrl = 'https://xsijishe.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/rank/:type',
+ categories: ['blog'],
+ example: '/xsijishe/rank/weekly',
+ parameters: { type: '排行榜类型: weekly | monthly' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '排行榜',
+ maintainers: ['akynazh'],
+ handler,
+};
+
+async function handler(ctx) {
const rankType = ctx.req.param('type');
let title;
let rankId;
@@ -52,10 +71,10 @@ export default async (ctx) => {
})
)
);
- ctx.set('data', {
+ return {
title,
link: url,
description: title,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/xueqiu/column.ts b/lib/routes/xueqiu/column.ts
index ad94dc2e8..1ef70611f 100644
--- a/lib/routes/xueqiu/column.ts
+++ b/lib/routes/xueqiu/column.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { JSDOM } from 'jsdom';
import { CookieJar } from 'tough-cookie';
@@ -5,7 +6,28 @@ import { parseDate } from '@/utils/parse-date';
const cookieJar = new CookieJar();
const baseUrl = 'https://xueqiu.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/column/:id',
+ categories: ['other'],
+ example: '/xueqiu/column/9962554712',
+ parameters: { id: '用户 id, 可在用户主页 URL 中找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['xueqiu.com/:id/column'],
+ },
+ name: '用户专栏',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const pageUrl = `${baseUrl}/${id}/column`;
@@ -33,10 +55,10 @@ export default async (ctx) => {
author: SNOWMAN_TARGET.screen_name,
}));
- ctx.set('data', {
+ return {
title: `${SNOWMAN_TARGET.screen_name} - 雪球`,
link: pageUrl,
description: SNOWMAN_TARGET.description,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/xueqiu/favorite.ts b/lib/routes/xueqiu/favorite.ts
index 67c8c2cb0..bdcab53f1 100644
--- a/lib/routes/xueqiu/favorite.ts
+++ b/lib/routes/xueqiu/favorite.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import queryString from 'query-string';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/favorite/:id',
+ categories: ['other'],
+ example: '/xueqiu/favorite/8152922548',
+ parameters: { id: '用户 id, 可在用户主页 URL 中找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['xueqiu.com/u/:id'],
+ },
+ name: '用户收藏动态',
+ maintainers: ['imlonghao'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const res1 = await got({
@@ -24,7 +46,7 @@ export default async (ctx) => {
});
const data = res2.data.list;
- ctx.set('data', {
+ return {
title: `ID: ${id} 的雪球收藏动态`,
link: `https://xueqiu.com/u/${id}`,
description: `ID: ${id} 的雪球收藏动态`,
@@ -34,5 +56,5 @@ export default async (ctx) => {
pubDate: parseDate(item.created_at),
link: `https://xueqiu.com${item.target}`,
})),
- });
-};
+ };
+}
diff --git a/lib/routes/xueqiu/fund.ts b/lib/routes/xueqiu/fund.ts
index 7ec01d5f1..052112c11 100644
--- a/lib/routes/xueqiu/fund.ts
+++ b/lib/routes/xueqiu/fund.ts
@@ -1,8 +1,27 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/fund/:id',
+ categories: ['other'],
+ example: '/xueqiu/fund/040008',
+ parameters: { id: '基金代码, 可在基金主页 URL 中找到. 此路由的数据为场外基金 (`F`开头)' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '蛋卷基金净值更新',
+ maintainers: ['HenryQW', 'NathanDai'],
+ handler,
+};
+
+async function handler(ctx) {
const guid = ctx.req.param('id');
const appUrl = `https://danjuanapp.com/funding/${guid}`;
const url = `https://danjuanapp.com/djapi/fund/${guid}`;
@@ -41,10 +60,10 @@ export default async (ctx) => {
link: appUrl,
};
- ctx.set('data', {
+ return {
title: fd_name,
link: appUrl,
description,
item: [single],
- });
-};
+ };
+}
diff --git a/lib/routes/xueqiu/hots.ts b/lib/routes/xueqiu/hots.ts
index 788fa2d8c..7b1e36fc0 100644
--- a/lib/routes/xueqiu/hots.ts
+++ b/lib/routes/xueqiu/hots.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import queryString from 'query-string';
import { parseDate } from '@/utils/parse-date';
import sanitizeHtml from 'sanitize-html';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/hots',
+ categories: ['other'],
+ example: '/xueqiu/hots',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['xueqiu.com/'],
+ },
+ name: '热帖',
+ maintainers: ['hillerliao'],
+ handler,
+};
+
+async function handler() {
const res1 = await got({
method: 'get',
url: 'https://xueqiu.com/',
@@ -28,7 +50,7 @@ export default async (ctx) => {
});
const data = res2.data;
- ctx.set('data', {
+ return {
title: `热帖 - 雪球`,
link: `https://xueqiu.com/`,
description: `雪球热门帖子`,
@@ -42,5 +64,5 @@ export default async (ctx) => {
author: item.user.screen_name,
};
}),
- });
-};
+ };
+}
diff --git a/lib/routes/xueqiu/snb.ts b/lib/routes/xueqiu/snb.ts
index d0ebbb309..b3aaf1dfb 100644
--- a/lib/routes/xueqiu/snb.ts
+++ b/lib/routes/xueqiu/snb.ts
@@ -1,10 +1,32 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import randUserAgent from '@/utils/rand-user-agent';
const UA = randUserAgent({ browser: 'chrome', os: 'android', device: 'mobile' });
-export default async (ctx) => {
+export const route: Route = {
+ path: '/snb/:id',
+ categories: ['other'],
+ example: '/xueqiu/snb/ZH1288184',
+ parameters: { id: '组合代码, 可在组合主页 URL 中找到.' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['xueqiu.com/P/:id', 'xueqiu.com/p/:id'],
+ },
+ name: '组合最新调仓信息',
+ maintainers: ['ZhishanZhang'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const url = 'https://xueqiu.com/p/' + id;
@@ -38,10 +60,10 @@ export default async (ctx) => {
guid: `xueqiu::snb::${id}::${time}`,
};
- ctx.set('data', {
+ return {
title: snb_title,
link: url,
description: snb_description,
item: [single],
- });
-};
+ };
+}
diff --git a/lib/routes/xueqiu/stock-comments.ts b/lib/routes/xueqiu/stock-comments.ts
index b0981723f..6e8dc2db1 100644
--- a/lib/routes/xueqiu/stock-comments.ts
+++ b/lib/routes/xueqiu/stock-comments.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -9,7 +10,28 @@ import * as path from 'node:path';
import { parseDate } from '@/utils/parse-date';
import sanitizeHtml from 'sanitize-html';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/stock_comments/:id',
+ categories: ['other'],
+ example: '/xueqiu/stock_comments/SZ002626',
+ parameters: { id: '股票代码(需要带上交易所)' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['xueqiu.com/S/:id'],
+ },
+ name: '股票评论',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const res = await got({
@@ -28,7 +50,7 @@ export default async (ctx) => {
});
const data = res.data.list;
- ctx.set('data', {
+ return {
title: `${id} ${stock_name} - 评论`,
link: `https://xueqiu.com/S/${id}`,
description: `${stock_name} - 评论`,
@@ -45,5 +67,5 @@ export default async (ctx) => {
link,
};
}),
- });
-};
+ };
+}
diff --git a/lib/routes/xueqiu/stock-info.ts b/lib/routes/xueqiu/stock-info.ts
index b1f618a28..f89ee8602 100644
--- a/lib/routes/xueqiu/stock-info.ts
+++ b/lib/routes/xueqiu/stock-info.ts
@@ -1,10 +1,33 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import queryString from 'query-string';
import { parseDate } from '@/utils/parse-date';
import sanitizeHtml from 'sanitize-html';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/stock_info/:id/:type?',
+ categories: ['other'],
+ example: '/xueqiu/stock_info/SZ000002',
+ parameters: { id: '股票代码(需要带上交易所)', type: '动态的类型, 不填则为股票公告' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['xueqiu.com/S/:id', 'xueqiu.com/s/:id'],
+ target: '/stock_info/:id',
+ },
+ name: '股票信息',
+ maintainers: ['YuYang'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const type = ctx.req.param('type') || 'announcement';
const count = 10;
@@ -49,7 +72,7 @@ export default async (ctx) => {
});
const data = res2.data.list;
- ctx.set('data', {
+ return {
title: `${id} ${stock_name} - ${source}`,
link: `https://xueqiu.com/S/${id}`,
description: `${stock_name} - ${source}`,
@@ -65,5 +88,5 @@ export default async (ctx) => {
link,
};
}),
- });
-};
+ };
+}
diff --git a/lib/routes/xueqiu/today.ts b/lib/routes/xueqiu/today.ts
index 0ab616124..96f0938db 100644
--- a/lib/routes/xueqiu/today.ts
+++ b/lib/routes/xueqiu/today.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/today',
+ categories: ['other'],
+ example: '/xueqiu/today',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['xueqiu.com/today'],
+ },
+ name: '今日话题',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const size = ctx.req.query('limit') ?? '20';
const rootUrl = 'https://xueqiu.com';
@@ -58,9 +80,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: '今日话题 - 雪球',
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/xueqiu/user-stock.ts b/lib/routes/xueqiu/user-stock.ts
index e3e4c69d8..0cc0afc5a 100644
--- a/lib/routes/xueqiu/user-stock.ts
+++ b/lib/routes/xueqiu/user-stock.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import queryString from 'query-string';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/user_stock/:id',
+ categories: ['other'],
+ example: '/xueqiu/user_stock/1247347556',
+ parameters: { id: '用户 id, 可在用户主页 URL 中找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['xueqiu.com/u/:id'],
+ },
+ name: '用户自选动态',
+ maintainers: ['hillerliao'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const res1 = await got({
@@ -34,7 +56,7 @@ export default async (ctx) => {
});
const screen_name = res3.data.user.screen_name;
- ctx.set('data', {
+ return {
title: `${screen_name} 的雪球自选动态`,
link: `https://xueqiu.com/u/${id}`,
description: `@${screen_name} 的雪球自选动态`,
@@ -44,5 +66,5 @@ export default async (ctx) => {
pubDate: parseDate(item.created),
link: `https://xueqiu.com/s/${item.symbol}`,
})),
- });
-};
+ };
+}
diff --git a/lib/routes/xueqiu/user.ts b/lib/routes/xueqiu/user.ts
index 23a421f08..c01b9de84 100644
--- a/lib/routes/xueqiu/user.ts
+++ b/lib/routes/xueqiu/user.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import queryString from 'query-string';
@@ -6,7 +7,29 @@ import sanitizeHtml from 'sanitize-html';
const rootUrl = 'https://xueqiu.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/user/:id/:type?',
+ categories: ['other'],
+ example: '/xueqiu/user/8152922548',
+ parameters: { id: '用户 id, 可在用户主页 URL 中找到', type: '动态的类型, 不填则默认全部' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['xueqiu.com/u/:id'],
+ target: '/user/:id',
+ },
+ name: '用户动态',
+ maintainers: ['imlonghao'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const type = ctx.req.param('type') || 10;
const source = type === '11' ? '买卖' : '';
@@ -68,10 +91,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `${data[0].user.screen_name} 的雪球${typename[type]}动态`,
link: `${rootUrl}/u/${id}`,
description: `${data[0].user.screen_name} 的雪球${typename[type]}动态`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/xunhupay/index.ts b/lib/routes/xunhupay/index.ts
index ecbc10c67..731e2f942 100644
--- a/lib/routes/xunhupay/index.ts
+++ b/lib/routes/xunhupay/index.ts
@@ -1,7 +1,29 @@
+import { Route } from '@/types';
import buildData from '@/utils/common-config';
const baseUrl = 'https://www.xunhupay.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/blog',
+ categories: ['programming'],
+ example: '/xunhupay/blog',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.xunhupay.com/blog'],
+ },
+ name: '文章',
+ maintainers: ['Joey'],
+ handler,
+};
+
+async function handler(ctx) {
const link = `${baseUrl}/blog.html`;
ctx.set(
'data',
@@ -24,4 +46,4 @@ export default async (ctx) => {
},
})
);
-};
+}
diff --git a/lib/routes/xys/new.ts b/lib/routes/xys/new.ts
index 5878bec95..57757d04c 100644
--- a/lib/routes/xys/new.ts
+++ b/lib/routes/xys/new.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -9,7 +10,28 @@ import * as path from 'node:path';
import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/new',
+ categories: ['programming'],
+ example: '/xys/new',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['xys.org/', 'xys.org/new.html'],
+ },
+ name: '新到资料',
+ maintainers: ['wenzhenl'],
+ handler,
+};
+
+async function handler(ctx) {
const rootUrl = 'http://www.xys.org';
const currentUrl = `${rootUrl}/new.html`;
@@ -70,9 +92,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: '新语丝 - 新到资料',
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/xyzrank/index.ts b/lib/routes/xyzrank/index.ts
index 4aa8d8c4e..dc96bbf78 100644
--- a/lib/routes/xyzrank/index.ts
+++ b/lib/routes/xyzrank/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -7,7 +8,18 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:category?',
+ radar: {
+ source: ['xyzrank.com/'],
+ target: '',
+ },
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? '';
const rootUrl = 'https://xyzrank.com';
@@ -105,10 +117,10 @@ export default async (ctx) => {
}),
}));
- ctx.set('data', {
+ return {
title: `${$('title').text()} - ${categories[category].title}`,
link: currentUrl,
item: items,
description: $('meta[property="og:description"]').attr('content'),
- });
-};
+ };
+}
diff --git a/lib/routes/yahoo/news/tw/index.ts b/lib/routes/yahoo/news/tw/index.ts
index 8f9bf4539..f6619f5fe 100644
--- a/lib/routes/yahoo/news/tw/index.ts
+++ b/lib/routes/yahoo/news/tw/index.ts
@@ -1,7 +1,29 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import { getArchive, getCategories, parseList, parseItem } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/:region/:category?',
+ categories: ['traditional-media'],
+ example: '/yahoo/news/hk/world',
+ parameters: { region: 'Region, see the table below', category: 'Category, see the table below' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['yahoo.com/'],
+ },
+ name: 'News',
+ maintainers: ['KeiLongW'],
+ handler,
+};
+
+async function handler(ctx) {
const { region, category } = ctx.req.param();
if (!['hk', 'tw'].includes(region)) {
throw new Error(`Unknown region: ${region}`);
@@ -16,10 +38,10 @@ export default async (ctx) => {
const items = await Promise.all(list.map((item) => parseItem(item, cache.tryGet)));
- ctx.set('data', {
+ return {
title: `Yahoo 新聞 - ${category ? categoryMap[category].name : '所有類別'}`,
link: `https://${region}.news.yahoo.com/${category ? `${category}/` : ''}archive`,
image: 'https://s.yimg.com/cv/apiv2/social/images/yahoo_default_logo-1200x1200.png',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/yahoo/news/tw/provider-helper.ts b/lib/routes/yahoo/news/tw/provider-helper.ts
index ad6df37f9..0bf8436fe 100644
--- a/lib/routes/yahoo/news/tw/provider-helper.ts
+++ b/lib/routes/yahoo/news/tw/provider-helper.ts
@@ -1,7 +1,26 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import { getProviderList } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/providers/:region',
+ categories: ['traditional-media'],
+ example: '/yahoo/news/providers/tw',
+ parameters: { region: '地區,見上表' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '新聞來源列表',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const region = ctx.req.param('region');
if (!['hk', 'tw'].includes(region)) {
throw new Error(`Unknown region: ${region}`);
@@ -14,10 +33,10 @@ export default async (ctx) => {
description: provider.key,
}));
- ctx.set('data', {
+ return {
title: 'Yahoo 新聞 - 新聞來源列表',
link: `https://${region}.news.yahoo.com`,
image: 'https://s.yimg.com/cv/apiv2/social/images/yahoo_default_logo-1200x1200.png',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/yahoo/news/tw/provider.ts b/lib/routes/yahoo/news/tw/provider.ts
index a01c52bf5..da562b3c1 100644
--- a/lib/routes/yahoo/news/tw/provider.ts
+++ b/lib/routes/yahoo/news/tw/provider.ts
@@ -1,7 +1,26 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import { getArchive, getProviderList, parseList, parseItem } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/provider/:region/:providerId',
+ categories: ['traditional-media'],
+ example: '/yahoo/news/provider/tw/udn.com',
+ parameters: { region: '地區,見下表', providerId: '新聞來源 ID,可透過下方新聞來源列表獲得' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '新聞來源',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const { region, providerId } = ctx.req.param();
if (!['hk', 'tw'].includes(region)) {
throw new Error(`Unknown region: ${region}`);
@@ -16,10 +35,10 @@ export default async (ctx) => {
const items = await Promise.all(list.map((item) => parseItem(item, cache.tryGet)));
- ctx.set('data', {
+ return {
title: `Yahoo 新聞 - ${provider?.title ?? ''}`,
link: provider?.link ?? `https://${region}.news.yahoo.com`,
image: 'https://s.yimg.com/cv/apiv2/social/images/yahoo_default_logo-1200x1200.png',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/yahoo/news/us/index.ts b/lib/routes/yahoo/news/us/index.ts
index cc38da57c..9d8e56847 100644
--- a/lib/routes/yahoo/news/us/index.ts
+++ b/lib/routes/yahoo/news/us/index.ts
@@ -1,10 +1,18 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import parser from '@/utils/rss-parser';
import { load } from 'cheerio';
import { isValidHost } from '@/utils/valid-host';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/en/:category?',
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const region = ctx.req.param('region') === 'en' ? '' : ctx.req.param('region').toLowerCase();
if (!isValidHost(region) && region !== '') {
throw new Error('Invalid region');
@@ -37,10 +45,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: feed.title,
link: feed.link,
description: feed.description,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/yangtzeu/dongke.ts b/lib/routes/yangtzeu/dongke.ts
index 7cd6b3517..ee287cf1e 100644
--- a/lib/routes/yangtzeu/dongke.ts
+++ b/lib/routes/yangtzeu/dongke.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getSubPath } from '@/utils/common-utils';
import cache from '@/utils/cache';
import got from '@/utils/got';
@@ -5,7 +6,14 @@ import { load } from 'cheerio';
import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/dongke/*',
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 10;
const rootUrl = 'https://dongke.yangtzeu.edu.cn';
@@ -51,12 +59,12 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
item: items,
title: $('title').text(),
link: currentUrl,
language: 'zh-cn',
image: new URL($('#head-img a img').prop('src'), rootUrl).href,
author: '长江大学动物科学学院',
- });
-};
+ };
+}
diff --git a/lib/routes/ycwb/index.ts b/lib/routes/ycwb/index.ts
index 27a69ce7d..b0ca39917 100644
--- a/lib/routes/ycwb/index.ts
+++ b/lib/routes/ycwb/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -9,7 +10,25 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:node',
+ categories: ['bbs'],
+ example: '/ycwb/1',
+ parameters: { node: '栏目 id' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '新闻',
+ maintainers: ['TimWu007'],
+ handler,
+};
+
+async function handler(ctx) {
const node = ctx.req.param('node') ?? 1;
const currentUrl = `https://6api.ycwb.com/app_if/jy/getArticles?nodeid=${node}&pagesize=15`;
@@ -78,9 +97,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `羊城晚报金羊网 - ${nodeName}`,
link: String(nodeLink === '' ? 'https://www.ycwb.com/' : nodeLink),
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/yicai/author.ts b/lib/routes/yicai/author.ts
index 3d6c970a7..0a63501bc 100644
--- a/lib/routes/yicai/author.ts
+++ b/lib/routes/yicai/author.ts
@@ -1,10 +1,33 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { rootUrl, ProcessItems } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/author/:id?',
+ categories: ['bbs'],
+ example: '/yicai/author/100005663',
+ parameters: { id: '作者 id,可在对应作者页中找到,默认为第一财经研究院' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['yicai.com/author/:id', 'yicai.com/author'],
+ target: '/author/:id',
+ },
+ name: '一财号',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id') ?? '100005663';
const currentUrl = `${rootUrl}/author/${id}.html`;
@@ -19,9 +42,9 @@ export default async (ctx) => {
const items = await ProcessItems(apiUrl, cache.tryGet);
- ctx.set('data', {
+ return {
title: `第一财经一财号 - ${$('title').text()}`,
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/yicai/brief.ts b/lib/routes/yicai/brief.ts
index c5d9adda4..613af8aeb 100644
--- a/lib/routes/yicai/brief.ts
+++ b/lib/routes/yicai/brief.ts
@@ -1,10 +1,32 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
import { rootUrl } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/brief',
+ categories: ['bbs'],
+ example: '/yicai/brief',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['yicai.com/brief'],
+ },
+ name: '正在',
+ maintainers: ['sanmmm', 'nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const currentUrl = `${rootUrl}/brief`;
const apiUrl = `${rootUrl}/api/ajax/getbrieflist?type=0&page=1&pagesize=${ctx.req.query('limit') ?? 50}`;
@@ -20,9 +42,9 @@ export default async (ctx) => {
pubDate: timezone(parseDate(`${item.datekey} ${item.hm}`, 'YYYY.MM.DD HH:mm'), +8),
}));
- ctx.set('data', {
+ return {
title: '第一财经 - 正在',
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/yicai/dt.ts b/lib/routes/yicai/dt.ts
index c740dbd7a..533d7abd9 100644
--- a/lib/routes/yicai/dt.ts
+++ b/lib/routes/yicai/dt.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -14,7 +15,25 @@ const columns = {
visualization: 4,
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/dt/:column?/:category?',
+ categories: ['bbs'],
+ example: '/yicai/dt/article',
+ parameters: { column: '栏目,见下表,默认为文章', category: '分类,见下表,默认为全部' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'DT 财经',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const { column = 'article', category = '0' } = ctx.req.param();
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30;
@@ -98,7 +117,7 @@ export default async (ctx) => {
const image = $('div.logo a img').prop('src');
const icon = new URL($('link[rel="shortcut icon"]').prop('href'), rootUrl).href;
- ctx.set('data', {
+ return {
item: items,
title: `${$(`a[data-cid="${category}"]`).text()}${title}`,
link: currentUrl,
@@ -110,5 +129,5 @@ export default async (ctx) => {
subtitle: $('meta[name="description"]').prop('content'),
author: title.split(/_/).pop(),
allowEmpty: true,
- });
-};
+ };
+}
diff --git a/lib/routes/yicai/feed.ts b/lib/routes/yicai/feed.ts
index 44fc27c36..4819f39ac 100644
--- a/lib/routes/yicai/feed.ts
+++ b/lib/routes/yicai/feed.ts
@@ -1,10 +1,33 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { rootUrl, ProcessItems } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/feed/:id?',
+ categories: ['bbs'],
+ example: '/yicai/feed/669',
+ parameters: { id: '主题 id,可在对应主题页中找到,默认为一财早报' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['yicai.com/feed/:id', 'yicai.com/feed'],
+ target: '/feed/:id',
+ },
+ name: '关注',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id') ?? '669';
const currentUrl = `${rootUrl}/feed/${id}`;
@@ -19,9 +42,9 @@ export default async (ctx) => {
const items = await ProcessItems(apiUrl, cache.tryGet);
- ctx.set('data', {
+ return {
title: `第一财经主题 - ${$('title').text()}`,
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/yicai/headline.ts b/lib/routes/yicai/headline.ts
index 06da83410..ddf1fa00a 100644
--- a/lib/routes/yicai/headline.ts
+++ b/lib/routes/yicai/headline.ts
@@ -1,14 +1,36 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import { rootUrl, ProcessItems } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/headline',
+ categories: ['bbs'],
+ example: '/yicai/headline',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['yicai.com/'],
+ },
+ name: '头条',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const apiUrl = `${rootUrl}/api/ajax/getlistbycid?cid=48&type=1&page=1&pagesize=${ctx.req.query('limit') ?? 30}`;
const items = await ProcessItems(apiUrl, cache.tryGet);
- ctx.set('data', {
+ return {
title: '第一财经 - 头条',
link: rootUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/yicai/latest.ts b/lib/routes/yicai/latest.ts
index 9ccbc9694..61c8e158f 100644
--- a/lib/routes/yicai/latest.ts
+++ b/lib/routes/yicai/latest.ts
@@ -1,14 +1,36 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import { rootUrl, ProcessItems } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/latest',
+ categories: ['bbs'],
+ example: '/yicai/latest',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['yicai.com/'],
+ },
+ name: '最新',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const apiUrl = `${rootUrl}/api/ajax/getlatest?page=1&pagesize=${ctx.req.query('limit') ?? 30}`;
const items = await ProcessItems(apiUrl, cache.tryGet);
- ctx.set('data', {
+ return {
title: '第一财经 - 最新',
link: rootUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/yicai/news.ts b/lib/routes/yicai/news.ts
index 327ce3487..7aa4083ad 100644
--- a/lib/routes/yicai/news.ts
+++ b/lib/routes/yicai/news.ts
@@ -1,9 +1,32 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { rootUrl, ProcessItems } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/:id?',
+ categories: ['bbs'],
+ example: '/yicai/news',
+ parameters: { id: '分类 id,见下表,可在对应分类页中找到,默认为新闻' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['yicai.com/news/:id', 'yicai.com/news'],
+ target: '/news/:id',
+ },
+ name: '新闻',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id') ?? '';
let channel;
@@ -32,9 +55,9 @@ export default async (ctx) => {
const items = await ProcessItems(apiUrl, cache.tryGet);
- ctx.set('data', {
+ return {
title: `第一财经 - ${channel?.name ?? '新闻'}`,
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/yicai/video.ts b/lib/routes/yicai/video.ts
index 17039d5b6..d43182214 100644
--- a/lib/routes/yicai/video.ts
+++ b/lib/routes/yicai/video.ts
@@ -1,9 +1,32 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { rootUrl, ProcessItems } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/video/:id?',
+ categories: ['bbs'],
+ example: '/yicai/video',
+ parameters: { id: '分类 id,见下表,可在对应分类页中找到,默认为视听' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['yicai.com/video/:id', 'yicai.com/video'],
+ target: '/video/:id',
+ },
+ name: '视听',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id') ?? '';
let channel;
@@ -32,9 +55,9 @@ export default async (ctx) => {
const items = await ProcessItems(apiUrl, cache.tryGet);
- ctx.set('data', {
+ return {
title: `第一财经 - ${channel?.name ?? '视听'}`,
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/yicai/vip.ts b/lib/routes/yicai/vip.ts
index 3eb77133c..721a6fa6c 100644
--- a/lib/routes/yicai/vip.ts
+++ b/lib/routes/yicai/vip.ts
@@ -1,10 +1,33 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { rootUrl, ProcessItems } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/vip/:id?',
+ categories: ['bbs'],
+ example: '/yicai/vip/428',
+ parameters: { id: '频道 id,可在对应频道页中找到,默认为一元点金' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['yicai.com/vip/product/:id', 'yicai.com/'],
+ target: '/vip/:id',
+ },
+ name: 'VIP 频道',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id') ?? '428';
const currentUrl = `${rootUrl}/vip/product/${id}`;
@@ -19,9 +42,9 @@ export default async (ctx) => {
const items = await ProcessItems(apiUrl, cache.tryGet);
- ctx.set('data', {
+ return {
title: `第一财经VIP频道 - ${$('title').text()}`,
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/ymgal/article.ts b/lib/routes/ymgal/article.ts
index dc7abfbfe..8d36c894a 100644
--- a/lib/routes/ymgal/article.ts
+++ b/lib/routes/ymgal/article.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -11,7 +12,25 @@ const types = {
column: '?type=COLUMN&page=1',
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/article/:type?',
+ categories: ['program-update'],
+ example: '/ymgal/article',
+ parameters: { type: '文章类型' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '文章',
+ maintainers: ['SunBK201'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type') || 'all';
const link = `${host}/co/topic/list` + types[type];
@@ -53,10 +72,10 @@ export default async (ctx) => {
info = '专栏';
}
- ctx.set('data', {
+ return {
title: `月幕 Galgame - ${info}`,
link: `${host}/co/article`,
description: `月幕 Galgame - ${info}`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/ymgal/game.ts b/lib/routes/ymgal/game.ts
index 7ab097e6b..4f26bee93 100644
--- a/lib/routes/ymgal/game.ts
+++ b/lib/routes/ymgal/game.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -12,7 +13,28 @@ const date = new Date();
const year = date.getFullYear();
const month = date.getMonth() + 1;
-export default async (ctx) => {
+export const route: Route = {
+ path: '/game/release',
+ categories: ['program-update'],
+ example: '/ymgal/game/release',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['ymgal.games/'],
+ },
+ name: '本月新作',
+ maintainers: ['SunBK201'],
+ handler,
+};
+
+async function handler() {
const response = await got({
method: 'get',
url: `${host}/release-list/${year}/${month}`,
@@ -35,10 +57,10 @@ export default async (ctx) => {
};
});
- ctx.set('data', {
+ return {
title: `月幕 Galgame - 本月新作`,
link: `${host}/release-list/${year}/${month}`,
description: '月幕 Galgame - 本月新作',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/yoasobi-music/info.ts b/lib/routes/yoasobi-music/info.ts
index 85da01039..01868473b 100644
--- a/lib/routes/yoasobi-music/info.ts
+++ b/lib/routes/yoasobi-music/info.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -7,7 +8,29 @@ import { parseJSONP } from './jsonp-helper';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/info/:category?',
+ categories: ['multimedia'],
+ example: '/yoasobi-music/info/news',
+ parameters: { category: '`news`, `biography`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.yoasobi-music.jp/', 'www.yoasobi-music.jp/:category'],
+ target: '/info/:category',
+ },
+ name: 'News & Biography',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category');
const ARTIST = 'YOASOBI',
@@ -42,7 +65,7 @@ export default async (ctx) => {
};
});
- ctx.set('data', {
+ return {
// the source title
title,
// the source url
@@ -67,5 +90,5 @@ export default async (ctx) => {
link: i.id ? `${officialUrl}/${i.id}` : officialUrl,
category: i.category,
})),
- });
-};
+ };
+}
diff --git a/lib/routes/yoasobi-music/live.ts b/lib/routes/yoasobi-music/live.ts
index 2028e10f6..94f0629e0 100644
--- a/lib/routes/yoasobi-music/live.ts
+++ b/lib/routes/yoasobi-music/live.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -6,7 +7,28 @@ import { parseJSONP } from './jsonp-helper';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/live',
+ categories: ['multimedia'],
+ example: '/yoasobi-music/live',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.yoasobi-music.jp/', 'www.yoasobi-music.jp/live'],
+ },
+ name: 'Live',
+ maintainers: ['Kiotlin'],
+ handler,
+};
+
+async function handler() {
const ARTIST = 'YOASOBI',
SONYJPURL = 'https://www.sonymusic.co.jp',
BASEURL = 'https://www.sonymusic.co.jp/json/v2/artist',
@@ -30,7 +52,7 @@ export default async (ctx) => {
sessions: item.liveItem,
}));
- ctx.set('data', {
+ return {
// the source title
title,
// the source url
@@ -51,5 +73,5 @@ export default async (ctx) => {
// the article link
link: item.link,
})),
- });
-};
+ };
+}
diff --git a/lib/routes/yoasobi-music/media.ts b/lib/routes/yoasobi-music/media.ts
index 192ad59bf..75a008324 100644
--- a/lib/routes/yoasobi-music/media.ts
+++ b/lib/routes/yoasobi-music/media.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -6,7 +7,28 @@ import { parseJSONP } from './jsonp-helper';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/media',
+ categories: ['multimedia'],
+ example: '/yoasobi-music/media',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.yoasobi-music.jp/', 'www.yoasobi-music.jp/media'],
+ },
+ name: 'Media',
+ maintainers: ['Kiotlin'],
+ handler,
+};
+
+async function handler() {
const ARTIST = 'YOASOBI',
BASEURL = 'https://www.sonymusic.co.jp/json/v2/artist',
POSTFIX = 'start/0/count/-1';
@@ -33,7 +55,7 @@ export default async (ctx) => {
description: item.note,
}));
- ctx.set('data', {
+ return {
// the source title
title,
// the source url
@@ -58,5 +80,5 @@ export default async (ctx) => {
// the article link
link: officialUrl,
})),
- });
-};
+ };
+}
diff --git a/lib/routes/yomiuri/news.ts b/lib/routes/yomiuri/news.ts
index d3ab637cd..8cf928e9e 100644
--- a/lib/routes/yomiuri/news.ts
+++ b/lib/routes/yomiuri/news.ts
@@ -1,10 +1,32 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:category?',
+ categories: ['bbs'],
+ example: '/yomiuri/news',
+ parameters: { category: 'Category, `news` by default' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.yomiuri.co.jp/:category?'],
+ },
+ name: 'News',
+ maintainers: ['Arracc'],
+ handler,
+};
+
+async function handler(ctx) {
const { category = 'news' } = ctx.req.param();
const url = `https://www.yomiuri.co.jp/${category}`;
@@ -71,10 +93,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('head title').text(),
link: url,
image: 'https://www.yomiuri.co.jp/apple-touch-icon.png',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/yomujp/level.ts b/lib/routes/yomujp/level.ts
index b6c16de43..1363f7686 100644
--- a/lib/routes/yomujp/level.ts
+++ b/lib/routes/yomujp/level.ts
@@ -1,9 +1,32 @@
+import { Route } from '@/types';
import { parseDate } from '@/utils/parse-date';
import { load } from 'cheerio';
import got from '@/utils/got';
import md5 from '@/utils/md5';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:level?',
+ categories: ['government'],
+ example: '/yomujp/n1',
+ parameters: { level: '等级,n1~n6,为空默认全部' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['yomujp.com/', 'yomujp.com/:level'],
+ target: '/:level',
+ },
+ name: '等级',
+ maintainers: ['eternasuno'],
+ handler,
+};
+
+async function handler(ctx) {
const level = formatLevel(ctx.req.param('level'));
const url = new URL('https://yomujp.com/wp-json/wp/v2/posts');
url.searchParams.append('categories', getLevel(level));
@@ -42,7 +65,7 @@ export default async (ctx) => {
};
});
- ctx.set('data', {
+ return {
title: level ? `${level.toUpperCase()} | 日本語多読道場` : '日本語多読道場',
link: `https://yomujp.com/${level}`,
description: 'みなさん、こんにちは。 「 日本語多読道場(にほんごたどくどうじょう) Yomujp」は日本語を勉強する人のための読みものサイト(website)です。 日本の地理、食べもの、動物、植物、文化や歴史などを紹介します。',
@@ -50,8 +73,8 @@ export default async (ctx) => {
itunes_author: 'Yomujp',
image: 'https://yomujp.com/wp-content/uploads/2023/08/top1-2-300x99-1.png',
item,
- });
-};
+ };
+}
const formatLevel = (level) => {
const lowerCaseLevel = level?.toLowerCase();
diff --git a/lib/routes/youku/channel.ts b/lib/routes/youku/channel.ts
index cb05b08c5..9ef3ee123 100644
--- a/lib/routes/youku/channel.ts
+++ b/lib/routes/youku/channel.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -7,7 +8,29 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/channel/:channelId/:embed?',
+ categories: ['picture'],
+ example: '/youku/channel/UNTg3MTM3OTcy',
+ parameters: { channelId: '频道 id', embed: '默认为开启内嵌视频, 任意值为关闭' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['i.youku.com/i/:id'],
+ target: '/channel/:id',
+ },
+ name: '频道',
+ maintainers: ['xyqfer', 'Fatpandac'],
+ handler,
+};
+
+async function handler(ctx) {
const channelId = ctx.req.param('channelId');
const embed = !ctx.req.param('embed');
@@ -24,7 +47,7 @@ export default async (ctx) => {
const $ = load(data);
const list = $('div.videoitem_pack');
- ctx.set('data', {
+ return {
title: $('.username').text(),
link: `https://i.youku.com/i/${channelId}`,
description: $('.desc').text(),
@@ -55,5 +78,5 @@ export default async (ctx) => {
})
.get()
.filter(Boolean),
- });
-};
+ };
+}
diff --git a/lib/routes/youtube/channel.ts b/lib/routes/youtube/channel.ts
index 44dbadb78..c72e26a5d 100644
--- a/lib/routes/youtube/channel.ts
+++ b/lib/routes/youtube/channel.ts
@@ -1,9 +1,32 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import utils from './utils';
import { config } from '@/config';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/channel/:id/:embed?',
+ categories: ['new-media'],
+ example: '/youtube/channel/UCDwDMPOZfxVV0x_dz0eQ8KQ',
+ parameters: { id: 'YouTube channel id', embed: 'Default to embed the video, set to any value to disable embedding' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.youtube.com/channel/:id'],
+ target: '/channel/:id',
+ },
+ name: 'Channel',
+ maintainers: ['DIYgod'],
+ handler,
+};
+
+async function handler(ctx) {
if (!config.youtube || !config.youtube.key) {
throw new Error('YouTube RSS is disabled due to the lack of relevant config');
}
@@ -18,7 +41,7 @@ export default async (ctx) => {
const data = (await utils.getPlaylistItems(playlistId, 'snippet', cache)).data.items;
- ctx.set('data', {
+ return {
title: `${data[0].snippet.channelTitle} - YouTube`,
link: `https://www.youtube.com/channel/${id}`,
description: `YouTube channel ${data[0].snippet.channelTitle}`,
@@ -36,5 +59,5 @@ export default async (ctx) => {
author: snippet.videoOwnerChannelTitle,
};
}),
- });
-};
+ };
+}
diff --git a/lib/routes/youtube/charts.ts b/lib/routes/youtube/charts.ts
index 505e2006c..169c3654a 100644
--- a/lib/routes/youtube/charts.ts
+++ b/lib/routes/youtube/charts.ts
@@ -1,9 +1,28 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { renderDescription } from './utils';
import { config } from '@/config';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/charts/:category?/:country?/:embed?',
+ categories: ['new-media'],
+ example: '/youtube/charts',
+ parameters: { category: 'Chart, see table below, default to `TopVideos`', country: 'Country Code, see table below, default to global', embed: 'Default to embed the video, set to any value to disable embedding' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Music Charts',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const contentMap = {
TopArtists: {
contentKey: 'artists',
@@ -88,9 +107,9 @@ export default async (ctx) => {
};
});
- ctx.set('data', {
+ return {
title: `YouTube Music Charts - ${contentMap[category].title}`,
link: `https://charts.youtube.com/charts/${category}/${country ?? 'global'}`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/youtube/community.ts b/lib/routes/youtube/community.ts
index 58dffdf1c..5343154ae 100644
--- a/lib/routes/youtube/community.ts
+++ b/lib/routes/youtube/community.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -8,7 +9,25 @@ import { art } from '@/utils/render';
import * as path from 'node:path';
import { isYouTubeChannelId } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/community/:handle',
+ categories: ['new-media'],
+ example: '/youtube/community/@JFlaMusic',
+ parameters: { handle: 'YouTube handles or channel id' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Community',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const handle = ctx.req.param('handle');
let urlPath = handle;
@@ -50,10 +69,10 @@ export default async (ctx) => {
};
});
- ctx.set('data', {
+ return {
title: `${username} - Community - YouTube`,
link: channelMetadata.channelUrl,
description: channelMetadata.description,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/youtube/custom.ts b/lib/routes/youtube/custom.ts
index 54add5ecb..b2b8ab338 100644
--- a/lib/routes/youtube/custom.ts
+++ b/lib/routes/youtube/custom.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import utils from './utils';
import { config } from '@/config';
@@ -5,7 +6,18 @@ import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/c/:username/:embed?',
+ radar: {
+ source: ['www.youtube.com/c/:id'],
+ target: '/c/:id',
+ },
+ name: 'Unknown',
+ maintainers: [],
+ handler,
+};
+
+async function handler(ctx) {
if (!config.youtube || !config.youtube.key) {
throw new Error('YouTube RSS is disabled due to the lack of relevant config');
}
@@ -24,7 +36,7 @@ export default async (ctx) => {
const data = (await utils.getPlaylistItems(playlistId, 'snippet', cache)).data.items;
- ctx.set('data', {
+ return {
title: `${username} - YouTube`,
link: `https://www.youtube.com/c/${username}`,
description: ytInitialData.metadata.channelMetadataRenderer.description,
@@ -42,5 +54,5 @@ export default async (ctx) => {
author: snippet.videoOwnerChannelTitle,
};
}),
- });
-};
+ };
+}
diff --git a/lib/routes/youtube/live.ts b/lib/routes/youtube/live.ts
index 845a99993..f95d535d1 100644
--- a/lib/routes/youtube/live.ts
+++ b/lib/routes/youtube/live.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import utils from './utils';
import { config } from '@/config';
@@ -5,7 +6,25 @@ import { parseDate } from '@/utils/parse-date';
import got from '@/utils/got';
import { load } from 'cheerio';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/live/:username/:embed?',
+ categories: ['multimedia'],
+ example: '/youtube/live/@GawrGura',
+ parameters: { username: 'YouTuber id', embed: 'Default to embed the video, set to any value to disable embedding' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Live',
+ maintainers: ['sussurr127'],
+ handler,
+};
+
+async function handler(ctx) {
if (!config.youtube || !config.youtube.key) {
throw new Error('YouTube RSS is disabled due to the lack of relevant config');
}
@@ -29,7 +48,7 @@ export default async (ctx) => {
const data = (await utils.getLive(channelId, cache)).data.items;
- ctx.set('data', {
+ return {
title: `${channelName || username}'s Live Status`,
link: `https://www.youtube.com/channel/${channelId}`,
description: `$${channelName || username}'s live streaming status`,
@@ -46,5 +65,5 @@ export default async (ctx) => {
};
}),
allowEmpty: true,
- });
-};
+ };
+}
diff --git a/lib/routes/youtube/playlist.ts b/lib/routes/youtube/playlist.ts
index 808e05df8..0081ecc3c 100644
--- a/lib/routes/youtube/playlist.ts
+++ b/lib/routes/youtube/playlist.ts
@@ -1,9 +1,28 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import utils from './utils';
import { config } from '@/config';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/playlist/:id/:embed?',
+ categories: ['new-media'],
+ example: '/youtube/playlist/PLqQ1RwlxOgeLTJ1f3fNMSwhjVgaWKo_9Z',
+ parameters: { id: 'YouTube playlist id', embed: 'Default to embed the video, set to any value to disable embedding' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'Playlist',
+ maintainers: ['HenryQW'],
+ handler,
+};
+
+async function handler(ctx) {
if (!config.youtube || !config.youtube.key) {
throw new Error('YouTube RSS is disabled due to the lack of relevant config');
}
@@ -14,7 +33,7 @@ export default async (ctx) => {
const data = (await utils.getPlaylistItems(id, 'snippet', cache)).data.items.filter((d) => d.snippet.title !== 'Private video' && d.snippet.title !== 'Deleted video');
- ctx.set('data', {
+ return {
title: `${playlistTitle} by ${data[0].snippet.channelTitle} - YouTube`,
link: `https://www.youtube.com/playlist?list=${id}`,
description: `YouTube playlist ${playlistTitle} by ${data[0].snippet.channelTitle}`,
@@ -30,5 +49,5 @@ export default async (ctx) => {
author: snippet.videoOwnerChannelTitle,
};
}),
- });
-};
+ };
+}
diff --git a/lib/routes/youtube/subscriptions.ts b/lib/routes/youtube/subscriptions.ts
index 992c4d194..017333a9b 100644
--- a/lib/routes/youtube/subscriptions.ts
+++ b/lib/routes/youtube/subscriptions.ts
@@ -1,10 +1,33 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import { config } from '@/config';
import utils from './utils';
import { parseDate } from '@/utils/parse-date';
import asyncPool from 'tiny-async-pool';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/subscriptions/:embed?',
+ categories: ['new-media'],
+ example: '/youtube/subscriptions',
+ parameters: { embed: 'Default to embed the video, set to any value to disable embedding' },
+ features: {
+ requireConfig: true,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.youtube.com/feed/subscriptions', 'www.youtube.com/feed/channels'],
+ target: '/subscriptions',
+ },
+ name: 'Subscriptions',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
if (!config.youtube || !config.youtube.key || !config.youtube.clientId || !config.youtube.clientSecret || !config.youtube.refreshToken) {
throw new Error('YouTube RSS is disabled due to the lack of relevant config');
}
@@ -41,11 +64,11 @@ export default async (ctx) => {
};
});
- ctx.set('data', {
+ return {
title: 'Subscriptions - YouTube',
description: 'YouTube Subscriptions',
item: items,
- });
+ };
ctx.set('json', {
title: 'Subscriptions - YouTube',
@@ -54,4 +77,4 @@ export default async (ctx) => {
playlistIds,
item: items,
});
-};
+}
diff --git a/lib/routes/youtube/user.ts b/lib/routes/youtube/user.ts
index 830a60e53..3eefb1933 100644
--- a/lib/routes/youtube/user.ts
+++ b/lib/routes/youtube/user.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import utils from './utils';
import { config } from '@/config';
@@ -5,7 +6,29 @@ import { parseDate } from '@/utils/parse-date';
import got from '@/utils/got';
import { load } from 'cheerio';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/user/:username/:embed?',
+ categories: ['new-media'],
+ example: '/youtube/user/JFlaMusic',
+ parameters: { username: 'YouTuber id', embed: 'Default to embed the video, set to any value to disable embedding' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.youtube.com/user/:username'],
+ target: '/user/:username',
+ },
+ name: 'User',
+ maintainers: ['DIYgod'],
+ handler,
+};
+
+async function handler(ctx) {
if (!config.youtube || !config.youtube.key) {
throw new Error('YouTube RSS is disabled due to the lack of relevant config');
}
@@ -26,7 +49,7 @@ export default async (ctx) => {
const data = (await utils.getPlaylistItems(playlistId, 'snippet', cache)).data.items;
- ctx.set('data', {
+ return {
title: `${channelName || username} - YouTube`,
link: username.startsWith('@') ? `https://www.youtube.com/${username}` : `https://www.youtube.com/user/${username}`,
description: `YouTube user ${username}`,
@@ -44,5 +67,5 @@ export default async (ctx) => {
author: snippet.videoOwnerChannelTitle,
};
}),
- });
-};
+ };
+}
diff --git a/lib/routes/youzhiyouxing/materials.ts b/lib/routes/youzhiyouxing/materials.ts
index d7aa18427..af15481db 100644
--- a/lib/routes/youzhiyouxing/materials.ts
+++ b/lib/routes/youzhiyouxing/materials.ts
@@ -1,9 +1,32 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/materials/:id?',
+ categories: ['other'],
+ example: '/youzhiyouxing/materials',
+ parameters: { id: '分类,见下表,默认为全部' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['youzhiyouxing.cn/materials'],
+ target: '/materials',
+ },
+ name: '有知文章',
+ maintainers: ['broven', 'Fatpandac', 'nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id') ?? '';
const rootUrl = 'https://youzhiyouxing.cn';
@@ -49,9 +72,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `有知有行 - ${$(`a[phx-value-column_id="${id === '' ? 0 : id}"]`).text()}`,
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/yuque/book.ts b/lib/routes/yuque/book.ts
index 2407f8f1e..2bcc2d419 100644
--- a/lib/routes/yuque/book.ts
+++ b/lib/routes/yuque/book.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -8,7 +9,28 @@ import { CookieJar } from 'tough-cookie';
const APP_DATA_REGEX = /window\.appData = JSON\.parse\(decodeURIComponent\("(.+?)"\)\);/;
const baseUrl = 'https://www.yuque.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:name/:book',
+ categories: ['journal'],
+ example: '/yuque/ruanyf/weekly',
+ parameters: { name: '用戶名', book: '知识库 ID' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['yuque.com:name/:book'],
+ },
+ name: '知识库',
+ maintainers: ['aha2mao', 'ltaoo'],
+ handler,
+};
+
+async function handler(ctx) {
const cookieJar = new CookieJar();
const { name, book } = ctx.req.param();
const bookUrl = `${baseUrl}/${name}/${book}`;
@@ -89,11 +111,11 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: appData.book.name,
description: appData.book.description,
image: appData.group.avatar,
link: bookUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/yxdown/news.ts b/lib/routes/yxdown/news.ts
index 39e3dae43..79272801a 100644
--- a/lib/routes/yxdown/news.ts
+++ b/lib/routes/yxdown/news.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -5,7 +6,25 @@ import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
import { rootUrl, getCookie } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/news/:category?',
+ categories: ['reading'],
+ example: '/yxdown/news',
+ parameters: { category: '分类,见下表,默认为资讯首页' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '资讯',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ? `${ctx.req.param('category')}/` : '';
const currentUrl = `${rootUrl}/news/${category}`;
@@ -50,9 +69,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `${$('.now').text()} - 游讯网`,
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/yxdown/recommend.ts b/lib/routes/yxdown/recommend.ts
index f8a1906d7..95b368d14 100644
--- a/lib/routes/yxdown/recommend.ts
+++ b/lib/routes/yxdown/recommend.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -5,7 +6,28 @@ import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
import { rootUrl, getCookie } from './utils';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/recommend',
+ categories: ['reading'],
+ example: '/yxdown/recommend',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['yxdown.com/'],
+ },
+ name: '精彩推荐',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler() {
const currentUrl = `${rootUrl}/news/`;
const cookie = await getCookie();
const response = await got(currentUrl, {
@@ -47,9 +69,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: '精彩推荐 - 游讯网',
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/yxdzqb/index.ts b/lib/routes/yxdzqb/index.ts
index e86fceb24..beabc3ff3 100644
--- a/lib/routes/yxdzqb/index.ts
+++ b/lib/routes/yxdzqb/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -16,7 +17,28 @@ const map = {
low_chinese: 'index_low_cn.html',
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:type',
+ categories: ['reading'],
+ example: '/yxdzqb/popular_cn',
+ parameters: { type: '折扣类型' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['yxdzqb.com/'],
+ },
+ name: '游戏折扣',
+ maintainers: ['LogicJake', 'nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type');
const link = `${host}/${Object.hasOwn(map, type) ? map[type] : `index_${type}.html`}`;
@@ -48,9 +70,9 @@ export default async (ctx) => {
})
.get();
- ctx.set('data', {
+ return {
title: `${title}-游戏打折情报`,
link,
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/yxrb/home.ts b/lib/routes/yxrb/home.ts
index 4f714f297..4db4cab68 100644
--- a/lib/routes/yxrb/home.ts
+++ b/lib/routes/yxrb/home.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -6,7 +7,29 @@ import timezone from '@/utils/timezone';
const baseUrl = 'http://news.yxrb.net';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:category?',
+ categories: ['reading'],
+ example: '/yxrb/info',
+ parameters: { category: '分类,见下表,预设为 `info`' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['news.yxrb.net/:category', 'news.yxrb.net/'],
+ target: '/:category',
+ },
+ name: '分类',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const { category = 'info' } = ctx.req.param();
const link = `${baseUrl}/${category}/`;
const response = await got(link);
@@ -52,11 +75,11 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: $('head title').text(),
description: $('head meta[name=description]').attr('content'),
link,
image: $('.channel-img img').attr('src'),
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/yyets/article.ts b/lib/routes/yyets/article.ts
index 563ba9ee9..3bd8ebb22 100644
--- a/lib/routes/yyets/article.ts
+++ b/lib/routes/yyets/article.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -6,7 +7,25 @@ import timezone from '@/utils/timezone';
const baseURL = 'https://yysub.net';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/article/:type?',
+ categories: ['picture'],
+ example: '/yyets/article',
+ parameters: { type: '[' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '影视资讯',
+ maintainers: ['wb121017405'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type') ?? '';
const url = `${baseURL}/article${type ? '?type=' + type : ''}`;
@@ -37,10 +56,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `${$('title').text()} - 人人影视`,
description: $('meta[name="description"]').attr('content'),
link: url,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/yyets/today.ts b/lib/routes/yyets/today.ts
index b9806042f..2d25b8895 100644
--- a/lib/routes/yyets/today.ts
+++ b/lib/routes/yyets/today.ts
@@ -1,7 +1,29 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/today',
+ categories: ['picture'],
+ example: '/yyets/today',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['yysub.net/tv/schedule', 'yysub.net/'],
+ },
+ name: '今日播出',
+ maintainers: ['bao1991213'],
+ handler,
+};
+
+async function handler() {
// 发起 HTTP GET 请求
const response = await got({
method: 'get',
@@ -18,7 +40,7 @@ export default async (ctx) => {
// 注:cheerio 选择器与 jquery 选择器几乎相同
// 参考 cheerio 文档:https://cheerio.js.org/
- ctx.set('data', {
+ return {
title: '人人影视-今日播出',
link: 'https://yysub.net',
item:
@@ -33,5 +55,5 @@ export default async (ctx) => {
};
})
.get(),
- });
-};
+ };
+}
diff --git a/lib/routes/yystv/category.ts b/lib/routes/yystv/category.ts
index 13383b97e..15deadd34 100644
--- a/lib/routes/yystv/category.ts
+++ b/lib/routes/yystv/category.ts
@@ -1,9 +1,28 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate, parseRelativeDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/category/:category',
+ categories: ['reading'],
+ example: '/yystv/category/recommend',
+ parameters: { category: '专栏类型' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '游研社 - 分类文章',
+ maintainers: ['LightStrawberry'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category');
const url = `https://www.yystv.cn/b/${category}`;
const response = await got({
@@ -56,11 +75,9 @@ export default async (ctx) => {
})
);
}
- await getDescription(items).then(() => {
- ctx.set('data', {
- title: '游研社-' + $('title').text(),
- link: `https://www.yystv.cn/b/${category}`,
- item: items,
- });
- });
-};
+ await getDescription(items).then(() => ({
+ title: '游研社-' + $('title').text(),
+ link: `https://www.yystv.cn/b/${category}`,
+ item: items,
+ }));
+}
diff --git a/lib/routes/yystv/docs.ts b/lib/routes/yystv/docs.ts
index 8d424d11c..2c8f4c8f7 100644
--- a/lib/routes/yystv/docs.ts
+++ b/lib/routes/yystv/docs.ts
@@ -1,8 +1,30 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseRelativeDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/docs',
+ categories: ['reading'],
+ example: '/yystv/docs',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['yystv.cn/docs'],
+ },
+ name: '游研社 - 全部文章',
+ maintainers: ['HaitianLiu'],
+ handler,
+};
+
+async function handler() {
const url = `https://www.yystv.cn/docs`;
const response = await got({
method: 'get',
@@ -26,9 +48,9 @@ export default async (ctx) => {
})
.get();
- ctx.set('data', {
+ return {
title: '游研社-' + $('title').text(),
link: `https://www.yystv.cn/docs`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/zagg/new-arrivals.ts b/lib/routes/zagg/new-arrivals.ts
index cf0df7822..d65bdb3a5 100644
--- a/lib/routes/zagg/new-arrivals.ts
+++ b/lib/routes/zagg/new-arrivals.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -6,7 +7,25 @@ import { art } from '@/utils/render';
import * as path from 'node:path';
import { load } from 'cheerio';
const host = 'https://www.zagg.com/en_us';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/new-arrivals/:query?',
+ categories: ['game'],
+ example: '/zagg/new-arrivals/brand=164&cat=3038,3041',
+ parameters: { query: 'query, search page querystring' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: 'New Arrivals',
+ maintainers: ['EthanWng97'],
+ handler,
+};
+
+async function handler(ctx) {
const query = ctx.req.param('query');
const params = new URLSearchParams(query);
const brands = params.get('brand');
@@ -42,7 +61,7 @@ export default async (ctx) => {
return data;
})
.get();
- ctx.set('data', {
+ return {
title: 'Zagg - New Arrivals',
link: response.url,
description: 'Zagg - New Arrivals',
@@ -51,5 +70,5 @@ export default async (ctx) => {
description: item.description,
link: item.link,
})),
- });
-};
+ };
+}
diff --git a/lib/routes/zaker/index.ts b/lib/routes/zaker/index.ts
index 8c8502b2a..8c4078b6c 100644
--- a/lib/routes/zaker/index.ts
+++ b/lib/routes/zaker/index.ts
@@ -1,10 +1,22 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseRelativeDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:type/:id?',
+ radar: {
+ source: ['myzaker.com/:type/:id'],
+ target: '/:type/:id',
+ },
+ name: 'Unknown',
+ maintainers: ['LogicJake', 'kt286', 'AlexdanerZe', 'TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type') ?? 'channel';
const id = ctx.req.param('id') ?? 1;
const rootUrl = 'http://www.myzaker.com';
@@ -58,9 +70,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: type === 'focusread' ? 'ZAKER 精读新闻' : feedTitle,
link,
item: items.filter((t) => t.description !== '原文已被删除'),
- });
-};
+ };
+}
diff --git a/lib/routes/zaobao/index.ts b/lib/routes/zaobao/index.ts
index 859cbf0f1..f446667d7 100644
--- a/lib/routes/zaobao/index.ts
+++ b/lib/routes/zaobao/index.ts
@@ -1,17 +1,36 @@
+import { Route } from '@/types';
import { parseList } from './util';
const baseUrl = 'https://www.zaobao.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:type?/:section?',
+ categories: ['bbs'],
+ example: '/zaobao/lifestyle/health',
+ parameters: { type: 'https://www.zaobao.com/**lifestyle**/health 中的 **lifestyle**', section: 'https://www.zaobao.com/lifestyle/**health** 中的 **health**' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '其他栏目',
+ maintainers: ['shunf4'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type') ?? 'realtime';
const section = ctx.req.param('section') ?? 'china';
const sectionLink = `/${type}/${section}`;
const { title, resultList } = await parseList(ctx, sectionLink);
- ctx.set('data', {
+ return {
title: `《联合早报》${title}`,
link: baseUrl + sectionLink,
description: '新加坡、中国、亚洲和国际的即时、评论、商业、体育、生活、科技与多媒体新闻,尽在联合早报。',
item: resultList,
- });
-};
+ };
+}
diff --git a/lib/routes/zaobao/interactive.ts b/lib/routes/zaobao/interactive.ts
index 0511764ff..15eba6bd4 100644
--- a/lib/routes/zaobao/interactive.ts
+++ b/lib/routes/zaobao/interactive.ts
@@ -1,15 +1,34 @@
+import { Route } from '@/types';
import { parseList } from './util';
const baseUrl = 'https://www.zaobao.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/interactive-graphics',
+ categories: ['bbs'],
+ example: '/zaobao/interactive-graphics',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '互动新闻',
+ maintainers: ['shunf4'],
+ handler,
+};
+
+async function handler(ctx) {
const sectionLink = `/interactive-graphics`;
const { resultList } = await parseList(ctx, sectionLink);
- ctx.set('data', {
+ return {
title: `《联合早报》互动新闻`,
link: baseUrl + sectionLink,
description: '新加坡、中国、亚洲和国际的即时、评论、商业、体育、生活、科技与多媒体新闻,尽在联合早报。',
item: resultList,
- });
-};
+ };
+}
diff --git a/lib/routes/zaobao/realtime.ts b/lib/routes/zaobao/realtime.ts
index 072d62154..13ad59bf4 100644
--- a/lib/routes/zaobao/realtime.ts
+++ b/lib/routes/zaobao/realtime.ts
@@ -1,7 +1,26 @@
+import { Route } from '@/types';
import { parseList } from './util';
const baseUrl = 'https://www.zaobao.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/realtime/:section?',
+ categories: ['bbs'],
+ example: '/zaobao/realtime/china',
+ parameters: { section: '分类,缺省为 china' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '即时新闻',
+ maintainers: ['shunf4'],
+ handler,
+};
+
+async function handler(ctx) {
const section = ctx.req.param('section') ?? 'china';
let name;
@@ -36,10 +55,10 @@ export default async (ctx) => {
const { resultList } = await parseList(ctx, sectionLink);
- ctx.set('data', {
+ return {
title: `《联合早报》-${name}-即时`,
link: baseUrl + sectionLink,
description: '新加坡、中国、亚洲和国际的即时、评论、商业、体育、生活、科技与多媒体新闻,尽在联合早报。',
item: resultList,
- });
-};
+ };
+}
diff --git a/lib/routes/zaobao/znews.ts b/lib/routes/zaobao/znews.ts
index ef05c4726..ffc31ad78 100644
--- a/lib/routes/zaobao/znews.ts
+++ b/lib/routes/zaobao/znews.ts
@@ -1,7 +1,26 @@
+import { Route } from '@/types';
import { parseList } from './util';
const baseUrl = 'https://www.zaobao.com';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/znews/:section?',
+ categories: ['bbs'],
+ example: '/zaobao/znews/china',
+ parameters: { section: '分类,缺省为 china' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '新闻',
+ maintainers: ['shunf4'],
+ handler,
+};
+
+async function handler(ctx) {
const section = ctx.req.param('section');
let info;
@@ -40,10 +59,10 @@ export default async (ctx) => {
const { resultList } = await parseList(ctx, sectionLink);
- ctx.set('data', {
+ return {
title: `《联合早报》-${info}-新闻`,
link: baseUrl + sectionLink,
description: '新加坡、中国、亚洲和国际的即时、评论、商业、体育、生活、科技与多媒体新闻,尽在联合早报。',
item: resultList,
- });
-};
+ };
+}
diff --git a/lib/routes/zaozao/article.ts b/lib/routes/zaozao/article.ts
index 880a5e561..621a85f73 100644
--- a/lib/routes/zaozao/article.ts
+++ b/lib/routes/zaozao/article.ts
@@ -1,7 +1,30 @@
+import { Route } from '@/types';
import { parseDate } from '@/utils/parse-date';
import got from '@/utils/got';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/article/:type?',
+ categories: ['design'],
+ example: '/zaozao/article/quality',
+ parameters: { type: '文章分类' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.zaozao.run/article/:type'],
+ target: '/article/:type',
+ },
+ name: '文章',
+ maintainers: ['shaomingbo'],
+ handler,
+};
+
+async function handler(ctx) {
const { type = 'recommend' } = ctx.req.param();
const response = await got({
method: 'put',
@@ -22,7 +45,7 @@ export default async (ctx) => {
const { data } = response.data;
- ctx.set('data', {
+ return {
title: `前端早早聊 - 文章`,
link: `https://www.zaozao.run/article/${type}`,
description: `前端早早聊 - 文章`,
@@ -32,5 +55,5 @@ export default async (ctx) => {
author: item.recommenderName,
pubDate: parseDate(item.updateAt),
})),
- });
-};
+ };
+}
diff --git a/lib/routes/zcmu/jwc/index.ts b/lib/routes/zcmu/jwc/index.ts
index bd1620e4e..16a7f363b 100644
--- a/lib/routes/zcmu/jwc/index.ts
+++ b/lib/routes/zcmu/jwc/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
@@ -13,7 +14,25 @@ const map = new Map([
[5, { title: '教务处 -- 排课管理', id: 'jwgl/pkgl' }],
]);
-export default async (ctx) => {
+export const route: Route = {
+ path: '/jwc/:type?',
+ categories: ['forecast'],
+ example: '/zcmu/jwc/1',
+ parameters: { type: '通知模块id' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '教务处',
+ maintainers: ['CCraftY'],
+ handler,
+};
+
+async function handler(ctx) {
const type = Number.parseInt(ctx.req.param('type'));
const id = map.get(type).id;
const res = await got({
@@ -33,9 +52,9 @@ export default async (ctx) => {
})
.get();
- ctx.set('data', {
+ return {
title: map.get(type).title,
link: `${host}${id}`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/zcmu/yxy/index.ts b/lib/routes/zcmu/yxy/index.ts
index beeb11bb6..a2291d76b 100644
--- a/lib/routes/zcmu/yxy/index.ts
+++ b/lib/routes/zcmu/yxy/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
@@ -14,7 +15,25 @@ const map = new Map([
[6, { title: '药学院 -- 日常通知', id: 'xsgz/rctz' }],
]);
-export default async (ctx) => {
+export const route: Route = {
+ path: '/yxy/:type?',
+ categories: ['forecast'],
+ example: '/zcmu/yxy/0',
+ parameters: { type: '模块id' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '药学院',
+ maintainers: ['CCraftY'],
+ handler,
+};
+
+async function handler(ctx) {
const type = Number.parseInt(ctx.req.param('type'));
const id = map.get(type).id;
const res = await got({
@@ -34,9 +53,9 @@ export default async (ctx) => {
})
.get();
- ctx.set('data', {
+ return {
title: map.get(type).title,
link: `${host}${id}`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/zcool/discover.ts b/lib/routes/zcool/discover.ts
index 7f87c49e5..5895574b0 100644
--- a/lib/routes/zcool/discover.ts
+++ b/lib/routes/zcool/discover.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -9,7 +10,33 @@ import { art } from '@/utils/render';
import * as path from 'node:path';
import locations from './locations';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/discover/:query?/:subCate?/:hasVideo?/:city?/:college?/:recommendLevel?/:sort?',
+ categories: ['live'],
+ example: '/zcool/discover',
+ parameters: {
+ query: '查询参数或分类,若填写分类见下表,默认为空 或 `0` 即精选',
+ subCate: '子分类,见下表,默认为 `0` 即该父分类下全部',
+ hasVideo: '是否含视频,默认为 `0` 即全部,亦可选 `1` 即含视频',
+ city: '地区代码,填入发现页中 `选择城市` 中的各级地名,如 `亚洲`、`中国`、`北京`、`纽约`、`巴黎`等',
+ college: '学校,默认为 `0` 即全部',
+ recommendLevel: '推荐等级,见下表,默认为 `2` 即编辑精选',
+ sort: '排序方式,可选 `0` 即最新发布 或 `9` 即默认排序,默认为 `9`',
+ },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '发现',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const categories = {
0: '精选',
8: '平面',
@@ -151,9 +178,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `站酷 - ${categories[queries.cate]}${queries.subCate === '0' ? '' : ` - ${subCategories[queries.cate][queries.subCate]}`}`,
link: `${rootUrl}/discover?${query}`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/zcool/top.ts b/lib/routes/zcool/top.ts
index fedcadec3..d26ee04e5 100644
--- a/lib/routes/zcool/top.ts
+++ b/lib/routes/zcool/top.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -5,7 +6,25 @@ import { parseDate } from '@/utils/parse-date';
import { extractArticle, extractWork } from './utils';
const baseUrl = 'https://www.zcool.com.cn';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/top/:type',
+ categories: ['live'],
+ example: '/zcool/top/design',
+ parameters: { type: '推荐类型,详见下面的表格' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '作品总榜单',
+ maintainers: ['yuuow'],
+ handler,
+};
+
+async function handler(ctx) {
const type = ctx.req.param('type');
const url = `${baseUrl}/top/${type === 'design' ? 'index.do' : 'article.do?rankType=8'}`;
@@ -43,10 +62,10 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: data.props.pageProps.seo.title,
description: data.props.pageProps.seo.description,
link: url,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/zcool/user.ts b/lib/routes/zcool/user.ts
index bfe2b8141..1684b78ca 100644
--- a/lib/routes/zcool/user.ts
+++ b/lib/routes/zcool/user.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -5,7 +6,29 @@ import { parseDate } from '@/utils/parse-date';
import { extractArticle, extractWork } from './utils';
import { isValidHost } from '@/utils/valid-host';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/user/:uid',
+ categories: ['live'],
+ example: '/zcool/user/baiyong',
+ parameters: { uid: '个性域名前缀或者用户ID' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.zcool.com.cn/u/:id'],
+ target: '/user/:id',
+ },
+ name: 'Unknown',
+ maintainers: ['junbaor'],
+ handler,
+};
+
+async function handler(ctx) {
const uid = ctx.req.param('uid');
let pageUrl = `https://www.zcool.com.cn/u/${uid}`;
if (isNaN(uid)) {
@@ -43,11 +66,11 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: data.props.pageProps.seo.title,
description: data.props.pageProps.seo.description,
image: data.props.pageProps.userInfo.avatar.includes('?x-oss-process') ? data.props.pageProps.userInfo.avatar.split('?')[0] : data.props.pageProps.userInfo.avatar,
link: pageUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/zhangyoubao/index.ts b/lib/routes/zhangyoubao/index.ts
index 477a3c377..e93ffa8ca 100644
--- a/lib/routes/zhangyoubao/index.ts
+++ b/lib/routes/zhangyoubao/index.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);
@@ -6,7 +7,28 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import * as path from 'node:path';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/:category',
+ categories: ['reading'],
+ example: '/zhangyoubao/lol',
+ parameters: { category: '分类,见下表' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['mobile.zhangyoubao.com/:category/'],
+ },
+ name: '推荐',
+ maintainers: ['ztmzzz'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category');
const map = new Map([
['lol', { title: '英雄联盟', id: 1 }],
@@ -36,10 +58,10 @@ export default async (ctx) => {
pubDate: parseDate(item.article.publish_time, 'X'),
}));
- ctx.set('data', {
+ return {
title: '掌游宝' + map.get(category).title + '分区',
link: 'https://mobile.zhangyoubao.com/' + category,
description: '掌游宝' + map.get(category).title + '分区',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/zhibo8/forum.ts b/lib/routes/zhibo8/forum.ts
index 9e9665b07..1dc4c1422 100644
--- a/lib/routes/zhibo8/forum.ts
+++ b/lib/routes/zhibo8/forum.ts
@@ -1,10 +1,29 @@
+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';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/forum/:id',
+ categories: ['blog'],
+ example: '/zhibo8/forum/8',
+ parameters: { id: '子论坛 id,可在子论坛 URL 找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '子论坛',
+ maintainers: ['LogicJake'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const link = `https://bbs.zhibo8.cc/forum/list/?fid=${id}`;
@@ -39,9 +58,9 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: `${title}—直播吧`,
link,
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/zhibo8/luxiang.ts b/lib/routes/zhibo8/luxiang.ts
index 84bede122..25f9e313f 100644
--- a/lib/routes/zhibo8/luxiang.ts
+++ b/lib/routes/zhibo8/luxiang.ts
@@ -1,9 +1,21 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/luxiang/:category?',
+ radar: {
+ source: ['zhibo8.cc/:category/luxiang.htm'],
+ target: '/luxiang/:category',
+ },
+ name: 'Unknown',
+ maintainers: ['TonyRL'],
+ handler,
+};
+
+async function handler(ctx) {
const rootUrl = 'https://www.zhibo8.cc';
const { category = 'nba' } = ctx.req.param();
const link = `${rootUrl}/${category}/luxiang.htm`;
@@ -29,10 +41,10 @@ export default async (ctx) => {
});
});
- ctx.set('data', {
+ return {
title: $('head title').text(),
link,
image: 'https://www.zhibo8.cc/favicon.ico',
item: list,
- });
-};
+ };
+}
diff --git a/lib/routes/zhibo8/more.ts b/lib/routes/zhibo8/more.ts
index 206b39e90..3ddc09c88 100644
--- a/lib/routes/zhibo8/more.ts
+++ b/lib/routes/zhibo8/more.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -11,7 +12,29 @@ const categories = {
other: '综合',
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/more/:category?',
+ categories: ['blog'],
+ example: '/zhibo8/more/nba',
+ parameters: { category: '分类,见下表,默认为 NBA' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['news.zhibo8.cc/:category'],
+ target: '/more/:category',
+ },
+ name: 'Unknown',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? 'nba';
const rootUrl = 'https://news.zhibo8.cc';
@@ -67,9 +90,9 @@ export default async (ctx) => {
)
);
- ctx.set('data', {
+ return {
title: `${categories[category]} - 直播吧`,
link: currentUrl,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/zhibo8/post.ts b/lib/routes/zhibo8/post.ts
index 4f159c2ab..a95e8f2fd 100644
--- a/lib/routes/zhibo8/post.ts
+++ b/lib/routes/zhibo8/post.ts
@@ -1,9 +1,28 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/post/:id',
+ categories: ['blog'],
+ example: '/zhibo8/post/3050708',
+ parameters: { id: '帖子 id,可在帖子 URL 找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '回帖',
+ maintainers: ['LogicJake'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const link = `https://bbs.zhibo8.cc/forum/topic?tid=${id}`;
@@ -41,9 +60,9 @@ export default async (ctx) => {
return single;
});
- ctx.set('data', {
+ return {
title: `“${title}”的新回复—直播吧`,
link,
item: out,
- });
-};
+ };
+}
diff --git a/lib/routes/zhihu/activities.ts b/lib/routes/zhihu/activities.ts
index 3d2b6a617..071f8e9d7 100644
--- a/lib/routes/zhihu/activities.ts
+++ b/lib/routes/zhihu/activities.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import utils from './utils';
@@ -5,7 +6,28 @@ import { parseDate } from '@/utils/parse-date';
import g_encrypt from './execlib/x-zse-96-v3';
import md5 from '@/utils/md5';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/people/activities/:id',
+ categories: ['new-media'],
+ example: '/zhihu/people/activities/diygod',
+ parameters: { id: '作者 id,可在用户主页 URL 中找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.zhihu.com/people/:id'],
+ },
+ name: '用户动态',
+ maintainers: ['DIYgod'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
// Because the API of zhihu.com has changed, we must use the value of `d_c0` (extracted from cookies) to calculate
@@ -50,7 +72,7 @@ export default async (ctx) => {
const data = response.data.data;
- ctx.set('data', {
+ return {
title: `${data[0].actor.name}的知乎动态`,
link: `https://www.zhihu.com/people/${id}/activities`,
image: data[0].actor.avatar_url,
@@ -154,5 +176,5 @@ export default async (ctx) => {
link: url,
};
}),
- });
-};
+ };
+}
diff --git a/lib/routes/zhihu/answers.ts b/lib/routes/zhihu/answers.ts
index 2e517c3e5..d3cc8fddf 100644
--- a/lib/routes/zhihu/answers.ts
+++ b/lib/routes/zhihu/answers.ts
@@ -1,9 +1,31 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import utils from './utils';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/people/answers/:id',
+ categories: ['new-media'],
+ example: '/zhihu/people/answers/diygod',
+ parameters: { id: '作者 id,可在用户主页 URL 中找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.zhihu.com/people/:id/answers'],
+ },
+ name: '用户回答',
+ maintainers: ['DIYgod', 'prnake'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const headers = {
'User-Agent': 'ZhihuHybrid com.zhihu.android/Futureve/6.59.0 Mozilla/5.0 (Linux; Android 10; GM1900 Build/QKQ1.190716.003; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/85.0.4183.127 Mobile Safari/537.36',
@@ -60,9 +82,9 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: `${name}的知乎回答`,
link: `https://www.zhihu.com/people/${id}/answers`,
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/zhihu/bookstore/newest.ts b/lib/routes/zhihu/bookstore/newest.ts
index cbf435bc2..905b8f433 100644
--- a/lib/routes/zhihu/bookstore/newest.ts
+++ b/lib/routes/zhihu/bookstore/newest.ts
@@ -1,6 +1,25 @@
+import { Route } from '@/types';
import got from '@/utils/got';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/bookstore/newest',
+ categories: ['new-media'],
+ example: '/zhihu/bookstore/newest',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '知乎书店 - 新书',
+ maintainers: ['xyqfer'],
+ handler,
+};
+
+async function handler() {
const response = await got({
method: 'get',
url: 'https://api.zhihu.com/books/features/new',
@@ -8,7 +27,7 @@ export default async (ctx) => {
const data = response.data.data;
- ctx.set('data', {
+ return {
title: '知乎书店-新书抢鲜',
link: 'https://www.zhihu.com/pub/features/new',
item: data.map((item) => {
@@ -26,5 +45,5 @@ export default async (ctx) => {
`,
};
}),
- });
-};
+ };
+}
diff --git a/lib/routes/zhihu/collection.ts b/lib/routes/zhihu/collection.ts
index e4cb2dbd5..d16829903 100644
--- a/lib/routes/zhihu/collection.ts
+++ b/lib/routes/zhihu/collection.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -5,7 +6,29 @@ import utils from './utils';
import { generateData } from './pin/utils';
import { parseDate } from '@/utils/parse-date';
-export default async (ctx) => {
+export const route: Route = {
+ path: '/collection/:id/:getAll?',
+ categories: ['new-media'],
+ example: '/zhihu/collection/26444956',
+ parameters: { id: '收藏夹 id,可在收藏夹页面 URL 中找到', getAll: '获取全部收藏内容,任意值为打开' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['www.zhihu.com/collection/:id'],
+ target: '/collection/:id',
+ },
+ name: '收藏夹',
+ maintainers: ['huruji', 'Colin-XKL', 'Fatpandac'],
+ handler,
+};
+
+async function handler(ctx) {
const id = ctx.req.param('id');
const getAll = ctx.req.param('getAll');
@@ -58,7 +81,7 @@ export default async (ctx) => {
const collection_description = $('.CollectionDetailPageHeader-description').text();
const generateDataPin = (item) => generateData([item.content])[0];
- ctx.set('data', {
+ return {
title: collection_title,
link: `https://www.zhihu.com/collection/${id}`,
description: collection_description,
@@ -74,5 +97,5 @@ export default async (ctx) => {
pubDate: parseDate((item.content.type === 'article' ? item.content.updated : item.content.updated_time) * 1000),
}
),
- });
-};
+ };
+}
diff --git a/lib/routes/zhihu/daily-section.ts b/lib/routes/zhihu/daily-section.ts
index b0515808f..366692244 100644
--- a/lib/routes/zhihu/daily-section.ts
+++ b/lib/routes/zhihu/daily-section.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import utils from './utils';
@@ -6,7 +7,29 @@ import { parseDate } from '@/utils/parse-date';
// 参考:https://github.com/izzyleung/ZhihuDailyPurify/wiki/%E7%9F%A5%E4%B9%8E%E6%97%A5%E6%8A%A5-API-%E5%88%86%E6%9E%90
// 文章给出了v7版 api的信息,包含全文api
-export default async (ctx) => {
+export const route: Route = {
+ path: '/daily/section/:sectionId',
+ categories: ['new-media'],
+ example: '/zhihu/daily/section/2',
+ parameters: { sectionId: '合集 id,可在 https://news-at.zhihu.com/api/7/sections 找到' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['daily.zhihu.com/*'],
+ target: '/daily',
+ },
+ name: '知乎日报 - 合集',
+ maintainers: ['ccbikai'],
+ handler,
+};
+
+async function handler(ctx) {
const sectionId = ctx.req.param('sectionId');
const listRes = await got({
method: 'get',
@@ -42,11 +65,11 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: `${listRes.data.name} - 知乎日报`,
link: 'https://daily.zhihu.com',
description: '每天3次,每次7分钟',
image: 'http://static.daily.zhihu.com/img/new_home_v3/mobile_top_logo.png',
item: resultItem,
- });
-};
+ };
+}
diff --git a/lib/routes/zhihu/daily.ts b/lib/routes/zhihu/daily.ts
index e31b95c1e..200d41db6 100644
--- a/lib/routes/zhihu/daily.ts
+++ b/lib/routes/zhihu/daily.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import utils from './utils';
@@ -20,7 +21,28 @@ async function dohResolve(name) {
return response.data.Answer.map((item) => item.data);
}
-export default async (ctx) => {
+export const route: Route = {
+ path: '/daily',
+ categories: ['new-media'],
+ example: '/zhihu/daily',
+ parameters: {},
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: {
+ source: ['daily.zhihu.com/*'],
+ },
+ name: '知乎日报',
+ maintainers: ['DHPO'],
+ handler,
+};
+
+async function handler() {
const api = 'https://news-at.zhihu.com/api/4/news';
const HOST = 'news-at.zhihu.com';
let address = HOST;
@@ -71,11 +93,11 @@ export default async (ctx) => {
})
);
- ctx.set('data', {
+ return {
title: '知乎日报',
link: 'https://daily.zhihu.com',
description: '每天3次,每次7分钟',
image: 'http://static.daily.zhihu.com/img/new_home_v3/mobile_top_logo.png',
item: items,
- });
-};
+ };
+}
diff --git a/lib/routes/zhihu/hot.ts b/lib/routes/zhihu/hot.ts
index 3fd88dfb3..d061c5c44 100644
--- a/lib/routes/zhihu/hot.ts
+++ b/lib/routes/zhihu/hot.ts
@@ -1,3 +1,4 @@
+import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -15,7 +16,25 @@ const titles = {
film: '影视',
};
-export default async (ctx) => {
+export const route: Route = {
+ path: '/hot/:category?',
+ categories: ['new-media'],
+ example: '/zhihu/hot',
+ parameters: { category: '分类,见下表,默认为全站' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: true,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ name: '知乎分类热榜',
+ maintainers: ['nczitzk'],
+ handler,
+};
+
+async function handler(ctx) {
const category = ctx.req.param('category') ?? 'total';
const response = await got({
@@ -30,9 +49,9 @@ export default async (ctx) => {
description: item.target.excerpt ? `