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.desc.split('\n-').join('
  • -')}
  • `, })); - ctx.set('data', { + return { title: `哔哩哔哩更新情报 - ${config[id]}`, link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/bilibili/article.ts b/lib/routes/bilibili/article.ts index e9bd984b0..db6adeef5 100644 --- a/lib/routes/bilibili/article.ts +++ b/lib/routes/bilibili/article.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import cache from './cache'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/user/article/:uid', + categories: ['new-media'], + example: '/bilibili/user/article/334958638', + parameters: { uid: '用户 id, 可在 UP 主主页中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['space.bilibili.com/:uid'], + }, + name: 'UP 主专栏', + maintainers: ['lengthmin', 'Qixingchen'], + handler, +}; + +async function handler(ctx) { const uid = ctx.req.param('uid'); const name = await cache.getUsernameFromUID(uid); const response = await got({ @@ -29,10 +51,10 @@ export default async (ctx) => { return single; }) ); - ctx.set('data', { + return { title, link, description, item, - }); -}; + }; +} diff --git a/lib/routes/bilibili/audio.ts b/lib/routes/bilibili/audio.ts index 62b67c2d6..7ef260630 100644 --- a/lib/routes/bilibili/audio.ts +++ b/lib/routes/bilibili/audio.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import got from '@/utils/got'; const audio = 'https://www.bilibili.com/audio/au'; -export default async (ctx) => { +export const route: Route = { + path: '/audio/:id', + categories: ['new-media'], + example: '/bilibili/audio/10624', + 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 = Number.parseInt(ctx.req.param('id')); const link = `https://www.bilibili.com/audio/am${id}`; @@ -33,10 +52,10 @@ export default async (ctx) => { return single; }); - ctx.set('data', { + return { title, link, description: introduction, item: out, - }); -}; + }; +} diff --git a/lib/routes/bilibili/bangumi.ts b/lib/routes/bilibili/bangumi.ts index deabec6a1..7e2ee8fe8 100644 --- a/lib/routes/bilibili/bangumi.ts +++ b/lib/routes/bilibili/bangumi.ts @@ -1,6 +1,14 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: ['/bangumi/:seasonid', '/bangumi/media/:mediaid'], + name: 'Unknown', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { let seasonid = ctx.req.param('seasonid'); const mediaid = ctx.req.param('mediaid'); @@ -42,11 +50,11 @@ export default async (ctx) => { } } - ctx.set('data', { + return { title: mediaData?.mediaInfo.title, link: `https://www.bilibili.com/bangumi/media/md${mediaData?.mediaInfo.media_id}/`, image: mediaData?.mediaInfo.cover, description: mediaData?.mediaInfo.evaluate, item: episodes, - }); -}; + }; +} diff --git a/lib/routes/bilibili/coin.ts b/lib/routes/bilibili/coin.ts index e79a0d810..ea0df153f 100644 --- a/lib/routes/bilibili/coin.ts +++ b/lib/routes/bilibili/coin.ts @@ -1,9 +1,32 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import cache from './cache'; import utils from './utils'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/user/coin/:uid/:disableEmbed?', + categories: ['new-media'], + example: '/bilibili/user/coin/208259', + parameters: { uid: '用户 id, 可在 UP 主主页中找到', disableEmbed: '默认为开启内嵌视频, 任意值为关闭' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['space.bilibili.com/:uid'], + target: '/user/coin/:uid', + }, + name: 'UP 主投币视频', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { const uid = ctx.req.param('uid'); const disableEmbed = ctx.req.param('disableEmbed'); @@ -20,7 +43,7 @@ export default async (ctx) => { throw new Error(message ?? code); } - ctx.set('data', { + return { title: `${name} 的 bilibili 投币视频`, link: `https://space.bilibili.com/${uid}`, description: `${name} 的 bilibili 投币视频`, @@ -31,5 +54,5 @@ export default async (ctx) => { link: item.time > utils.bvidTime && item.bvid ? `https://www.bilibili.com/video/${item.bvid}` : `https://www.bilibili.com/video/av${item.aid}`, author: item.owner.name, })), - }); -}; + }; +} diff --git a/lib/routes/bilibili/danmaku.ts b/lib/routes/bilibili/danmaku.ts index 870dceb89..16175d33a 100644 --- a/lib/routes/bilibili/danmaku.ts +++ b/lib/routes/bilibili/danmaku.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { load } from 'cheerio'; import cache from './cache'; import got from '@/utils/got'; @@ -11,7 +12,25 @@ const processFloatTime = (time) => { return `${hours}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; }; -export default async (ctx) => { +export const route: Route = { + path: '/video/danmaku/:bvid/:pid?', + categories: ['new-media'], + example: '/bilibili/video/danmaku/BV1vA411b7ip/1', + parameters: { bvid: '视频AV号,可在视频页 URL 中找到', pid: '分P号,不填默认为1' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '视频弹幕', + maintainers: ['Qixingchen'], + handler, +}; + +async function handler(ctx) { let bvid = ctx.req.param('bvid'); let aid; if (!bvid.startsWith('BV')) { @@ -45,7 +64,7 @@ export default async (ctx) => { danmakuList = danmakuList.reverse().slice(0, limit); - ctx.set('data', { + return { title: `${videoName} 的 弹幕动态`, link, description: `${videoName} 的 弹幕动态`, @@ -55,5 +74,5 @@ export default async (ctx) => { guid: `${cid}-${item.p.split(',')[4]}-${item.p.split(',')[7]}`, link, })), - }); -}; + }; +} diff --git a/lib/routes/bilibili/dynamic.ts b/lib/routes/bilibili/dynamic.ts index a8015a874..f6d97d794 100644 --- a/lib/routes/bilibili/dynamic.ts +++ b/lib/routes/bilibili/dynamic.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import JSONbig from 'json-bigint'; @@ -67,7 +68,29 @@ import cacheIn from './cache'; - cover 直播间封面 */ -export default async (ctx) => { +export const route: Route = { + path: '/user/dynamic/:uid/:routeParams?', + categories: ['new-media'], + example: '/bilibili/user/dynamic/2267573', + parameters: { uid: '用户 id, 可在 UP 主主页中找到', routeParams: '额外参数;请参阅以下说明和表格' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['space.bilibili.com/:uid'], + target: '/user/dynamic/:uid', + }, + name: 'UP 主动态', + maintainers: ['DIYgod', 'zytomorrow', 'CaoMeiYouRen', 'JimenezLi'], + handler, +}; + +async function handler(ctx) { const uid = ctx.req.param('uid'); const routeParams = Object.fromEntries(new URLSearchParams(ctx.req.param('routeParams'))); const showEmoji = fallback(undefined, queryToBoolean(routeParams.showEmoji), false); @@ -236,11 +259,11 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `${author} 的 bilibili 动态`, link: `https://space.bilibili.com/${uid}/dynamic`, description: `${author} 的 bilibili 动态`, image: face, item: items, - }); -}; + }; +} diff --git a/lib/routes/bilibili/fav.ts b/lib/routes/bilibili/fav.ts index 849b191c0..f9913e421 100644 --- a/lib/routes/bilibili/fav.ts +++ b/lib/routes/bilibili/fav.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import utils from './utils'; import { parseDate } from '@/utils/parse-date'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/fav/:uid/:fid/:disableEmbed?', + categories: ['new-media'], + example: '/bilibili/fav/756508/50948568', + parameters: { uid: '用户 id, 可在 UP 主主页中找到', fid: '收藏夹 ID, 可在收藏夹的 URL 中找到, 默认收藏夹建议使用 UP 主默认收藏夹功能', disableEmbed: '默认为开启内嵌视频, 任意值为关闭' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'UP 主非默认收藏夹', + maintainers: ['Qixingchen'], + handler, +}; + +async function handler(ctx) { const fid = ctx.req.param('fid'); const uid = ctx.req.param('uid'); const disableEmbed = ctx.req.param('disableEmbed'); @@ -23,7 +42,7 @@ export default async (ctx) => { const userName = data.info.upper.name; const favName = data.info.title; - ctx.set('data', { + return { title: `${userName} 的 bilibili 收藏夹 ${favName}`, link: `https://space.bilibili.com/${uid}/#/favlist?fid=${fid}`, description: `${userName} 的 bilibili 收藏夹 ${favName}`, @@ -37,5 +56,5 @@ export default async (ctx) => { link: item.fav_time > utils.bvidTime && item.bvid ? `https://www.bilibili.com/video/${item.bvid}` : `https://www.bilibili.com/video/av${item.id}`, author: item.upper.name, })), - }); -}; + }; +} diff --git a/lib/routes/bilibili/followers.ts b/lib/routes/bilibili/followers.ts index ba9ee195e..2821d0ad3 100644 --- a/lib/routes/bilibili/followers.ts +++ b/lib/routes/bilibili/followers.ts @@ -1,8 +1,31 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import cache from './cache'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/user/followers/:uid/:loginUid', + categories: ['new-media'], + example: '/bilibili/user/followers/2267573/3', + parameters: { uid: '用户 id, 可在 UP 主主页中找到', loginUid: '用于登入的用户id,需要配置对应的 Cookie 值' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['space.bilibili.com/:uid'], + target: '/user/followers/:uid', + }, + name: 'UP 主粉丝', + maintainers: ['Qixingchen'], + handler, +}; + +async function handler(ctx) { const uid = ctx.req.param('uid'); const loginUid = ctx.req.param('loginUid'); @@ -35,7 +58,7 @@ export default async (ctx) => { } const data = response.data.data.list; - ctx.set('data', { + return { title: `${name} 的 bilibili 粉丝`, link: `https://space.bilibili.com/${uid}/#/fans/fans`, description: `${name} 的 bilibili 粉丝`, @@ -45,5 +68,5 @@ export default async (ctx) => { pubDate: new Date(item.mtime * 1000), link: `https://space.bilibili.com/${item.mid}`, })), - }); -}; + }; +} diff --git a/lib/routes/bilibili/followings-article.ts b/lib/routes/bilibili/followings-article.ts index 0d5969065..5c7e04b0d 100644 --- a/lib/routes/bilibili/followings-article.ts +++ b/lib/routes/bilibili/followings-article.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import cache from './cache'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/followings/article/:uid', + categories: ['new-media'], + example: '/bilibili/followings/article/99800931', + parameters: { uid: '用户 id' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '用户关注专栏', + maintainers: ['woshiluo'], + handler, +}; + +async function handler(ctx) { const uid = String(ctx.req.param('uid')); const name = await cache.getUsernameFromUID(uid); @@ -40,9 +59,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `${name} 关注专栏动态`, link: `https://t.bilibili.com/?tab=64`, item: out, - }); -}; + }; +} diff --git a/lib/routes/bilibili/followings-dynamic.ts b/lib/routes/bilibili/followings-dynamic.ts index abb1b542f..c56ab6306 100644 --- a/lib/routes/bilibili/followings-dynamic.ts +++ b/lib/routes/bilibili/followings-dynamic.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import cache from './cache'; import { config } from '@/config'; @@ -6,7 +7,25 @@ import JSONbig from 'json-bigint'; import { fallback, queryToBoolean } from '@/utils/readable-social'; import querystring from 'querystring'; -export default async (ctx) => { +export const route: Route = { + path: '/followings/dynamic/:uid/:routeParams?', + categories: ['new-media'], + example: '/bilibili/followings/dynamic/109937383', + parameters: { uid: '用户 id', routeParams: '额外参数;请参阅 [#UP 主动态](#bilibili-up-zhu-dong-tai) 的说明和表格' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '用户关注动态', + maintainers: ['TigerCubDen', 'JimenezLi'], + handler, +}; + +async function handler(ctx) { const uid = String(ctx.req.param('uid')); const routeParams = querystring.parse(ctx.req.param('routeParams')); @@ -143,10 +162,10 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `${name} 关注的动态`, link: `https://t.bilibili.com`, description: `${name} 关注的动态`, item: items, - }); -}; + }; +} diff --git a/lib/routes/bilibili/followings-video.ts b/lib/routes/bilibili/followings-video.ts index 8c6ef292c..0210685cf 100644 --- a/lib/routes/bilibili/followings-video.ts +++ b/lib/routes/bilibili/followings-video.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import cache from './cache'; import { config } from '@/config'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/followings/video/:uid/:disableEmbed?', + categories: ['new-media'], + example: '/bilibili/followings/video/2267573', + parameters: { uid: '用户 id', disableEmbed: '默认为开启内嵌视频, 任意值为关闭' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '用户关注视频动态', + maintainers: ['LogicJake'], + handler, +}; + +async function handler(ctx) { const uid = String(ctx.req.param('uid')); const disableEmbed = ctx.req.param('disableEmbed'); const name = await cache.getUsernameFromUID(uid); @@ -38,9 +57,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `${name} 关注视频动态`, link: `https://t.bilibili.com/?tab=8`, item: out, - }); -}; + }; +} diff --git a/lib/routes/bilibili/followings.ts b/lib/routes/bilibili/followings.ts index fdf5ac3b2..1abaaa830 100644 --- a/lib/routes/bilibili/followings.ts +++ b/lib/routes/bilibili/followings.ts @@ -1,8 +1,31 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import cache from './cache'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/user/followings/:uid/:loginUid', + categories: ['new-media'], + example: '/bilibili/user/followings/2267573/3', + parameters: { uid: '用户 id, 可在 UP 主主页中找到', loginUid: '用于登入的用户id,需要配置对应的 Cookie 值' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['space.bilibili.com/:uid'], + target: '/user/followings/:uid', + }, + name: 'UP 主关注用户', + maintainers: ['Qixingchen'], + handler, +}; + +async function handler(ctx) { const loginUid = ctx.req.param('loginUid'); const cookie = config.bilibili.cookies[loginUid]; if (cookie === undefined) { @@ -38,7 +61,7 @@ export default async (ctx) => { } const data = response.data.data.list; - ctx.set('data', { + return { title: `${name} 的 bilibili 关注`, link: `https://space.bilibili.com/${uid}/#/fans/follow`, description: `${name} 的 bilibili 关注`, @@ -48,5 +71,5 @@ export default async (ctx) => { pubDate: new Date(item.mtime * 1000), link: `https://space.bilibili.com/${item.mid}`, })), - }); -}; + }; +} diff --git a/lib/routes/bilibili/hot-search.ts b/lib/routes/bilibili/hot-search.ts index 9b05458bf..16f6ac94d 100644 --- a/lib/routes/bilibili/hot-search.ts +++ b/lib/routes/bilibili/hot-search.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import cache from './cache'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/hot-search', + categories: ['new-media'], + example: '/bilibili/hot-search', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.bilibili.com/'], + }, + name: '热搜', + maintainers: ['CaoMeiYouRen'], + handler, +}; + +async function handler() { const wbiVerifyString = await cache.getWbiVerifyString(); const params = utils.addWbiVerifyInfo('limit=10&platform=web', wbiVerifyString); const url = `https://api.bilibili.com/x/web-interface/wbi/search/square?${params}`; @@ -16,7 +38,7 @@ export default async (ctx) => { const trending = response?.data?.data?.trending; const title = trending?.title; const list = trending?.list || []; - ctx.set('data', { + return { title, link: url, description: 'bilibili热搜', @@ -25,5 +47,5 @@ export default async (ctx) => { description: `${item.keyword}
    ${item.icon ? `` : ''}`, link: item.link || item.goto || `https://search.bilibili.com/all?${new URLSearchParams({ keyword: item.keyword })}&from_source=webtop_search`, })), - }); -}; + }; +} diff --git a/lib/routes/bilibili/like.ts b/lib/routes/bilibili/like.ts index 41f15ea7d..a7ed6de08 100644 --- a/lib/routes/bilibili/like.ts +++ b/lib/routes/bilibili/like.ts @@ -1,9 +1,32 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import cache from './cache'; import utils from './utils'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/user/like/:uid/:disableEmbed?', + categories: ['new-media'], + example: '/bilibili/user/like/208259', + parameters: { uid: '用户 id, 可在 UP 主主页中找到', disableEmbed: '默认为开启内嵌视频, 任意值为关闭' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['space.bilibili.com/:uid'], + target: '/user/like/:uid', + }, + name: 'UP 主点赞视频', + maintainers: ['ygguorun'], + handler, +}; + +async function handler(ctx) { const uid = ctx.req.param('uid'); const disableEmbed = ctx.req.param('disableEmbed'); @@ -20,7 +43,7 @@ export default async (ctx) => { throw new Error(message ?? code); } - ctx.set('data', { + return { title: `${name} 的 bilibili 点赞视频`, link: `https://space.bilibili.com/${uid}`, description: `${name} 的 bilibili 点赞视频`, @@ -31,5 +54,5 @@ export default async (ctx) => { link: item.pubdate > utils.bvidTime && item.bvid ? `https://www.bilibili.com/video/${item.bvid}` : `https://www.bilibili.com/video/av${item.aid}`, author: item.owner.name, })), - }); -}; + }; +} diff --git a/lib/routes/bilibili/link-news.ts b/lib/routes/bilibili/link-news.ts index 1d02e87f6..12c35d3ae 100644 --- a/lib/routes/bilibili/link-news.ts +++ b/lib/routes/bilibili/link-news.ts @@ -1,6 +1,25 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/link/news/:product', + categories: ['new-media'], + example: '/bilibili/link/news/live', + parameters: { product: '公告分类, 包括 直播:live 小视频:vc 相簿:wh' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'link 公告', + maintainers: ['Qixingchen'], + handler, +}; + +async function handler(ctx) { const product = ctx.req.param('product'); let productTitle = ''; @@ -26,7 +45,7 @@ export default async (ctx) => { }); const data = response.data.data.items; - ctx.set('data', { + return { title: `bilibili ${productTitle}公告`, link: `https://link.bilibili.com/p/eden/news#/?tab=${product}&tag=all&page_no=1`, description: `bilibili ${productTitle}公告`, @@ -38,5 +57,5 @@ export default async (ctx) => { pubDate: new Date(item.ctime.replace(' ', 'T') + '+08:00').toUTCString(), link: item.announce_link ?? `https://link.bilibili.com/p/eden/news#/newsdetail?id=${item.id}`, })), - }); -}; + }; +} diff --git a/lib/routes/bilibili/live-area.ts b/lib/routes/bilibili/live-area.ts index 0a3311ffd..5a63800d6 100644 --- a/lib/routes/bilibili/live-area.ts +++ b/lib/routes/bilibili/live-area.ts @@ -1,6 +1,25 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/live/area/:areaID/:order', + categories: ['multimedia'], + example: '/bilibili/live/area/207/online', + parameters: { areaID: '分区 ID 分区增删较多, 可通过 [分区列表](https://api.live.bilibili.com/room/v1/Area/getList) 查询', order: '排序方式, live_time 开播时间, online 人气' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '直播分区', + maintainers: ['Qixingchen'], + handler, +}; + +async function handler(ctx) { const areaID = ctx.req.param('areaID'); const order = ctx.req.param('order'); @@ -49,7 +68,7 @@ export default async (ctx) => { }); const data = response.data.data; - ctx.set('data', { + return { title: `哔哩哔哩直播-${parentTitle}·${areaTitle}分区-${orderTitle}`, link: areaLink, description: `哔哩哔哩直播-${parentTitle}·${areaTitle}分区-${orderTitle}`, @@ -60,5 +79,5 @@ export default async (ctx) => { guid: `https://live.bilibili.com/${item.roomid} ${item.title}`, link: `https://live.bilibili.com/${item.roomid}`, })), - }); -}; + }; +} diff --git a/lib/routes/bilibili/live-room.ts b/lib/routes/bilibili/live-room.ts index 2f21223c5..4b587d968 100644 --- a/lib/routes/bilibili/live-room.ts +++ b/lib/routes/bilibili/live-room.ts @@ -1,7 +1,29 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import cache from './cache'; -export default async (ctx) => { +export const route: Route = { + path: '/live/room/:roomID', + categories: ['multimedia'], + example: '/bilibili/live/room/3', + parameters: { roomID: '房间号, 可在直播间 URL 中找到, 长短号均可' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['live.bilibili.com/:roomID'], + }, + name: '直播开播', + maintainers: ['Qixingchen'], + handler, +}; + +async function handler(ctx) { let roomID = ctx.req.param('roomID'); // 短号查询长号 @@ -31,11 +53,11 @@ export default async (ctx) => { }); } - ctx.set('data', { + return { title: `${name} 直播间开播状态`, link: `https://live.bilibili.com/${roomID}`, description: `${name} 直播间开播状态`, item: liveItem, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/bilibili/live-search.ts b/lib/routes/bilibili/live-search.ts index bb0ebc31f..a28bb57dc 100644 --- a/lib/routes/bilibili/live-search.ts +++ b/lib/routes/bilibili/live-search.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import utils from './utils'; import cache from './cache'; -export default async (ctx) => { +export const route: Route = { + path: '/live/search/:key/:order', + categories: ['multimedia'], + example: '/bilibili/live/search/dota/online', + parameters: { key: '搜索关键字', order: '排序方式, live_time 开播时间, online 人气' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '直播搜索', + maintainers: ['Qixingchen'], + handler, +}; + +async function handler(ctx) { const key = ctx.req.param('key'); const order = ctx.req.param('order'); @@ -31,7 +50,7 @@ export default async (ctx) => { }); const data = response.data.data.result.live_room; - ctx.set('data', { + return { title: `哔哩哔哩直播-${key}-${orderTitle}`, link: `https://search.bilibili.com/live?keyword=${urlEncodedKey}&order=${order}&coverType=user_cover&page=1&search_type=live`, description: `哔哩哔哩直播-${key}-${orderTitle}`, @@ -44,5 +63,5 @@ export default async (ctx) => { guid: `https://live.bilibili.com/${item.roomid} ${item.live_time}`, link: `https://live.bilibili.com/${item.roomid}`, })), - }); -}; + }; +} diff --git a/lib/routes/bilibili/mall-ip.ts b/lib/routes/bilibili/mall-ip.ts index 60bf7f117..8a8714ca2 100644 --- a/lib/routes/bilibili/mall-ip.ts +++ b/lib/routes/bilibili/mall-ip.ts @@ -1,6 +1,25 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/mall/ip/:id', + categories: ['new-media'], + example: '/bilibili/mall/ip/0_3000294', + parameters: { id: '作品 id, 可在作品列表页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '会员购作品', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const detail = await got({ @@ -21,7 +40,7 @@ export default async (ctx) => { const data = response.data.data; - ctx.set('data', { + return { title: `${detail.data.data.name} - 会员购`, description: detail.data.data.intro, link: `https://mall.bilibili.com/list.html?ip=${id}`, @@ -30,5 +49,5 @@ export default async (ctx) => { description: `${item.name}
    ¥${item.price}
    `, link: item.jumpUrlH5, })), - }); -}; + }; +} diff --git a/lib/routes/bilibili/mall-new.ts b/lib/routes/bilibili/mall-new.ts index 80b1abac6..91a602364 100644 --- a/lib/routes/bilibili/mall-new.ts +++ b/lib/routes/bilibili/mall-new.ts @@ -1,6 +1,25 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/mall/new/:category?', + categories: ['new-media'], + example: '/bilibili/mall/new/1', + parameters: { category: '分类,默认全部,见下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '会员购新品上架', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') || 0; const response = await got({ @@ -19,7 +38,7 @@ export default async (ctx) => { const type = response.data.data.vo.cateTabs.find((item) => item.cateType === response.data.data.vo.currentCateType).cateName; - ctx.set('data', { + return { title: `会员购新品上架-${type}`, link: 'https://mall.bilibili.com/newdate.html?noTitleBar=1&page=new&from=new_product&loadingShow=1', item: items.map((item) => ({ @@ -27,5 +46,5 @@ export default async (ctx) => { description: `${item.name}
    ${item.priceDesc ? `${item.pricePrefix}${item.priceSymbol}${item.priceDesc[0]}` : ''}

    APP 内打开`, link: item.itemUrlForH5, })), - }); -}; + }; +} diff --git a/lib/routes/bilibili/manga-followings.ts b/lib/routes/bilibili/manga-followings.ts index dad9e1d30..c910d0193 100644 --- a/lib/routes/bilibili/manga-followings.ts +++ b/lib/routes/bilibili/manga-followings.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import cache from './cache'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/manga/followings/:uid/:limits?', + categories: ['new-media'], + example: '/bilibili/manga/followings/26009', + parameters: { uid: '用户 id', limits: '抓取最近更新前多少本漫画,默认为10' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '用户追漫更新', + maintainers: ['yindaheng98'], + handler, +}; + +async function handler(ctx) { const uid = String(ctx.req.param('uid')); const name = await cache.getUsernameFromUID(uid); @@ -26,7 +45,7 @@ export default async (ctx) => { } const comics = response.data.data; - ctx.set('data', { + return { title: `${name} 的追漫更新 - 哔哩哔哩漫画`, link, item: comics.map((item) => ({ @@ -35,5 +54,5 @@ export default async (ctx) => { pubDate: new Date(item.last_ep_publish_time + ' +0800'), link: `https://manga.bilibili.com/detail/mc${item.comic_id}`, })), - }); -}; + }; +} diff --git a/lib/routes/bilibili/manga-update.ts b/lib/routes/bilibili/manga-update.ts index 155ce7373..e05e6378b 100644 --- a/lib/routes/bilibili/manga-update.ts +++ b/lib/routes/bilibili/manga-update.ts @@ -1,6 +1,28 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/manga/update/:comicid', + categories: ['new-media'], + example: '/bilibili/manga/update/26009', + parameters: { comicid: '漫画 id, 可在 URL 中找到, 支持带有`mc`前缀' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['manga.bilibili.com/detail/:comicid'], + }, + name: '漫画更新', + maintainers: ['hoilc'], + handler, +}; + +async function handler(ctx) { const comic_id = ctx.req.param('comicid').startsWith('mc') ? ctx.req.param('comicid').replace('mc', '') : ctx.req.param('comicid'); const link = `https://manga.bilibili.com/detail/mc${comic_id}`; @@ -17,7 +39,7 @@ export default async (ctx) => { const data = response.data.data; const author = data.author_name.join(', '); - ctx.set('data', { + return { title: `${data.title} - 哔哩哔哩漫画`, link, image: data.vertical_cover, @@ -29,5 +51,5 @@ export default async (ctx) => { pubDate: new Date(item.pub_time + ' +0800'), link: `https://manga.bilibili.com/mc${comic_id}/${item.id}`, })), - }); -}; + }; +} diff --git a/lib/routes/bilibili/page.ts b/lib/routes/bilibili/page.ts index 1977d35a5..0bad4da7a 100644 --- a/lib/routes/bilibili/page.ts +++ b/lib/routes/bilibili/page.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/video/page/:bvid/:disableEmbed?', + categories: ['new-media'], + example: '/bilibili/video/page/BV1i7411M7N9', + parameters: { bvid: '可在视频页 URL 中找到', disableEmbed: '默认为开启内嵌视频, 任意值为关闭' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '视频选集列表', + maintainers: ['sxzz'], + handler, +}; + +async function handler(ctx) { let bvid = ctx.req.param('bvid'); let aid; if (!bvid.startsWith('BV')) { @@ -20,7 +39,7 @@ export default async (ctx) => { const { title: name, pages: data } = response.data.data; - ctx.set('data', { + return { title: `视频 ${name} 的选集列表`, link, description: `视频 ${name} 的视频选集列表`, @@ -32,5 +51,5 @@ export default async (ctx) => { description: `${item.part} - ${name}${disableEmbed ? '' : `

    ${utils.iframe(aid, item.page, bvid)}`}`, link: `${link}?p=${item.page}`, })), - }); -}; + }; +} diff --git a/lib/routes/bilibili/partion-ranking.ts b/lib/routes/bilibili/partion-ranking.ts index 262636d58..4169ffcf5 100644 --- a/lib/routes/bilibili/partion-ranking.ts +++ b/lib/routes/bilibili/partion-ranking.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import utils from './utils'; const got_ins = got.extend({ @@ -14,7 +15,25 @@ function formatDate(now) { return dateTime; } -export default async (ctx) => { +export const route: Route = { + path: '/partion/ranking/:tid/:days?/:disableEmbed?', + categories: ['new-media'], + example: '/bilibili/partion/ranking/171/3', + parameters: { tid: '分区 id, 见上方表格', days: '缺省为 7, 指最近多少天内的热度排序', disableEmbed: '默认为开启内嵌视频, 任意值为关闭' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '分区视频排行榜', + maintainers: ['lengthmin'], + handler, +}; + +async function handler(ctx) { const tid = ctx.req.param('tid'); const days = ctx.req.param('days') ?? 7; const disableEmbed = ctx.req.param('disableEmbed'); @@ -48,10 +67,10 @@ export default async (ctx) => { items.push(item); } - ctx.set('data', { + return { title: `bilibili ${name} 最热视频`, link: 'https://www.bilibili.com', description: `bilibili ${name}分区 最热视频`, item: items, - }); -}; + }; +} diff --git a/lib/routes/bilibili/partion.ts b/lib/routes/bilibili/partion.ts index 9968e4d48..7dbb643e1 100644 --- a/lib/routes/bilibili/partion.ts +++ b/lib/routes/bilibili/partion.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/partion/:tid/:disableEmbed?', + categories: ['new-media'], + example: '/bilibili/partion/33', + parameters: { tid: '分区 id', disableEmbed: '默认为开启内嵌视频, 任意值为关闭' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '分区视频', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { const tid = ctx.req.param('tid'); const disableEmbed = ctx.req.param('disableEmbed'); @@ -19,7 +38,7 @@ export default async (ctx) => { name = list[0].tname; } - ctx.set('data', { + return { title: `bilibili ${name}分区`, link: 'https://www.bilibili.com', description: `bilibili ${name}分区`, @@ -32,5 +51,5 @@ export default async (ctx) => { link: item.pubdate > utils.bvidTime && item.bvid ? `https://www.bilibili.com/video/${item.bvid}` : `https://www.bilibili.com/video/av${item.aid}`, author: item.owner.name, })), - }); -}; + }; +} diff --git a/lib/routes/bilibili/platform.ts b/lib/routes/bilibili/platform.ts index 12148bb01..f446e3e40 100644 --- a/lib/routes/bilibili/platform.ts +++ b/lib/routes/bilibili/platform.ts @@ -1,8 +1,34 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/platform/:area?/:p_type?/:uid?', + categories: ['new-media'], + example: '/bilibili/platform/-1', + parameters: { + area: '省市-国标码,默认为-1即全国', + p_type: '类型:见下表,默认为全部类型', + uid: '用户id,可以不填,不过不填不设置cookie,搜索结果与登入账号后搜索结果不一样。可以在url中找到,需要配置cookie值,只需要SESSDATA的值即可', + }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['show.bilibili.com/platform'], + }, + name: '会员购票务', + maintainers: ['nightmare-mio'], + handler, +}; + +async function handler(ctx) { const { area = -1, type = '全部类型', uid } = ctx.req.param(); const cookie = config.bilibili.cookies[uid]; const link = 'https://show.bilibili.com/api/ticket/project/listV2'; @@ -35,9 +61,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `bilibili会员购票务-${area}`, link, item: items, - }); -}; + }; +} diff --git a/lib/routes/bilibili/popular.ts b/lib/routes/bilibili/popular.ts index 147f93473..3130a97bb 100644 --- a/lib/routes/bilibili/popular.ts +++ b/lib/routes/bilibili/popular.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/popular/all', + categories: ['new-media'], + example: '/bilibili/popular/all', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '综合热门', + maintainers: ['ziminliu'], + handler, +}; + +async function handler(ctx) { const disableEmbed = ctx.req.param('disableEmbed'); const response = await got({ method: 'get', @@ -12,7 +31,7 @@ export default async (ctx) => { }); const list = response.data.data.list; - ctx.set('data', { + return { title: `bilibili 综合热门`, link: 'https://www.bilibili.com', description: `bilibili 综合热门`, @@ -25,5 +44,5 @@ export default async (ctx) => { link: item.pubdate > utils.bvidTime && item.bvid ? `https://www.bilibili.com/video/${item.bvid}` : `https://www.bilibili.com/video/av${item.aid}`, author: item.owner.name, })), - }); -}; + }; +} diff --git a/lib/routes/bilibili/ranking.ts b/lib/routes/bilibili/ranking.ts index 0ee1db976..86527ea9a 100644 --- a/lib/routes/bilibili/ranking.ts +++ b/lib/routes/bilibili/ranking.ts @@ -1,7 +1,15 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/ranking/:rid?/:day?/:arc_type?/:disableEmbed?', + name: 'Unknown', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { const rid = ctx.req.param('rid') || '0'; const day = ctx.req.param('day') || '3'; const arc_type = ctx.req.param('arc_type') || '1'; @@ -29,7 +37,7 @@ export default async (ctx) => { list = [...list, ...list[i].others]; } } - ctx.set('data', { + return { title: `bilibili ${day}日排行榜-${rid_type}-${arc_type1}`, link: `https://www.bilibili.com/ranking/all/${rid}/0/${day}`, item: list.map((item) => ({ @@ -39,5 +47,5 @@ export default async (ctx) => { author: item.author, link: !item.create || (new Date(item.create) / 1000 > utils.bvidTime && item.bvid) ? `https://www.bilibili.com/video/${item.bvid}` : `https://www.bilibili.com/video/av${item.aid}`, })), - }); -}; + }; +} diff --git a/lib/routes/bilibili/readlist.ts b/lib/routes/bilibili/readlist.ts index bab0a5bad..ef276d401 100644 --- a/lib/routes/bilibili/readlist.ts +++ b/lib/routes/bilibili/readlist.ts @@ -1,6 +1,25 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/readlist/:listid', + categories: ['new-media'], + example: '/bilibili/readlist/25611', + parameters: { listid: '文集 id, 可在专栏文集 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '专栏文集', + maintainers: ['hoilc'], + handler, +}; + +async function handler(ctx) { const listid = ctx.req.param('listid'); const listurl = `https://www.bilibili.com/read/readlist/rl${listid}`; @@ -13,7 +32,7 @@ export default async (ctx) => { }); const data = response.data.data; - ctx.set('data', { + return { title: `bilibili 专栏文集 - ${data.list.name}`, link: listurl, image: data.list.image_url, @@ -27,5 +46,5 @@ export default async (ctx) => { pubDate: new Date(item.publish_time * 1000).toUTCString(), link: `https://www.bilibili.com/read/cv${item.id}/?from=readlist`, })), - }); -}; + }; +} diff --git a/lib/routes/bilibili/reply.ts b/lib/routes/bilibili/reply.ts index 24d965b52..02098d26b 100644 --- a/lib/routes/bilibili/reply.ts +++ b/lib/routes/bilibili/reply.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import cache from './cache'; -export default async (ctx) => { +export const route: Route = { + path: '/video/reply/:bvid', + categories: ['new-media'], + example: '/bilibili/video/reply/BV1vA411b7ip', + parameters: { bvid: '可在视频页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '视频评论', + maintainers: ['Qixingchen'], + handler, +}; + +async function handler(ctx) { let bvid = ctx.req.param('bvid'); let aid; if (!bvid.startsWith('BV')) { @@ -24,7 +43,7 @@ export default async (ctx) => { const data = response.data.data.replies; - ctx.set('data', { + return { title: `${name} 的 评论`, link, description: `${name} 的评论`, @@ -34,5 +53,5 @@ export default async (ctx) => { pubDate: new Date(item.ctime * 1000).toUTCString(), link: `${link}/#reply${item.rpid}`, })), - }); -}; + }; +} diff --git a/lib/routes/bilibili/user-bangumi.ts b/lib/routes/bilibili/user-bangumi.ts index c79727d16..f799e4e35 100644 --- a/lib/routes/bilibili/user-bangumi.ts +++ b/lib/routes/bilibili/user-bangumi.ts @@ -1,7 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import cache from './cache'; -export default async (ctx) => { +export const route: Route = { + path: '/user/bangumi/:uid/:type?', + categories: ['new-media'], + example: '/bilibili/user/bangumi/208259', + parameters: { uid: '用户 id', type: '1为番,2为剧,留空为1' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['space.bilibili.com/:uid'], + target: '/user/bangumi/:uid', + }, + name: '用户追番列表', + maintainers: ['wdssmq'], + handler, +}; + +async function handler(ctx) { const uid = ctx.req.param('uid'); const type = Number(ctx.req.param('type') || 1); const type_name = ((t) => ['', 'bangumi', 'cinema'][t])(type); @@ -19,7 +42,7 @@ export default async (ctx) => { throw new Error(`It looks like something went wrong when querying the Bilibili API: code = ${data.code}, message = ${data.message}`); } - ctx.set('data', { + return { title: `${name} 的追番列表`, link: `https://space.bilibili.com/${uid}/${type_name}`, description: `${name} 的追番列表`, @@ -32,5 +55,5 @@ export default async (ctx) => { pubDate: new Date(item.new_ep.pub_time ?? Date.now()).toUTCString(), link: `https://www.bilibili.com/bangumi/play/` + (item.new_ep.id ? `ep${item.new_ep.id}` : `ss${item.season_id}`), })), - }); -}; + }; +} diff --git a/lib/routes/bilibili/user-channel.ts b/lib/routes/bilibili/user-channel.ts index f35743696..05f671070 100644 --- a/lib/routes/bilibili/user-channel.ts +++ b/lib/routes/bilibili/user-channel.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'; @@ -8,7 +9,25 @@ const notFoundData = { title: '此 bilibili 频道不存在', }; -export default async (ctx) => { +export const route: Route = { + path: '/user/channel/:uid/:sid/:disableEmbed?', + categories: ['new-media'], + example: '/bilibili/user/channel/2267573/396050', + parameters: { uid: '用户 id, 可在 UP 主主页中找到', sid: '频道 id, 可在频道的 URL 中找到', disableEmbed: '默认为开启内嵌视频, 任意值为关闭' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'UP 主频道的视频列表', + maintainers: ['weirongxu'], + handler, +}; + +async function handler(ctx) { const uid = Number.parseInt(ctx.req.param('uid')); const sid = Number.parseInt(ctx.req.param('sid')); const disableEmbed = ctx.req.param('disableEmbed'); @@ -47,7 +66,7 @@ export default async (ctx) => { return; } - ctx.set('data', { + return { title: `${userName} 的 bilibili 频道 ${channelInfo.meta.name}`, link, description: `${userName} 的 bilibili 频道`, @@ -67,5 +86,5 @@ export default async (ctx) => { author: userName, }; }), - }); -}; + }; +} diff --git a/lib/routes/bilibili/user-collection.ts b/lib/routes/bilibili/user-collection.ts index 497a3c163..012b2a1fa 100644 --- a/lib/routes/bilibili/user-collection.ts +++ b/lib/routes/bilibili/user-collection.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import cache from './cache'; import utils from './utils'; @@ -7,7 +8,25 @@ const notFoundData = { title: '此 bilibili 频道不存在', }; -export default async (ctx) => { +export const route: Route = { + path: '/user/collection/:uid/:sid/:disableEmbed?', + categories: ['new-media'], + example: '/bilibili/user/collection/245645656/529166', + parameters: { uid: '用户 id, 可在 UP 主主页中找到', sid: '合集 id, 可在合集页面的 URL 中找到', disableEmbed: '默认为开启内嵌视频, 任意值为关闭' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'UP 主频道的合集', + maintainers: ['shininome'], + handler, +}; + +async function handler(ctx) { const uid = Number.parseInt(ctx.req.param('uid')); const sid = Number.parseInt(ctx.req.param('sid')); const disableEmbed = ctx.req.param('disableEmbed'); @@ -29,7 +48,7 @@ export default async (ctx) => { return; } - ctx.set('data', { + return { title: `${userName} 的 bilibili 合集 ${data.meta.name}`, link, description: `${userName} 的 bilibili 合集`, @@ -49,5 +68,5 @@ export default async (ctx) => { author: userName, }; }), - }); -}; + }; +} diff --git a/lib/routes/bilibili/user-fav.ts b/lib/routes/bilibili/user-fav.ts index 157827caf..235be0c3c 100644 --- a/lib/routes/bilibili/user-fav.ts +++ b/lib/routes/bilibili/user-fav.ts @@ -1,9 +1,32 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import cache from './cache'; import utils from './utils'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/user/fav/:uid/:disableEmbed?', + categories: ['new-media'], + example: '/bilibili/user/fav/2267573', + parameters: { uid: '用户 id, 可在 UP 主主页中找到', disableEmbed: '默认为开启内嵌视频, 任意值为关闭' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['space.bilibili.com/:uid', 'space.bilibili.com/:uid/favlist'], + target: '/user/fav/:uid', + }, + name: 'UP 主默认收藏夹', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { const uid = ctx.req.param('uid'); const disableEmbed = ctx.req.param('disableEmbed'); const name = await cache.getUsernameFromUID(uid); @@ -18,7 +41,7 @@ export default async (ctx) => { }); const data = response.data; - ctx.set('data', { + return { title: `${name} 的 bilibili 收藏夹`, link: `https://space.bilibili.com/${uid}/#/favlist`, description: `${name} 的 bilibili 收藏夹`, @@ -33,5 +56,5 @@ export default async (ctx) => { link: item.fav_at > utils.bvidTime && item.bvid ? `https://www.bilibili.com/video/${item.bvid}` : `https://www.bilibili.com/video/av${item.aid}`, author: item.owner.name, })), - }); -}; + }; +} diff --git a/lib/routes/bilibili/video-all.ts b/lib/routes/bilibili/video-all.ts index 20483b62d..e436d0b3e 100644 --- a/lib/routes/bilibili/video-all.ts +++ b/lib/routes/bilibili/video-all.ts @@ -1,9 +1,17 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import cache from './cache'; import utils from './utils'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/user/video-all/:uid/:disableEmbed?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const uid = ctx.req.param('uid'); const disableEmbed = ctx.req.param('disableEmbed'); const cookie = await cache.getCookie(); @@ -57,7 +65,7 @@ export default async (ctx) => { } } - ctx.set('data', { + return { title: name, link: `https://space.bilibili.com/${uid}/video`, description: `${name} 的 bilibili 所有视频`, @@ -71,5 +79,5 @@ export default async (ctx) => { author: name, comments: item.comment, })), - }); -}; + }; +} diff --git a/lib/routes/bilibili/video.ts b/lib/routes/bilibili/video.ts index e37db4777..40f6201b1 100644 --- a/lib/routes/bilibili/video.ts +++ b/lib/routes/bilibili/video.ts @@ -1,9 +1,32 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import cache from './cache'; import utils from './utils'; import logger from '@/utils/logger'; -export default async (ctx) => { +export const route: Route = { + path: '/user/video/:uid/:disableEmbed?', + categories: ['new-media'], + example: '/bilibili/user/video/2267573', + parameters: { uid: '用户 id, 可在 UP 主主页中找到', disableEmbed: '默认为开启内嵌视频, 任意值为关闭' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['space.bilibili.com/:uid'], + target: '/user/video/:uid', + }, + name: 'UP 主投稿', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { const uid = ctx.req.param('uid'); const disableEmbed = ctx.req.param('disableEmbed'); const cookie = await cache.getCookie(); @@ -30,7 +53,7 @@ export default async (ctx) => { throw new Error(`Got error code ${data.code} while fetching: ${data.message}`); } - ctx.set('data', { + return { title: `${name} 的 bilibili 空间`, link: `https://space.bilibili.com/${uid}`, description: `${name} 的 bilibili 空间`, @@ -48,5 +71,5 @@ export default async (ctx) => { author: name, comments: item.comment, })), - }); -}; + }; +} diff --git a/lib/routes/bilibili/vsearch.ts b/lib/routes/bilibili/vsearch.ts index 26cbb6e5b..ab5af43a0 100644 --- a/lib/routes/bilibili/vsearch.ts +++ b/lib/routes/bilibili/vsearch.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { config } from '@/config'; import got from '@/utils/got'; @@ -7,7 +8,25 @@ import { CookieJar } from 'tough-cookie'; const cookieJar = new CookieJar(); import { queryToBoolean } from '@/utils/readable-social'; -export default async (ctx) => { +export const route: Route = { + path: '/vsearch/:kw/:order?/:disableEmbed?/:tid?', + categories: ['new-media'], + example: '/bilibili/vsearch/RSSHub', + parameters: { kw: '检索关键字', order: '排序方式, 综合:totalrank 最多点击:click 最新发布:pubdate(缺省) 最多弹幕:dm 最多收藏:stow', disableEmbed: '默认为开启内嵌视频, 任意值为关闭', tid: '分区 id' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '视频搜索', + maintainers: ['Symty'], + handler, +}; + +async function handler(ctx) { const kw = ctx.req.param('kw'); const order = ctx.req.param('order') || 'pubdate'; const disableEmbed = queryToBoolean(ctx.req.param('disableEmbed')); @@ -40,7 +59,7 @@ export default async (ctx) => { false ); - ctx.set('data', { + return { title: `${kw} - bilibili`, link: `https://search.bilibili.com/all?keyword=${kw}&order=${order}`, description: `Result from ${kw} bilibili search, ordered by ${order}.`, @@ -69,5 +88,5 @@ export default async (ctx) => { link: item.arcurl, }; }), - }); -}; + }; +} diff --git a/lib/routes/bilibili/watchlater.ts b/lib/routes/bilibili/watchlater.ts index 5bbedf89d..1051bc3d9 100644 --- a/lib/routes/bilibili/watchlater.ts +++ b/lib/routes/bilibili/watchlater.ts @@ -1,10 +1,29 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import cache from './cache'; import { config } from '@/config'; import utils from './utils'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/watchlater/:uid/:disableEmbed?', + categories: ['new-media'], + example: '/bilibili/watchlater/2267573', + parameters: { uid: '用户 id', disableEmbed: '默认为开启内嵌视频, 任意值为关闭' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '用户稍后再看', + maintainers: ['JimenezLi'], + handler, +}; + +async function handler(ctx) { const uid = ctx.req.param('uid'); const disableEmbed = ctx.req.param('disableEmbed'); const name = await cache.getUsernameFromUID(uid); @@ -36,9 +55,9 @@ export default async (ctx) => { author: item.owner.name, })); - ctx.set('data', { + return { title: `${name} 稍后再看`, link: 'https://www.bilibili.com/watchlater#/list', item: out, - }); -}; + }; +} diff --git a/lib/routes/bilibili/weekly-recommend.ts b/lib/routes/bilibili/weekly-recommend.ts index 6ec1ee17f..a78a047b8 100644 --- a/lib/routes/bilibili/weekly-recommend.ts +++ b/lib/routes/bilibili/weekly-recommend.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/weekly/:disableEmbed?', + categories: ['new-media'], + example: '/bilibili/weekly', + parameters: { disableEmbed: '默认为开启内嵌视频, 任意值为关闭' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'B 站每周必看', + maintainers: ['ttttmr'], + handler, +}; + +async function handler(ctx) { const disableEmbed = ctx.req.param('disableEmbed'); const status_response = await got({ @@ -23,7 +42,7 @@ export default async (ctx) => { }); const data = response.data.data.list; - ctx.set('data', { + return { title: 'B站每周必看', link: 'https://www.bilibili.com/h5/weekly-recommend', description: 'B站每周必看', @@ -37,5 +56,5 @@ export default async (ctx) => { `, link: weekly_number > 60 && item.bvid ? `https://www.bilibili.com/video/${item.bvid}` : `https://www.bilibili.com/video/av${item.param}`, })), - }); -}; + }; +} diff --git a/lib/routes/bing/daily-wallpaper.ts b/lib/routes/bing/daily-wallpaper.ts index 00127ec9f..c0f274042 100644 --- a/lib/routes/bing/daily-wallpaper.ts +++ b/lib/routes/bing/daily-wallpaper.ts @@ -1,6 +1,18 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['cn.bing.com/'], + target: '', + }, + name: 'Unknown', + maintainers: ['FHYunCai'], + handler, +}; + +async function handler(ctx) { const response = await got({ method: 'get', prefixUrl: 'https://cn.bing.com', @@ -13,7 +25,7 @@ export default async (ctx) => { }, }); const data = response.data; - ctx.set('data', { + return { title: 'Bing每日壁纸', link: 'https://cn.bing.com/', item: data.images.map((item) => ({ @@ -21,5 +33,5 @@ export default async (ctx) => { description: ``, link: item.copyrightlink, })), - }); -}; + }; +} diff --git a/lib/routes/bing/search.ts b/lib/routes/bing/search.ts index 193d18e55..50c8ae943 100644 --- a/lib/routes/bing/search.ts +++ b/lib/routes/bing/search.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import parser from '@/utils/rss-parser'; import { parseDate } from '@/utils/parse-date'; import dayjs from 'dayjs'; @@ -5,7 +6,29 @@ import localizedFormat from 'dayjs/plugin/localizedFormat'; import 'dayjs/locale/zh-cn'; dayjs.extend(localizedFormat); -export default async (ctx) => { +export const route: Route = { + path: '/search/:keyword', + categories: ['other'], + example: '/bing/search/rss', + parameters: { keyword: '搜索关键词' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['cn.bing.com/'], + target: '', + }, + name: '搜索', + maintainers: ['CaoMeiYouRen'], + handler, +}; + +async function handler(ctx) { const q = ctx.req.param('keyword'); const searchParams = new URLSearchParams({ format: 'rss', @@ -14,7 +37,7 @@ export default async (ctx) => { const url = new URL('https://cn.bing.com/search'); url.search = searchParams.toString(); const data = await parser.parseURL(url.toString()); - ctx.set('data', { + return { title: data.title, link: data.link, description: data.description + ' - ' + data.copyright, @@ -24,5 +47,5 @@ export default async (ctx) => { description: e.content, pubDate: parseDate(e.pubDate, 'dddd, DD MMM YYYY HH:mm:ss [GMT]', 'zh-cn'), })), - }); -}; + }; +} diff --git a/lib/routes/biodiscover/index.ts b/lib/routes/biodiscover/index.ts index 663115611..f1593cb6a 100644 --- a/lib/routes/biodiscover/index.ts +++ b/lib/routes/biodiscover/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: '/:channel?', + radar: { + source: ['www.biodiscover.com/:channel'], + target: '/:channel', + }, + name: 'Unknown', + maintainers: ['aidistan'], + handler, +}; + +async function handler(ctx) { const channel = ctx.req.param('channel'); const listUrl = 'http://www.biodiscover.com/' + channel; const response = await got({ url: listUrl }); @@ -16,7 +28,7 @@ export default async (ctx) => { })) .toArray(); - ctx.set('data', { + return { title: '生物探索 - ' + $('.header li.sel a').text(), link: listUrl, description: $('meta[name=description]').attr('content'), @@ -41,5 +53,5 @@ export default async (ctx) => { }) ) ), - }); -}; + }; +} diff --git a/lib/routes/bioone/featured.ts b/lib/routes/bioone/featured.ts index 6ab375734..dc10eb809 100644 --- a/lib/routes/bioone/featured.ts +++ b/lib/routes/bioone/featured.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: '/featured', + categories: ['finance'], + example: '/bioone/featured', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bioone.org/'], + }, + name: 'Featured articles', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const rootUrl = 'https://bioone.org'; const response = await got(rootUrl, { https: { @@ -45,9 +67,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'Featured articles - BioOne', link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/bioone/journal.ts b/lib/routes/bioone/journal.ts index c50dd54b7..4807db9c3 100644 --- a/lib/routes/bioone/journal.ts +++ b/lib/routes/bioone/journal.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: '/journals/:journal?', + categories: ['finance'], + example: '/bioone/journals/acta-chiropterologica', + parameters: { journal: 'Journals, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bioone.org/journals/:journal', 'bioone.org/'], + target: '/journals/:journal', + }, + name: 'Journals', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const journal = ctx.req.param('journal') ?? 'acta-chiropterologica'; const rootUrl = 'https://bioone.org'; @@ -52,10 +75,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('.panel-body .row a').first().text()} - BioOne`, description: $('.panel-body .row text').first().text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/biquge/index.ts b/lib/routes/biquge/index.ts index 43ffcb8fb..2f71468e6 100644 --- a/lib/routes/biquge/index.ts +++ b/lib/routes/biquge/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'; @@ -24,7 +25,14 @@ const allowHost = new Set([ 'www.mayiwxw.com', ]); -export default async (ctx) => { +export const route: Route = { + path: '*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const rootUrl = getSubPath(ctx).split('/').slice(1, 4).join('/'); const currentUrl = getSubPath(ctx).slice(1); if (!config.feature.allow_user_supply_unsafe_domain && !allowHost.has(new URL(rootUrl).hostname)) { @@ -93,9 +101,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('meta[property="og:title"]').attr('content')} - 笔趣阁`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/bit/cs/cs.ts b/lib/routes/bit/cs/cs.ts index 3b49ece90..48d711186 100644 --- a/lib/routes/bit/cs/cs.ts +++ b/lib/routes/bit/cs/cs.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import util from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/cs', + categories: ['forecast'], + example: '/bit/cs', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['cs.bit.edu.cn/tzgg', 'cs.bit.edu.cn/'], + }, + name: '计院通知', + maintainers: ['sinofp'], + handler, +}; + +async function handler() { const link = 'https://cs.bit.edu.cn/tzgg/'; const response = await got({ method: 'get', @@ -16,10 +38,10 @@ export default async (ctx) => { const result = await util.ProcessFeed(list, cache); - ctx.set('data', { + return { title: $('title').text(), link, description: $('meta[name="description"]').attr('content'), item: result, - }); -}; + }; +} diff --git a/lib/routes/bit/jwc/jwc.ts b/lib/routes/bit/jwc/jwc.ts index 79db13ded..dcf3799ec 100644 --- a/lib/routes/bit/jwc/jwc.ts +++ b/lib/routes/bit/jwc/jwc.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import util from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/jwc', + categories: ['forecast'], + example: '/bit/jwc', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['jwc.bit.edu.cn/tzgg', 'jwc.bit.edu.cn/'], + }, + name: '教务处通知', + maintainers: ['sinofp'], + handler, +}; + +async function handler() { const link = 'https://jwc.bit.edu.cn/tzgg/'; const response = await got({ method: 'get', @@ -16,10 +38,10 @@ export default async (ctx) => { const result = await util.ProcessFeed(list, cache); - ctx.set('data', { + return { title: $('title').text(), link, description: '北京理工大学教务部', item: result, - }); -}; + }; +} diff --git a/lib/routes/bit/rszhaopin.ts b/lib/routes/bit/rszhaopin.ts index 1cd05f910..1a064e4d1 100644 --- a/lib/routes/bit/rszhaopin.ts +++ b/lib/routes/bit/rszhaopin.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: '/rszhaopin', + categories: ['forecast'], + example: '/bit/rszhaopin', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['rszhaopin.bit.edu.cn/'], + }, + name: '人才招聘', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://rszhaopin.bit.edu.cn'; const apiUrl = `${rootUrl}/ajax/ajaxService`; const currentUrl = `${rootUrl}/zp.html#/notices/0`; @@ -22,9 +44,9 @@ export default async (ctx) => { link: `${rootUrl}/zp.html#/notice/${item.id}`, })); - ctx.set('data', { + return { title: '人才招聘 - 北京理工大学', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/bit/yjs.ts b/lib/routes/bit/yjs.ts index 705fd438c..443de7337 100644 --- a/lib/routes/bit/yjs.ts +++ b/lib/routes/bit/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: '/bit/yjs', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['grd.bit.edu.cn/zsgz/zsxx/index.htm', 'grd.bit.edu.cn/'], + }, + name: '研究生院招生信息', + maintainers: ['shengmaosu'], + handler, +}; + +async function handler() { const link = 'https://grd.bit.edu.cn/zsgz/zsxx/index.htm'; const response = await got(link); const $ = load(response.data); const list = $('.tongzhigonggao 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/bitbucket/commits.ts b/lib/routes/bitbucket/commits.ts index c9e895373..b0fa94b89 100644 --- a/lib/routes/bitbucket/commits.ts +++ b/lib/routes/bitbucket/commits.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { config } from '@/config'; import queryString from 'query-string'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/commits/:workspace/:repo_slug', + categories: ['design'], + example: '/bitbucket/commits/blaze-lib/blaze', + parameters: { workspace: 'Workspace', repo_slug: 'Repository' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bitbucket.com/commits/:workspace/:repo_slug'], + }, + name: 'Commits', + maintainers: ['AuroraDysis'], + handler, +}; + +async function handler(ctx) { const workspace = ctx.req.param('workspace'); const repo_slug = ctx.req.param('repo_slug'); @@ -23,7 +45,7 @@ export default async (ctx) => { headers, }); const data = response.data.values; - ctx.set('data', { + return { allowEmpty: true, title: `Recent Commits to ${workspace}/${repo_slug}`, link: `https://bitbucket.org/${workspace}/${repo_slug}`, @@ -36,5 +58,5 @@ export default async (ctx) => { pubDate: parseDate(item.date), link: item.links.html.href, })), - }); -}; + }; +} diff --git a/lib/routes/bitbucket/tags.ts b/lib/routes/bitbucket/tags.ts index cddcd8601..d7a6ec584 100644 --- a/lib/routes/bitbucket/tags.ts +++ b/lib/routes/bitbucket/tags.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { config } from '@/config'; import queryString from 'query-string'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/tags/:workspace/:repo_slug', + categories: ['design'], + example: '/bitbucket/tags/blaze-lib/blaze', + parameters: { workspace: 'Workspace', repo_slug: 'Repository' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Tags', + maintainers: ['AuroraDysis'], + handler, +}; + +async function handler(ctx) { const workspace = ctx.req.param('workspace'); const repo_slug = ctx.req.param('repo_slug'); @@ -23,7 +42,7 @@ export default async (ctx) => { headers, }); const data = response.data.values; - ctx.set('data', { + return { allowEmpty: true, title: `Recent Tags in ${workspace}/${repo_slug}`, link: `https://bitbucket.org/${workspace}/${repo_slug}`, @@ -36,5 +55,5 @@ export default async (ctx) => { pubDate: parseDate(item.date), link: item.links.html.href, })), - }); -}; + }; +} diff --git a/lib/routes/bitmovin/blog.ts b/lib/routes/bitmovin/blog.ts index 763b74e59..846e8f017 100644 --- a/lib/routes/bitmovin/blog.ts +++ b/lib/routes/bitmovin/blog.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; const baseUrl = 'https://bitmovin.com'; -export default async (ctx) => { +export const route: Route = { + path: '/blog', + categories: ['design'], + example: '/bitmovin/blog', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bitmovin.com/blog', 'bitmovin.com/'], + }, + name: 'Blog', + maintainers: ['elxy'], + handler, +}; + +async function handler(ctx) { const apiUrl = `${baseUrl}/wp-json/wp/v2`; const { data } = await got(`${apiUrl}/posts`, { searchParams: { @@ -19,9 +41,9 @@ export default async (ctx) => { link: item.link, })); - ctx.set('data', { + return { title: 'Blog - Bitmovin', link: `${baseUrl}/blog/`, item: items, - }); -}; + }; +} diff --git a/lib/routes/bjfu/grs.ts b/lib/routes/bjfu/grs.ts index 3f83ddac7..fdee2d6d9 100644 --- a/lib/routes/bjfu/grs.ts +++ b/lib/routes/bjfu/grs.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: '/grs', + categories: ['forecast'], + example: '/bjfu/grs', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['graduate.bjfu.edu.cn/'], + }, + name: '研究生院培养动态', + maintainers: ['markmingjie'], + handler, +}; + +async function handler() { const url = 'http://graduate.bjfu.edu.cn/pygl/pydt/index.html'; const response = await got.get(url); const data = response.data; @@ -44,9 +66,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '北林研培养动态', link: url, item: result, - }); -}; + }; +} diff --git a/lib/routes/bjfu/it/index.ts b/lib/routes/bjfu/it/index.ts index 4a7d7523e..e26a4eb54 100644 --- a/lib/routes/bjfu/it/index.ts +++ b/lib/routes/bjfu/it/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 util from './utils'; import iconv from 'iconv-lite'; -export default async (ctx) => { +export const route: Route = { + path: '/it/:type', + categories: ['forecast'], + example: '/bjfu/it/xyxw', + parameters: { type: '通知类别' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['it.bjfu.edu.cn/:type/index.html'], + }, + name: '信息学院通知', + maintainers: ['wzc-blog'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); let title, path; switch (type) { @@ -43,10 +65,10 @@ export default async (ctx) => { const result = await util.ProcessFeed(base, list, cache); // 感谢@hoilc指导 - ctx.set('data', { + return { title: '北林信息 - ' + title, link: 'http://it.bjfu.edu.cn/' + path, description: '北京林业大学信息学院 - ' + title, item: result, - }); -}; + }; +} diff --git a/lib/routes/bjfu/jwc/index.ts b/lib/routes/bjfu/jwc/index.ts index 6485b73da..d1f47b442 100644 --- a/lib/routes/bjfu/jwc/index.ts +++ b/lib/routes/bjfu/jwc/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 util from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/jwc/:type', + categories: ['forecast'], + example: '/bjfu/jwc/jwkx', + parameters: { type: '通知类别' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['jwc.bjfu.edu.cn/:type/index.html'], + }, + name: '教务处通知公告', + maintainers: ['markmingjie'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); let title, path; switch (type) { @@ -42,10 +64,10 @@ export default async (ctx) => { const result = await util.ProcessFeed(base, list, cache); // 感谢@hoilc指导 - ctx.set('data', { + return { title: '北林教务处 - ' + title, link: 'http://jwc.bjfu.edu.cn/' + path, description: '北京林业大学教务处 - ' + title, item: result, - }); -}; + }; +} diff --git a/lib/routes/bjfu/kjc.ts b/lib/routes/bjfu/kjc.ts index d103fe7ca..be2bab169 100644 --- a/lib/routes/bjfu/kjc.ts +++ b/lib/routes/bjfu/kjc.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: '/kjc', + categories: ['forecast'], + example: '/bjfu/kjc', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['kyc.bjfu.edu.cn/'], + }, + name: '科技处通知公告', + maintainers: ['markmingjie'], + handler, +}; + +async function handler() { const url = 'http://kyc.bjfu.edu.cn/tztg/index.html'; const response = await got.get(url); const data = response.data; @@ -45,9 +67,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '北林科技处通知', link: url, item: result, - }); -}; + }; +} diff --git a/lib/routes/bjfu/news/index.ts b/lib/routes/bjfu/news/index.ts index 87e359c56..2a510072a 100644 --- a/lib/routes/bjfu/news/index.ts +++ b/lib/routes/bjfu/news/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 util from './utils'; import iconv from 'iconv-lite'; -export default async (ctx) => { +export const route: Route = { + path: '/news/:type', + categories: ['forecast'], + example: '/bjfu/news/lsyw', + parameters: { type: '新闻栏目' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['news.bjfu.edu.cn/:type/index.html'], + }, + name: '绿色新闻网', + maintainers: ['markmingjie'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); let title, path; switch (type) { @@ -50,10 +72,10 @@ export default async (ctx) => { const result = await util.ProcessFeed(base, list, cache); // 感谢@hoilc指导 - ctx.set('data', { + return { title: '北林新闻- ' + title, link: 'http://news.bjfu.edu.cn/' + path, description: '绿色新闻网 - ' + title, item: result, - }); -}; + }; +} diff --git a/lib/routes/bjp/apod.ts b/lib/routes/bjp/apod.ts index b880c4ccf..64359b070 100644 --- a/lib/routes/bjp/apod.ts +++ b/lib/routes/bjp/apod.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: '/apod', + categories: ['anime'], + example: '/bjp/apod', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bjp.org.cn/APOD/today.shtml', 'bjp.org.cn/APOD/list.shtml', 'bjp.org.cn/'], + }, + name: '每日一图', + maintainers: ['HenryQW'], + handler, +}; + +async function handler() { const baseUrl = 'https://www.bjp.org.cn'; const listUrl = `${baseUrl}/APOD/list.shtml`; @@ -35,10 +57,10 @@ export default async (ctx) => { }) ) ); - ctx.set('data', { + return { title: $('head title').text(), description: '探索宇宙!每天发布一张迷人宇宙的影像,以及由专业天文学家撰写的简要说明。', link: listUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/bjsk/index.ts b/lib/routes/bjsk/index.ts index 11738bd34..0d6b9ca91 100644 --- a/lib/routes/bjsk/index.ts +++ b/lib/routes/bjsk/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 baseUrl = 'https://www.bjsk.org.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/:path?', + categories: ['study'], + example: '/bjsk/newslist-1394-1474-0', + parameters: { path: '路径,默认为 `newslist-1486-0-0`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '通用', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { path = 'newslist-1486-0-0' } = ctx.req.param(); const link = `${baseUrl}/${path}.html`; const { data: response } = await got(link, { @@ -44,10 +63,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), link, image: 'https://www.bjsk.org.cn/favicon.ico', item: items, - }); -}; + }; +} diff --git a/lib/routes/bjsk/keti.ts b/lib/routes/bjsk/keti.ts index a16a6a864..e706ff745 100644 --- a/lib/routes/bjsk/keti.ts +++ b/lib/routes/bjsk/keti.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: '/keti/:id?', + categories: ['study'], + example: '/bjsk/keti', + parameters: { id: '分类 id,见下表,默认为通知公告' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['keti.bjsk.org.cn/indexAction!to_index.action', 'keti.bjsk.org.cn/'], + target: '/keti/:id', + }, + name: '基金项目管理平台', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? '402881027cbb8c6f017cbb8e17710002'; const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 100; @@ -47,9 +70,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `北京社科基金项目管理平台 - ${$('.noticetop').text()}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/bjwxdxh/index.ts b/lib/routes/bjwxdxh/index.ts index c7b6ee3d6..ca1e1512d 100644 --- a/lib/routes/bjwxdxh/index.ts +++ b/lib/routes/bjwxdxh/index.ts @@ -1,10 +1,29 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { load } from 'cheerio'; 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: '/:type?', + categories: ['study'], + example: '/bjwxdxh/114', + parameters: { type: '类型,见下表,默认为全部' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '最新资讯', + maintainers: ['Misaka13514'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'http://www.bjwxdxh.org.cn'; const type = ctx.req.param('type'); const link = type ? `${baseUrl}/news/class/?${type}.html` : `${baseUrl}/news/class/`; @@ -45,9 +64,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link, item: list, - }); -}; + }; +} diff --git a/lib/routes/bjx/huanbao.ts b/lib/routes/bjx/huanbao.ts index b6913fe97..c46b122b7 100644 --- a/lib/routes/bjx/huanbao.ts +++ b/lib/routes/bjx/huanbao.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'; @@ -13,7 +14,28 @@ const asyncPoolAll = async (...args) => { return results; }; -export default async (ctx) => { +export const route: Route = { + path: '/huanbao', + categories: ['bbs'], + example: '/bjx/huanbao', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['huanbao.bjx.com.cn/yw', 'huanbao.bjx.com.cn/'], + }, + name: '环保要闻', + maintainers: ['zsimple'], + handler, +}; + +async function handler() { const listURL = 'https://huanbao.bjx.com.cn/yw/'; const response = await got(listURL); @@ -36,12 +58,12 @@ export default async (ctx) => { (items) => fetchPage(items.link) ); - ctx.set('data', { + return { title: '北极星环保 - 环保行业垂直门户网站', link: listURL, item: items, - }); -}; + }; +} const fetchPage = (link) => cache.tryGet(link, async () => { diff --git a/lib/routes/bjx/types.ts b/lib/routes/bjx/types.ts index 09a9e4904..888f149c7 100644 --- a/lib/routes/bjx/types.ts +++ b/lib/routes/bjx/types.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: '/gf/:type', + categories: ['bbs'], + example: '/bjx/gf/sc', + parameters: { type: '分类,北极星光伏最后的`type`字段' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '光伏', + maintainers: ['Sxuet'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); const response = await got({ method: 'get', @@ -12,7 +31,7 @@ export default async (ctx) => { const $ = load(data); const typeName = $('div.box2 em:last').text(); const list = $('div.cc-list-content ul li'); - ctx.set('data', { + return { title: `北极星太阳能光大网${typeName}`, description: $('meta[name="Description"]').attr('content'), link: `https://guangfu.bjx.com.cn/${type}/`, @@ -29,5 +48,5 @@ export default async (ctx) => { }; }) .get(), - }); -}; + }; +} diff --git a/lib/routes/blizzard/news.ts b/lib/routes/blizzard/news.ts index 144683a6b..b3ea62d1a 100644 --- a/lib/routes/blizzard/news.ts +++ b/lib/routes/blizzard/news.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: '/news/:language?/:category?', + categories: ['reading'], + example: '/blizzard/news', + parameters: { language: 'Language code, see below, en-US by default', category: 'Category, see below, All News 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 category = ctx.req.param('category') || ''; const language = ctx.req.param('language') || 'en-us'; @@ -43,9 +62,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/bloomberg/authors.ts b/lib/routes/bloomberg/authors.ts index a7ec8bdd6..e2aea0840 100644 --- a/lib/routes/bloomberg/authors.ts +++ b/lib/routes/bloomberg/authors.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { load } from 'cheerio'; import got from '@/utils/got'; import rssParser from '@/utils/rss-parser'; @@ -27,7 +28,29 @@ const parseAuthorNewsList = async (slug) => { .get(); }; -export default async (ctx) => { +export const route: Route = { + path: '/authors/:id/:slug/:source?', + categories: ['other'], + example: '/bloomberg/authors/ARbTQlRLRjE/matthew-s-levine', + parameters: { id: 'Author ID, can be found in URL', slug: 'Author Slug, can be found in URL', source: 'Data source, either `api` or `rss`,`api` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.bloomberg.com/*/authors/:id/:slug', 'www.bloomberg.com/authors/:id/:slug'], + target: '/authors/:id/:slug', + }, + name: 'Authors', + maintainers: ['josh'], + handler, +}; + +async function handler(ctx) { const { id, slug, source } = ctx.req.param(); const link = `https://www.bloomberg.com/authors/${id}/${slug}`; @@ -43,10 +66,10 @@ export default async (ctx) => { const item = await asyncPoolAll(1, list, (item) => parseArticle(item)); const authorName = item.find((i) => i.author)?.author ?? 'Unknown'; - ctx.set('data', { + return { title: `Bloomberg - ${authorName}`, link, language: 'en-us', item, - }); -}; + }; +} diff --git a/lib/routes/bloomberg/index.ts b/lib/routes/bloomberg/index.ts index c62908082..d6477af22 100644 --- a/lib/routes/bloomberg/index.ts +++ b/lib/routes/bloomberg/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { rootUrl, asyncPoolAll, parseNewsList, parseArticle } from './utils'; const site_title_mapping = { '/': 'News', @@ -14,15 +15,22 @@ const site_title_mapping = { citylab: 'CityLab', }; -export default async (ctx) => { +export const route: Route = { + path: ['/:site', '/'], + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const site = ctx.req.param('site'); const currentUrl = site ? `${rootUrl}/${site}/sitemap_news.xml` : `${rootUrl}/sitemap_news.xml`; const list = await parseNewsList(currentUrl, ctx); const items = await asyncPoolAll(1, list, (item) => parseArticle(item)); - ctx.set('data', { + return { title: `Bloomberg - ${site_title_mapping[site ?? '/']}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/bluestacks/release.ts b/lib/routes/bluestacks/release.ts index 1a9f37dad..ff9170b56 100644 --- a/lib/routes/bluestacks/release.ts +++ b/lib/routes/bluestacks/release.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import cherrio from 'cheerio'; import { parseDate } from '@/utils/parse-date'; @@ -5,7 +6,28 @@ import puppeteer from '@/utils/puppeteer'; const pageUrl = 'https://support.bluestacks.com/hc/en-us/articles/360056960211-Release-Notes-BlueStacks-5'; -export default async (ctx) => { +export const route: Route = { + path: '/release/5', + categories: ['university'], + example: '/bluestacks/release/5', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bluestacks.com/hc/en-us/articles/360056960211-Release-Notes-BlueStacks-5', 'bluestacks.com/'], + }, + name: 'BlueStacks 5 Release Notes', + maintainers: ['TonyRL'], + handler, +}; + +async function handler() { const browser = await puppeteer(); const page = await browser.newPage(); await page.setRequestInterception(true); @@ -55,11 +77,11 @@ export default async (ctx) => { await browser.close(); - ctx.set('data', { + return { title: $('.article__title').text().trim(), description: $('meta[name=description]').text().trim(), link: pageUrl, image: $('link[rel="shortcut icon"]').attr('href'), item: items, - }); -}; + }; +} diff --git a/lib/routes/bmkg/earthquake.ts b/lib/routes/bmkg/earthquake.ts index 19d6801d0..8a0f156a9 100644 --- a/lib/routes/bmkg/earthquake.ts +++ b/lib/routes/bmkg/earthquake.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: '/earthquake', + categories: ['travel'], + example: '/bmkg/earthquake', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bmkg.go.id/', 'bmkg.go.id/gempabumi-terkini.html'], + }, + name: 'Recent Earthquakes', + maintainers: ['Shinanory'], + handler, +}; + +async function handler() { const url = 'https://www.bmkg.go.id/gempabumi-terkini.html'; const response = await got(url); const $ = load(response.data); @@ -19,11 +41,11 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('title').text(), link: url, description: '印尼气象气候和地球物理局 最近的地震(M ≥ 5.0) | BMKG earthquake', item: items, language: 'in', - }); -}; + }; +} diff --git a/lib/routes/bmkg/news.ts b/lib/routes/bmkg/news.ts index f3e717ba8..cba8510a9 100644 --- a/lib/routes/bmkg/news.ts +++ b/lib/routes/bmkg/news.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: '/news', + categories: ['travel'], + example: '/bmkg/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bmkg.go.id/', 'bmkg.go.id/berita'], + }, + name: 'News', + maintainers: ['Shinanory'], + handler, +}; + +async function handler() { const url = 'https://www.bmkg.go.id'; const response = await got(url); const $ = load(response.data); @@ -33,11 +55,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: url, description: '印尼气象气候和地球物理局 新闻 | BMKG news', item: items, language: 'in', - }); -}; + }; +} diff --git a/lib/routes/bnu/bs.ts b/lib/routes/bnu/bs.ts index 5e23aec8e..8f83ea6b1 100644 --- a/lib/routes/bnu/bs.ts +++ b/lib/routes/bnu/bs.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: '/bs/:category?', + categories: ['forecast'], + example: '/bnu/bs', + parameters: { category: '分类,见下表,默认为学院新闻' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bs.bnu.edu.cn/:category/index.html'], + target: '/bs/:category', + }, + name: '经济与工商管理学院', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'xw'; const rootUrl = 'http://bs.bnu.edu.cn'; @@ -45,9 +68,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('.right-c-title').text()} - ${$('title').text()}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/bnu/dwxgb.ts b/lib/routes/bnu/dwxgb.ts index 78a5f410e..6529c7971 100644 --- a/lib/routes/bnu/dwxgb.ts +++ b/lib/routes/bnu/dwxgb.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: '/dwxgb/:category/:type', + categories: ['forecast'], + example: '/bnu/dwxgb/xwzx/tzgg', + parameters: { category: '大分类', type: '子分类,例子如下' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['dwxgb.bnu.edu.cn/:category/:type/index.html'], + }, + name: '党委学生工作部', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const { category, type } = ctx.req.param(); const rootUrl = 'https://dwxgb.bnu.edu.cn'; @@ -45,9 +67,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('div.breadcrumb1 > a:nth-child(3)').text()} - ${$('div.breadcrumb1 > a:nth-child(4)').text()}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/bnu/fdy.ts b/lib/routes/bnu/fdy.ts index 58fe3a6d4..0d52179a6 100644 --- a/lib/routes/bnu/fdy.ts +++ b/lib/routes/bnu/fdy.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: '/fdy/:path{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const baseUrl = 'http://fdy.bnu.edu.cn'; const { path = 'tzgg' } = ctx.req.param(); const link = `${baseUrl}/${path}/index.htm`; @@ -34,9 +42,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), link, item: items, - }); -}; + }; +} diff --git a/lib/routes/bnu/lib.ts b/lib/routes/bnu/lib.ts index 56ac52358..1c47e2a7d 100644 --- a/lib/routes/bnu/lib.ts +++ b/lib/routes/bnu/lib.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: '/lib/:category?', + radar: { + source: ['www.lib.bnu.edu.cn/:category/index.htm'], + target: '/lib/:category', + }, + name: 'Unknown', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'http://www.lib.bnu.edu.cn'; const { category = 'zydt' } = ctx.req.param(); const link = `${baseUrl}/${category}/index.htm`; @@ -33,9 +45,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), link, item: items, - }); -}; + }; +} diff --git a/lib/routes/boc/whpj.ts b/lib/routes/boc/whpj.ts index 308dc2f1d..e48ce02ca 100644 --- a/lib/routes/boc/whpj.ts +++ b/lib/routes/boc/whpj.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: '/whpj/:format?', + categories: ['other'], + example: '/boc/whpj/zs?filter_title=%E8%8B%B1%E9%95%91', + parameters: { format: '输出的标题格式,默认为标题 + 所有价格。短格式仅包含货币名称。' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['boc.cn/sourcedb/whpj', 'boc.cn/'], + target: '/whpj', + }, + name: '外汇牌价', + maintainers: ['LogicJake', 'HenryQW'], + handler, +}; + +async function handler(ctx) { const link = 'https://www.boc.cn/sourcedb/whpj/'; const response = await got(link); const $ = load(response.data); @@ -92,9 +115,9 @@ export default async (ctx) => { return info; }); - ctx.set('data', { + return { title: '中国银行外汇牌价', link, item: out, - }); -}; + }; +} diff --git a/lib/routes/bookfere/category.ts b/lib/routes/bookfere/category.ts index 5a9ec5333..bce325bf4 100644 --- a/lib/routes/bookfere/category.ts +++ b/lib/routes/bookfere/category.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: '/:category', + categories: ['government'], + example: '/bookfere/skills', + parameters: { category: '分类名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '分类', + maintainers: ['OdinZhang'], + handler, +}; + +async function handler(ctx) { const url = 'https://bookfere.com/category/' + ctx.req.param('category'); const response = await got({ method: 'get', @@ -14,7 +33,7 @@ export default async (ctx) => { const $ = load(data); const list = $('main div div section'); - ctx.set('data', { + return { title: $('head title').text(), link: url, item: @@ -32,5 +51,5 @@ export default async (ctx) => { }; }) .get(), - }); -}; + }; +} diff --git a/lib/routes/booru/mmda.ts b/lib/routes/booru/mmda.ts index 1997c6f5e..d0bcf4ac0 100644 --- a/lib/routes/booru/mmda.ts +++ b/lib/routes/booru/mmda.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import queryString from 'query-string'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/mmda/tags/:tags?', + categories: ['anime'], + example: '/booru/mmda/tags/full_body+blue_eyes', + parameters: { tags: '标签,多个标签使用空格 ` ` 或者 `%20` 连接,如需根据作者查询则在 `user:` 后接上作者名,如:`user:xxxx`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'MMDArchive 标签查询', + maintainers: ['N78Wy'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://mmda.booru.org'; const tags = ctx.req.param('tags'); @@ -39,9 +58,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: tags, link: `${baseUrl}/index.php?${query}`, item: list, - }); -}; + }; +} diff --git a/lib/routes/bossdesign/index.ts b/lib/routes/bossdesign/index.ts index 3e2577f35..3bf928bc6 100644 --- a/lib/routes/bossdesign/index.ts +++ b/lib/routes/bossdesign/index.ts @@ -1,8 +1,27 @@ +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: '/:category?', + categories: ['live'], + example: '/bossdesign', + parameters: { category: '分类,可在对应分类页 URL 中找到,留空为全部' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '分类', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category'); const limit = Number.parseInt(ctx.req.query('limit'), 10) || undefined; const baseUrl = 'https://www.bossdesign.cn'; @@ -32,11 +51,11 @@ export default async (ctx) => { category: [...new Set([...item._embedded['wp:term'][0].map((item) => item.name), ...item._embedded['wp:term'][1].map((item) => item.name)])], })); - ctx.set('data', { + return { title: currentCategory?.name ? `${currentCategory.name} | Boss设计` : 'Boss设计 | 收集国外设计素材网站的资源平台。', description: currentCategory?.description ?? 'Boss设计-收集国外设计素材网站的资源平台。专注于收集国外设计素材和国外设计网站,以及超实用的设计师神器,只为设计初学者和设计师提供海量的资源平台。..', image: currentCategory?.cover ?? `${baseUrl}/wp-content/themes/pinghsu/images/Bossdesign-ico.ico`, link: currentCategory?.link ?? baseUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/brave/latest.ts b/lib/routes/brave/latest.ts index be94b9f5f..65dd43466 100644 --- a/lib/routes/brave/latest.ts +++ b/lib/routes/brave/latest.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: '/latest', + categories: ['university'], + example: '/brave/latest', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['brave.com/latest', 'brave.com/'], + }, + name: 'Release Notes', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://brave.com'; const currentUrl = `${rootUrl}/latest`; @@ -32,9 +54,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/brooklynmuseum/exhibitions.ts b/lib/routes/brooklynmuseum/exhibitions.ts index 3b6bc3215..16bc8bfb9 100644 --- a/lib/routes/brooklynmuseum/exhibitions.ts +++ b/lib/routes/brooklynmuseum/exhibitions.ts @@ -1,6 +1,25 @@ +import { Route } from '@/types'; import buildData from '@/utils/common-config'; -export default async (ctx) => { +export const route: Route = { + path: '/exhibitions/:state?', + categories: ['shopping'], + example: '/brooklynmuseum/exhibitions', + parameters: { state: '展览进行的状态:`current` 对应展览当前正在进行,`past` 对应过去的展览,`upcoming` 对应即将举办的展览,默认为 `current`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Exhibitions', + maintainers: [], + handler, +}; + +async function handler(ctx) { let link; const state = ctx.req.param('state'); @@ -27,4 +46,4 @@ export default async (ctx) => { }, }) ); -}; +} diff --git a/lib/routes/bse/index.ts b/lib/routes/bse/index.ts index 6b79c0997..2dbd07231 100644 --- a/lib/routes/bse/index.ts +++ b/lib/routes/bse/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; @@ -119,7 +120,28 @@ const nodes = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/:category?/:keyword?', + categories: ['other'], + example: '/bse', + parameters: { category: '分类,见下表,默认为本所要闻', keyword: '关键字,默认为空' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bse.cn/'], + }, + name: '栏目', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'important_news'; const keyword = ctx.req.param('keyword') ?? ''; @@ -163,10 +185,10 @@ export default async (ctx) => { break; } - ctx.set('data', { + return { title: `${nodes[category].title} - 北京证券交易所`, link: `${rootUrl}${nodes[category].url}`, item: items, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/bsky/keyword.ts b/lib/routes/bsky/keyword.ts index 2fc29d2a1..427e08eb6 100644 --- a/lib/routes/bsky/keyword.ts +++ b/lib/routes/bsky/keyword.ts @@ -1,6 +1,25 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/keyword/:keyword', + categories: ['new-media'], + example: '/bsky/keyword/hello', + parameters: { keyword: 'N' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Keywords', + maintainers: ['untitaker'], + handler, +}; + +async function handler(ctx) { const keyword = ctx.req.param('keyword'); const apiLink = `https://search.bsky.social/search/posts?q=${encodeURIComponent(keyword)}`; @@ -14,9 +33,9 @@ export default async (ctx) => { author: item.user.handle, })); - ctx.set('data', { + return { title: `Bluesky Keyword - ${keyword}`, link: `https://bsky.app/search?q=${encodeURIComponent(keyword)}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/bsky/posts.ts b/lib/routes/bsky/posts.ts index 40d5111f8..c2c4a0c4c 100644 --- a/lib/routes/bsky/posts.ts +++ b/lib/routes/bsky/posts.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 { resolveHandle, getProfile, getAuthorFeed } from './utils'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/profile/:handle', + categories: ['new-media'], + example: '/bsky/profile/bsky.app', + parameters: { handle: 'User handle, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bsky.app/profile/:handle'], + }, + name: 'Post', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const handle = ctx.req.param('handle'); const DID = await resolveHandle(handle, cache.tryGet); const profile = await getProfile(DID, cache.tryGet); @@ -28,7 +50,7 @@ export default async (ctx) => { comments: post.replyCount, })); - ctx.set('data', { + return { title: `${profile.displayName} (@${profile.handle}) — Bluesky`, description: profile.description?.replaceAll('\n', ' '), link: `https://bsky.app/profile/${profile.handle}`, @@ -36,11 +58,11 @@ export default async (ctx) => { icon: profile.avatar, logo: profile.avatar, item: items, - }); + }; ctx.set('json', { DID, profile, authorFeed, }); -}; +} diff --git a/lib/routes/btzj/index.ts b/lib/routes/btzj/index.ts index 45048867c..36f6d3286 100644 --- a/lib/routes/btzj/index.ts +++ b/lib/routes/btzj/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -11,7 +12,28 @@ import * as path from 'node:path'; import { config } from '@/config'; const allowDomain = new Set(['2btjia.com', '88btbtt.com', 'btbtt15.com', 'btbtt20.com']); -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + categories: ['picture'], + example: '/btzj', + parameters: { category: '分类,可在对应分类页 URL 中找到,默认为首页' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['btbtt20.com/'], + }, + name: '分类', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { let category = ctx.req.param('category') ?? ''; let domain = ctx.req.query('domain') ?? 'btbtt15.com'; if (!config.feature.allow_user_supply_unsafe_domain && !allowDomain.has(new URL(`http://${domain}/`).hostname)) { @@ -94,7 +116,7 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('#menu, #threadtype') .find('.checked') .toArray() @@ -103,5 +125,5 @@ export default async (ctx) => { .join('|')} - BT之家`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/buaa/news/index.ts b/lib/routes/buaa/news/index.ts index 33d976d91..47ca6ab47 100644 --- a/lib/routes/buaa/news/index.ts +++ b/lib/routes/buaa/news/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: '/news/:type', + categories: ['forecast'], + example: '/buaa/news/zhxw', + parameters: { type: '新闻版块' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '新闻网', + maintainers: ['AlanDecode'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://news.buaa.edu.cn'; const type = ctx.req.param('type'); @@ -38,10 +57,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `北航新闻 - ${title}`, link, description: `北京航空航天大学新闻网 - ${title}`, item: result, - }); -}; + }; +} diff --git a/lib/routes/buaa/sme.ts b/lib/routes/buaa/sme.ts index 9f7be9e85..c4b1a4474 100755 --- a/lib/routes/buaa/sme.ts +++ b/lib/routes/buaa/sme.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -6,19 +7,26 @@ import timezone from '@/utils/timezone'; const BASE_URL = 'http://www.sme.buaa.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/sme/:path{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { path = 'tzgg' } = ctx.req.param(); const url = `${BASE_URL}/${path}.htm`; const { title, list } = await getList(url); - ctx.set('data', { + return { // 源标题 title, // 源链接 link: url, // 源文章 item: await getItems(list), - }); -}; + }; +} async function getList(url) { const { data } = await got(url); diff --git a/lib/routes/bulianglin/bulianglin.ts b/lib/routes/bulianglin/bulianglin.ts index 05a5904a1..d7fb0c831 100644 --- a/lib/routes/bulianglin/bulianglin.ts +++ b/lib/routes/bulianglin/bulianglin.ts @@ -1,8 +1,20 @@ +import { Route } from '@/types'; import { parseDate } from '@/utils/parse-date'; import got from '@/utils/got'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['bulianglin.com/'], + target: '', + }, + name: 'Unknown', + maintainers: ['cnkmmk'], + handler, +}; + +async function handler() { const url = 'https://bulianglin.com/'; const response = await got({ method: 'get', url }); const $ = load(response.data); @@ -24,9 +36,9 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title: '不良林', link: url, item: list, - }); -}; + }; +} diff --git a/lib/routes/bupt/rczp.ts b/lib/routes/bupt/rczp.ts index 80f48cb71..430a81c31 100644 --- a/lib/routes/bupt/rczp.ts +++ b/lib/routes/bupt/rczp.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: '/rczp', + categories: ['forecast'], + example: '/bupt/rczp', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bupt.edu.cn/'], + }, + name: '人才招聘', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://www.bupt.edu.cn'; const currentUrl = `${rootUrl}/rczp.htm`; @@ -44,9 +66,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/byteclicks/index.ts b/lib/routes/byteclicks/index.ts index f8d94c607..c88c9733b 100644 --- a/lib/routes/byteclicks/index.ts +++ b/lib/routes/byteclicks/index.ts @@ -1,8 +1,20 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseItem } from './utils'; const baseUrl = 'https://byteclicks.com'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['byteclicks.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.parseInt(ctx.req.query('limit')) : 100, @@ -11,12 +23,12 @@ export default async (ctx) => { const items = parseItem(data); - ctx.set('data', { + return { title: '字节点击 - 聚合全球优质资源,跟踪世界前沿科技', description: 'byteclicks.com 最专业的前沿科技网站。聚合全球优质资源,跟踪世界前沿科技,精选推荐一些很棒的互联网好资源好工具好产品。寻找有前景好项目、找论文、找报告、找数据、找课程、找电子书上byteclicks!byteclicks.com是投资人、科研学者、学生每天必看的网站。', image: 'https://byteclicks.com/wp-content/themes/RK-Blogger/images/wbolt.ico', link: baseUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/byteclicks/tag.ts b/lib/routes/byteclicks/tag.ts index 51bf3d4b6..c9a0afddf 100644 --- a/lib/routes/byteclicks/tag.ts +++ b/lib/routes/byteclicks/tag.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseItem } from './utils'; const baseUrl = 'https://byteclicks.com'; -export default async (ctx) => { +export const route: Route = { + path: '/tag/:tag', + categories: ['traditional-media'], + example: '/byteclicks/tag/人工智能', + parameters: { tag: '标签,可在URL中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['byteclicks.com/tag/:tag'], + }, + name: '标签', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const tag = ctx.req.param('tag'); const { data: search } = await got(`${baseUrl}/wp-json/wp/v2/tags`, { searchParams: { @@ -21,10 +43,10 @@ export default async (ctx) => { const items = parseItem(data); - ctx.set('data', { + return { title: `${tagData.name} - 字节点击`, image: 'https://byteclicks.com/wp-content/themes/RK-Blogger/images/wbolt.ico', link: tagData.link, item: items, - }); -}; + }; +} diff --git a/lib/routes/bytes/bytes.ts b/lib/routes/bytes/bytes.ts index 739a5c311..6c6736b72 100644 --- a/lib/routes/bytes/bytes.ts +++ b/lib/routes/bytes/bytes.ts @@ -1,9 +1,21 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; const currentURL = 'https://bytes.dev/archives'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['bytes.dev/archives', '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: `/archives/${item.slug}`, })); - ctx.set('data', { + return { title: 'bytes.dev', description: 'Your weekly dose of JS', link: currentURL, item: items, - }); -}; + }; +} diff --git a/lib/routes/c114/roll.ts b/lib/routes/c114/roll.ts index f25a0ba2c..672de3e0c 100644 --- a/lib/routes/c114/roll.ts +++ b/lib/routes/c114/roll.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 iconv from 'iconv-lite'; -export default async (ctx) => { +export const route: Route = { + path: '/roll', + categories: ['traditional-media'], + example: '/c114/roll', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['c114.com.cn/news/roll.asp', 'c114.com.cn/'], + }, + name: '滚动新闻', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const rootUrl = 'https://www.c114.com.cn'; const currentUrl = `${rootUrl}/news/roll.asp`; @@ -50,9 +72,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/caai/index.ts b/lib/routes/caai/index.ts index dbc686778..30a15ee0d 100644 --- a/lib/routes/caai/index.ts +++ b/lib/routes/caai/index.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import utils from './utils'; import got from '@/utils/got'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/:caty', + categories: ['journal'], + example: '/caai/45', + parameters: { caty: '分类 ID,可在 URL 找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '学会动态', + maintainers: ['tudou027'], + handler, +}; + +async function handler(ctx) { const base = utils.urlBase(ctx.req.param('caty')); const res = await got(base); const info = utils.fetchAllArticles(res.data); @@ -15,9 +34,9 @@ export default async (ctx) => { info, }); - ctx.set('data', { + return { title: '中国人工智能学会 - ' + $('.article-list h1').text(), link: base, item: details, - }); -}; + }; +} diff --git a/lib/routes/caam/index.ts b/lib/routes/caam/index.ts index 1c91640fc..f2ee231f2 100644 --- a/lib/routes/caam/index.ts +++ b/lib/routes/caam/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 { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + name: 'Unknown', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const { category = '1' } = 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 subtitle = $('div.topMeuns ul li a').last().text(); const image = new URL('images/header-back-7.png', rootUrl).href; - ctx.set('data', { + return { item: items, title: `${author} - ${subtitle}`, link: currentUrl, @@ -61,5 +69,5 @@ export default async (ctx) => { image, subtitle, author, - }); -}; + }; +} diff --git a/lib/routes/caareviews/book.ts b/lib/routes/caareviews/book.ts index 55cc0599d..652526b7f 100644 --- a/lib/routes/caareviews/book.ts +++ b/lib/routes/caareviews/book.ts @@ -1,14 +1,36 @@ +import { Route } from '@/types'; import { rootUrl, getList, getItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/book', + categories: ['finance'], + example: '/caareviews/book', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['caareviews.org/reviews/book'], + }, + name: 'Book Reviews', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const url = `${rootUrl}/reviews/book`; const list = await getList(url); const items = await getItems(ctx, list); - ctx.set('data', { + return { title: 'Book Reviews', link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/caareviews/essay.ts b/lib/routes/caareviews/essay.ts index e91432300..436904ba6 100644 --- a/lib/routes/caareviews/essay.ts +++ b/lib/routes/caareviews/essay.ts @@ -1,14 +1,36 @@ +import { Route } from '@/types'; import { rootUrl, getList, getItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/essay', + categories: ['finance'], + example: '/caareviews/essay', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['caareviews.org/reviews/essay'], + }, + name: 'Essays', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const url = `${rootUrl}/reviews/essay`; const list = await getList(url); const items = await getItems(ctx, list); - ctx.set('data', { + return { title: 'Essays', link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/caareviews/exhibition.ts b/lib/routes/caareviews/exhibition.ts index dabc7b05f..252f328a6 100644 --- a/lib/routes/caareviews/exhibition.ts +++ b/lib/routes/caareviews/exhibition.ts @@ -1,14 +1,36 @@ +import { Route } from '@/types'; import { rootUrl, getList, getItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/exhibition', + categories: ['finance'], + example: '/caareviews/exhibition', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['caareviews.org/reviews/exhibition'], + }, + name: 'Exhibition Reviews', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const url = `${rootUrl}/reviews/exhibition`; const list = await getList(url); const items = await getItems(ctx, list); - ctx.set('data', { + return { title: 'Exhibition Reviews', link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/cahkms/index.ts b/lib/routes/cahkms/index.ts index a49e5b5c2..1e78dfc51 100644 --- a/lib/routes/cahkms/index.ts +++ b/lib/routes/cahkms/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -22,7 +23,28 @@ const titles = { 11: '研究资讯', }; -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + categories: ['traditional-media'], + example: '/cahkms', + parameters: { category: '分类,见下表,默认为重要新闻' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['cahkms.org/'], + }, + name: '分类', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? '03'; const rootUrl = 'http://www.cahkms.org'; @@ -65,9 +87,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${titles[category]} - 全国港澳研究会`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/caijing/roll.ts b/lib/routes/caijing/roll.ts index a23b0f79a..23eba5390 100644 --- a/lib/routes/caijing/roll.ts +++ b/lib/routes/caijing/roll.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: '/roll', + categories: ['other'], + example: '/caijing/roll', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['roll.caijing.com.cn/index1.html', 'roll.caijing.com.cn/'], + }, + name: '滚动新闻', + maintainers: ['TonyRL'], + handler, +}; + +async function handler() { const baseUrl = 'https://roll.caijing.com.cn'; const response = await got(`${baseUrl}/ajax_lists.php`, { searchParams: { @@ -38,10 +60,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '滚动新闻-财经网', image: 'https://www.caijing.com.cn/favicon.ico', link: response.url, item: items, - }); -}; + }; +} diff --git a/lib/routes/caixin/article.ts b/lib/routes/caixin/article.ts index 6afba4c00..351c063c4 100644 --- a/lib/routes/caixin/article.ts +++ b/lib/routes/caixin/article.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 { parseArticle } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/article', + categories: ['bbs'], + example: '/caixin/article', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: true, + supportScihub: false, + }, + radar: { + source: ['caixin.com/'], + }, + name: '首页新闻', + maintainers: ['EsuRt'], + handler, +}; + +async function handler() { const { data: response } = await got('https://mapiv5.caixin.com/m/api/getWapIndexListByPage'); const list = response.data.list.map((item) => ({ @@ -19,10 +41,10 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => parseArticle(item, cache.tryGet))); - ctx.set('data', { + return { title: '财新网 - 首页', link: 'https://www.caixin.com', description: '财新网 - 首页', item: items, - }); -}; + }; +} diff --git a/lib/routes/caixin/blog.ts b/lib/routes/caixin/blog.ts index a91a4d495..3f5f3d154 100644 --- a/lib/routes/caixin/blog.ts +++ b/lib/routes/caixin/blog.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 { isValidHost } from '@/utils/valid-host'; import { parseDate } from '@/utils/parse-date'; import { parseBlogArticle } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/blog/:column?', + categories: ['programming'], + example: '/caixin/blog/zhangwuchang', + parameters: { column: '博客名称,可在博客主页的 URL 找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '用户博客', + maintainers: [], + handler, +}; + +async function handler(ctx) { const column = ctx.req.param('column'); const { limit = 20 } = ctx.req.query(); if (column) { @@ -48,13 +67,13 @@ export default async (ctx) => { const items = await Promise.all(posts.map((item) => parseBlogArticle(item, cache.tryGet))); - ctx.set('data', { + return { title: `财新博客 - ${authorName}`, link, description: introduce, image: avatar, item: items, - }); + }; } else { const { data } = await got('https://blog.caixin.com/blog-api/post/index', { searchParams: { @@ -71,12 +90,12 @@ export default async (ctx) => { })); const items = await Promise.all(posts.map((item) => parseBlogArticle(item, cache.tryGet))); - ctx.set('data', { + return { title: `财新博客 - 全部`, link: 'https://blog.caixin.com', // description: introduce, // image: avatar, item: items, - }); + }; } -}; +} diff --git a/lib/routes/caixin/category.ts b/lib/routes/caixin/category.ts index 6c4cdd461..fb74492dd 100644 --- a/lib/routes/caixin/category.ts +++ b/lib/routes/caixin/category.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 { parseArticle } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/:column/:category', + categories: ['bbs'], + example: '/caixin/finance/regulation', + parameters: { column: '栏目名', category: '栏目下的子分类名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: true, + supportScihub: false, + }, + name: '新闻分类', + maintainers: ['idealclover'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category'); const column = ctx.req.param('column'); const url = `https://${column}.caixin.com/${category}`; @@ -48,10 +67,10 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => parseArticle(item, cache.tryGet))); - ctx.set('data', { + return { title, link: url, description: '财新网 - 提供财经新闻及资讯服务', item: items, - }); -}; + }; +} diff --git a/lib/routes/caixin/database.ts b/lib/routes/caixin/database.ts index 6e49cd0bf..8bc700413 100644 --- a/lib/routes/caixin/database.ts +++ b/lib/routes/caixin/database.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 { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/database', + categories: ['bbs'], + example: '/caixin/database', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['k.caixin.com/web', 'k.caixin.com/'], + }, + name: '财新数据通', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://database.caixin.com'; const currentUrl = `${rootUrl}/news/`; const response = await got(currentUrl); @@ -44,9 +66,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '财新数据通 - 专享资讯', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/caixin/k.ts b/lib/routes/caixin/k.ts index 1b096eff9..c61f289d4 100644 --- a/lib/routes/caixin/k.ts +++ b/lib/routes/caixin/k.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: '/k', + categories: ['bbs'], + example: '/caixin/k', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: true, + supportScihub: false, + }, + name: '财新一线', + maintainers: ['boypt'], + handler, +}; + +async function handler() { const response = await got('https://k.caixin.com/app/v1/list', { searchParams: { productIdList: '8,28', @@ -31,10 +50,10 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: '财新网 - 财新一线新闻', link: 'https://k.caixin.com/web/', description: '财新网 - 财新一线新闻', item: items, - }); -}; + }; +} diff --git a/lib/routes/caixin/latest.ts b/lib/routes/caixin/latest.ts index d47b2b40a..495e2643e 100644 --- a/lib/routes/caixin/latest.ts +++ b/lib/routes/caixin/latest.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 { load } from 'cheerio'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/latest', + categories: ['bbs'], + example: '/caixin/latest', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['caixin.com/'], + }, + name: '最新文章', + maintainers: ['tpnonthealps'], + handler, +}; + +async function handler() { const { data } = await got('https://gateway.caixin.com/api/dataplatform/scroll/index').json(); const list = data.articleList @@ -41,9 +63,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '财新网 - 最新文章', link: 'https://www.caixin.com/', item: rss, - }); -}; + }; +} diff --git a/lib/routes/caixin/weekly.ts b/lib/routes/caixin/weekly.ts index aac758fca..b836799af 100644 --- a/lib/routes/caixin/weekly.ts +++ b/lib/routes/caixin/weekly.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: '/weekly', + categories: ['bbs'], + example: '/caixin/weekly', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['weekly.caixin.com/', 'weekly.caixin.com/*'], + }, + name: '财新周刊', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const link = 'https://weekly.caixin.com'; const { data: response } = await got(link); @@ -50,12 +72,12 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title') .text() .replace(/_财新周刊频道_财新网$/, '') .trim(), link, item: items, - }); -}; + }; +} diff --git a/lib/routes/caixinglobal/latest.ts b/lib/routes/caixinglobal/latest.ts index 3cf97dd4a..6337e01b3 100644 --- a/lib/routes/caixinglobal/latest.ts +++ b/lib/routes/caixinglobal/latest.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: '/latest', + categories: ['bbs'], + example: '/caixinglobal/latest', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['caixinglobal.com/news', 'caixinglobal.com/'], + }, + name: 'Latest News', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { data } = await got('https://gateway.caixin.com/api/extapi/homeInterface.jsp', { searchParams: { subject: '100990318;100990314;100990311', @@ -55,11 +77,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'The Latest Top Headlines on China - Caixin Global', description: 'The latest headlines on China finance, companies, politics, international affairs and other China-related issues from around the world. Caixin Global', language: 'en', link: 'https://www.caixinglobal.com/news/', item: items, - }); -}; + }; +} diff --git a/lib/routes/camchina/index.ts b/lib/routes/camchina/index.ts index b91166d6a..64c7cbeba 100644 --- a/lib/routes/camchina/index.ts +++ b/lib/routes/camchina/index.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: '/:id?', + categories: ['journal'], + example: '/camchina', + parameters: { id: '分类,见下表,默认为 1,即新闻' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['cste.org.cn/categories/:id', 'cste.org.cn/'], + }, + name: '栏目', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? '1'; const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 50; @@ -45,9 +67,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `中国管理现代化研究会 - ${$('.title_red').text()}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/cankaoxiaoxi/index.ts b/lib/routes/cankaoxiaoxi/index.ts index 502ca8509..67a097b29 100644 --- a/lib/routes/cankaoxiaoxi/index.ts +++ b/lib/routes/cankaoxiaoxi/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: ['/column/:id?', '/:id?'], + categories: ['bbs'], + example: '/cankaoxiaoxi/column/diyi', + parameters: { id: '栏目 id,默认为 `diyi`,即第一关注' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '栏目', + maintainers: ['yuxinliu-alex', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? 'diyi'; const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 50; @@ -62,11 +81,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `参考消息 - ${channelResponse.data.name}`, link: currentUrl, description: '参考消息', language: 'zh-cn', item: items, - }); -}; + }; +} diff --git a/lib/routes/cartoonmad/comic.ts b/lib/routes/cartoonmad/comic.ts index 305b86990..2bc4fa720 100644 --- a/lib/routes/cartoonmad/comic.ts +++ b/lib/routes/cartoonmad/comic.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -33,7 +34,28 @@ const getChapters = (id, list, tryGet) => ) ); -export default async (ctx) => { +export const route: Route = { + path: '/comic/:id', + categories: ['program-update'], + example: '/cartoonmad/comic/5827', + parameters: { id: '漫画ID' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['cartoonmad.com/comic/:id'], + }, + name: '漫画更新', + maintainers: ['KellyHwong'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const link = `${baseUrl}/comic/${id}`; @@ -65,10 +87,10 @@ export default async (ctx) => { const chapters = await getChapters(id, list, cache.tryGet); - ctx.set('data', { + return { title: $('head title').text(), link, description: bookIntro, item: chapters, - }); -}; + }; +} diff --git a/lib/routes/cas/cg/index.ts b/lib/routes/cas/cg/index.ts index a0a40bb65..25afaf69b 100644 --- a/lib/routes/cas/cg/index.ts +++ b/lib/routes/cas/cg/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'; import timezone from '@/utils/timezone'; -export default async (ctx) => { +export const route: Route = { + path: '/cg/:caty?', + categories: ['forecast'], + example: '/cas/cg/cgzhld', + parameters: { caty: '分类,见下表,默认为工作动态' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.cas.cn/cg/:caty?'], + }, + name: '成果转化', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const caty = ctx.req.param('caty') || 'zh'; const rootUrl = 'https://www.cas.cn'; @@ -45,9 +67,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text().replace('----', ' - '), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/cas/genetics/index.ts b/lib/routes/cas/genetics/index.ts index b41f515a7..25c29be56 100644 --- a/lib/routes/cas/genetics/index.ts +++ b/lib/routes/cas/genetics/index.ts @@ -1,10 +1,18 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; const baseUrl = 'https://genetics.cas.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/genetics/:path{.+}', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const path = ctx.req.param('path'); const currentUrl = `${baseUrl}/${path}/`; @@ -55,9 +63,9 @@ export default async (ctx) => { }); } - ctx.set('data', { + return { title: $('head title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/cas/ia/yjs.ts b/lib/routes/cas/ia/yjs.ts index 97c771694..a6a710964 100644 --- a/lib/routes/cas/ia/yjs.ts +++ b/lib/routes/cas/ia/yjs.ts @@ -1,13 +1,35 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/ia/yjs', + categories: ['forecast'], + example: '/cas/ia/yjs', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.ia.cas.cn/yjsjy/zs/sszs', 'www.ia.cas.cn/'], + }, + name: '自动化所', + maintainers: ['shengmaosu'], + handler, +}; + +async function handler() { const link = 'http://www.ia.cas.cn/yjsjy/zs/sszs/'; const response = await got(link); const $ = load(response.data); const list = $('.col-md-9 li'); - ctx.set('data', { + return { title: '中科院自动化所', link, description: '中科院自动化所通知公告', @@ -21,5 +43,5 @@ export default async (ctx) => { link: item.find('li a').attr('href'), }; }), - }); -}; + }; +} diff --git a/lib/routes/cas/iee/kydt.ts b/lib/routes/cas/iee/kydt.ts index a5bbeeddf..9d6810ae7 100644 --- a/lib/routes/cas/iee/kydt.ts +++ b/lib/routes/cas/iee/kydt.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: '/iee/kydt', + categories: ['forecast'], + example: '/cas/iee/kydt', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.iee.cas.cn/xwzx/kydt', 'www.iee.cas.cn/'], + }, + name: '电工研究所 科研动态', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'http://www.iee.cas.cn/xwzx/kydt/'; const response = await got({ method: 'get', @@ -41,9 +63,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '科研成果 - 中国科学院电工研究所', link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/cas/is/index.ts b/lib/routes/cas/is/index.ts index d4eb3f030..c608a8cc8 100644 --- a/lib/routes/cas/is/index.ts +++ b/lib/routes/cas/is/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 = 'https://is.cas.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/is/:path{.+}', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const path = ctx.req.param('path'); const response = await got(`${baseUrl}/${path}/`); @@ -37,9 +45,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), link: `${baseUrl}/${path}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/cas/mesalab/kb.ts b/lib/routes/cas/mesalab/kb.ts index 9f36cfa36..be0d17f0b 100644 --- a/lib/routes/cas/mesalab/kb.ts +++ b/lib/routes/cas/mesalab/kb.ts @@ -1,10 +1,32 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import cherrio 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: '/mesalab/kb', + categories: ['forecast'], + example: '/cas/mesalab/kb', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.mesalab.cn/f/article/articleList', 'www.mesalab.cn/'], + }, + name: '信息工程研究所 第二研究室 处理架构组 知识库', + maintainers: ['renzhexigua'], + handler, +}; + +async function handler() { const homepage = 'https://www.mesalab.cn'; const url = `${homepage}/f/article/articleList?pageNo=1&pageSize=15&createTimeSort=DESC`; const response = await got(url); @@ -33,10 +55,10 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: 'MESA 知识库', description: '中国科学院信息工程研究所 第二研究室 处理架构组', link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/cas/sim/kyjz.ts b/lib/routes/cas/sim/kyjz.ts index 29075a980..f89f8a125 100644 --- a/lib/routes/cas/sim/kyjz.ts +++ b/lib/routes/cas/sim/kyjz.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 host = 'http://www.sim.cas.cn/'; -export default async (ctx) => { +export const route: Route = { + path: '/sim/kyjz', + categories: ['forecast'], + example: '/cas/sim/kyjz', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.sim.cas.cn/xwzx2016/kyjz', 'www.sim.cas.cn/'], + }, + name: '上海微系统与信息技术研究所 科技进展', + maintainers: ['HenryQW'], + handler, +}; + +async function handler() { const link = new URL('xwzx2016/kyjz/', host).href; const response = await got(link); @@ -40,9 +62,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '中国科学院上海微系统与信息技术研究所 -- 科技进展', link, item: out, - }); -}; + }; +} diff --git a/lib/routes/casssp/news.ts b/lib/routes/casssp/news.ts index f0e9ff94d..5bc085ed0 100644 --- a/lib/routes/casssp/news.ts +++ b/lib/routes/casssp/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/:category?', + categories: ['study'], + example: '/casssp/news/3', + 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 = '3' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 15; @@ -44,7 +63,7 @@ export default async (ctx) => { const image = $('img[la="la"]').first().prop('src'); const icon = new URL($('link[rel="shortcut icon "]').prop('href'), rootUrl).href; - ctx.set('data', { + return { item: items, title: $('title').text(), link: currentUrl, @@ -56,5 +75,5 @@ export default async (ctx) => { subtitle: $('meta[name="keywords"]').prop('content'), author: $('img[la="la"]').first().prop('title'), allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/cast/index.ts b/lib/routes/cast/index.ts index c01912a1e..8af4fe7ee 100644 --- a/lib/routes/cast/index.ts +++ b/lib/routes/cast/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got, { type Response } from '@/utils/got'; import { load } from 'cheerio'; @@ -41,7 +42,25 @@ async function parsePage(html: string) { ); } -export default async (ctx) => { +export const route: Route = { + path: '/:column/:subColumn/:category?', + categories: ['study'], + example: '/cast/xw/tzgg/ZH', + parameters: { column: '栏目编号,见下表', subColumn: '二级栏目编号', category: '分类' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '通用', + maintainers: ['KarasuShin', 'TonyRL'], + handler, +}; + +async function handler(ctx) { const { column, subColumn, category } = ctx.req.param(); const { limit = 10 } = ctx.req.query(); let link = `${baseUrl}/${column}/${subColumn}`; @@ -72,10 +91,10 @@ export default async (ctx) => { const pageTitle = $('head title').text(); - ctx.set('data', { + return { title: pageTitle, link, image: 'https://www.cast.org.cn/favicon.ico', item: items, - }); -}; + }; +} diff --git a/lib/routes/cau/ele.ts b/lib/routes/cau/ele.ts index 9dfaab0cb..edf4318a1 100644 --- a/lib/routes/cau/ele.ts +++ b/lib/routes/cau/ele.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: '/ele', + categories: ['forecast'], + example: '/cau/ele', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ciee.cau.edu.cn/col/col26712/index.html', 'ciee.cau.edu.cn/'], + }, + name: '研招网通知公告', + maintainers: ['shengmaosu'], + handler, +}; + +async function handler() { const baseUrl = 'https://ciee.cau.edu.cn'; const link = `${baseUrl}/col/col26712/index.html`; const response = await got(`${baseUrl}/module/web/jpage/dataproxy.jsp`, { @@ -20,7 +42,7 @@ export default async (ctx) => { const $ = load(response.data); const list = $('recordset record'); - ctx.set('data', { + return { title: '中国农业大学信电学院', link, description: '中国农业大学信电学院通知公告', @@ -38,5 +60,5 @@ export default async (ctx) => { guid: `${link}#${title}`, }; }), - }); -}; + }; +} diff --git a/lib/routes/cau/yjs.ts b/lib/routes/cau/yjs.ts index d13af1255..30bbe93c5 100644 --- a/lib/routes/cau/yjs.ts +++ b/lib/routes/cau/yjs.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: '/yjs', + categories: ['forecast'], + example: '/cau/yjs', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['yz.cau.edu.cn/col/col41740/index.html', 'yz.cau.edu.cn/'], + }, + name: 'Unknown', + maintainers: ['shengmaosu'], + handler, +}; + +async function handler() { const baseUrl = 'https://yz.cau.edu.cn'; const link = `${baseUrl}/col/col41740/index.html`; const response = await got(`${baseUrl}/module/web/jpage/dataproxy.jsp`, { @@ -20,7 +42,7 @@ export default async (ctx) => { const $ = load(response.data); const list = $('recordset record'); - ctx.set('data', { + return { title: '中农研究生学院', link, description: '中农研究生学院', @@ -38,5 +60,5 @@ export default async (ctx) => { pubDate: parseDate(item.find('span').text().replaceAll(/[[\]]/g, '')), }; }), - }); -}; + }; +} diff --git a/lib/routes/caus/index.ts b/lib/routes/caus/index.ts index 53b6d9a74..6ba6699d6 100644 --- a/lib/routes/caus/index.ts +++ b/lib/routes/caus/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; @@ -28,7 +29,25 @@ const categories = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + categories: ['traditional-media'], + example: '/caus', + 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') || '0'; const isHome = category === '0'; @@ -81,9 +100,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${categories[category].title} - 加美财经`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/cbaigui/index.ts b/lib/routes/cbaigui/index.ts index 11ab731a9..7417c8e8a 100644 --- a/lib/routes/cbaigui/index.ts +++ b/lib/routes/cbaigui/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 * as path from 'node:path'; import { rootUrl, apiSlug, GetFilterId } from './utils'; -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; let filterName; @@ -89,7 +97,7 @@ export default async (ctx) => { const icon = $('link[rel="apple-touch-icon"]').first().prop('href'); - ctx.set('data', { + return { item: items, title: `纪妖${filterName ? ` - ${filterName}` : ''}`, link: currentUrl, @@ -100,5 +108,5 @@ export default async (ctx) => { logo: icon, subtitle: $('p.site-description').text(), author: $('p.site-title').text(), - }); -}; + }; +} diff --git a/lib/routes/cbc/topics.ts b/lib/routes/cbc/topics.ts index 54ce39614..ec3ed192b 100644 --- a/lib/routes/cbc/topics.ts +++ b/lib/routes/cbc/topics.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: '/topics/:topic?', + categories: ['bbs'], + example: '/cbc/topics', + parameters: { topic: 'Channel,`Top Stories` by default. For secondary channel like `canada/toronto`, use `-` to replace `/`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['cbc.ca/news'], + target: '/topics', + }, + name: 'News', + maintainers: ['wb14123'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://www.cbc.ca'; const topic = ctx.req.param('topic') || ''; const url = `${baseUrl}/news${topic ? `/${topic.replace('-', '/')}` : ''}`; @@ -51,9 +74,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: url, item: out.filter((x) => x.title), - }); -}; + }; +} diff --git a/lib/routes/cbirc/index.ts b/lib/routes/cbirc/index.ts index 0374fc711..58a18116d 100644 --- a/lib/routes/cbirc/index.ts +++ b/lib/routes/cbirc/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; @@ -78,7 +79,17 @@ async function getContent(item) { return response.data.data.docClob; } -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + radar: { + source: ['cbirc.gov.cn/:category', 'cbirc.gov.cn/'], + }, + name: 'Unknown', + maintainers: ['JkCheung'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'ggtz'; const cat = categories[category]; @@ -110,11 +121,11 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `中国银保监会-${cat.title}`, link: cat.link, description: `中国银保监会-${cat.title}`, item: dataLs, language: 'zh-CN', - }); -}; + }; +} diff --git a/lib/routes/cbnweek/index.ts b/lib/routes/cbnweek/index.ts index 40df892fe..ba0f620c3 100644 --- a/lib/routes/cbnweek/index.ts +++ b/lib/routes/cbnweek/index.ts @@ -1,8 +1,20 @@ +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: '/', + radar: { + source: ['cbnweek.com/'], + target: '', + }, + name: 'Unknown', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://www2021.cbnweek.com'; const apiRootUrl = 'https://api2021.cbnweek.com'; const currentUrl = `${apiRootUrl}/v4/first_page_infos?per=1`; @@ -39,9 +51,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '第一财经杂志', link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/ccac/news.ts b/lib/routes/ccac/news.ts index 51d1d9aca..ec9ab05f9 100644 --- a/lib/routes/ccac/news.ts +++ b/lib/routes/ccac/news.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import utils from './utils'; import { parseDate } from '@/utils/parse-date'; @@ -5,7 +6,25 @@ import got from '@/utils/got'; import { load } from 'cheerio'; import puppeteer from '@/utils/puppeteer'; -export default async (ctx) => { +export const route: Route = { + path: '/news/:type/:lang?', + categories: ['study'], + example: '/ccac/news/all', + parameters: { type: 'Category', lang: 'Language, default to `sc`. Supprot `en`(English), `sc`(Simplified Chinese), `tc`(Traditional Chinese) and `pt`(Portuguese)' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Latest News', + maintainers: ['linbuxiao'], + handler, +}; + +async function handler(ctx) { const browser = await puppeteer(); const lang = ctx.req.param('lang') ?? 'sc'; const type = utils.TYPE[ctx.req.param('type')]; @@ -48,11 +67,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `CCAC ${type}`, link: BASE, description: `CCAC ${type}`, language: ctx.req.param('lang') ? utils.LANG_TYPE[ctx.req.param('lang')] : utils.LANG_TYPE.sc, item: items, - }); -}; + }; +} diff --git a/lib/routes/ccf/ccfcv/index.ts b/lib/routes/ccf/ccfcv/index.ts index e37762b02..efb998e2d 100644 --- a/lib/routes/ccf/ccfcv/index.ts +++ b/lib/routes/ccf/ccfcv/index.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 cateTitleMap = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/ccfcv/:channel/:category', + categories: ['journal'], + example: '/ccf/ccfcv/xsdt/xsqy', + parameters: { channel: '频道,仅支持 `xsdt`', category: '分类,见下表,亦可在网站 url 里找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '计算机视觉专委会 - 学术动态 - 分类', + maintainers: ['elxy'], + handler, +}; + +async function handler(ctx) { const channel = ctx.req.param('channel'); const cate = ctx.req.param('category'); @@ -64,9 +83,9 @@ export default async (ctx) => { ); } - ctx.set('data', { + return { title: `计算机视觉专委 - ${cateTitleMap[channel][cate]}`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/ccf/news.ts b/lib/routes/ccf/news.ts index 0596db897..4b979de80 100644 --- a/lib/routes/ccf/news.ts +++ b/lib/routes/ccf/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: '/news/:category?', + categories: ['journal'], + example: '/ccf/news', + parameters: { category: '分类,见下表,默认为 CCF 新闻' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ccf.org.cn/:category', 'ccf.org.cn/'], + target: '/news/:category', + }, + name: '新闻', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') || 'Media_list'; const rootUrl = 'https://www.ccf.org.cn'; @@ -38,9 +61,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/ccf/tfbd/index.ts b/lib/routes/ccf/tfbd/index.ts index 66f2005ae..09a2b255f 100644 --- a/lib/routes/ccf/tfbd/index.ts +++ b/lib/routes/ccf/tfbd/index.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import utils from './utils'; import got from '@/utils/got'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/tfbd/:caty/:id', + categories: ['journal'], + example: '/ccf/tfbd/xwdt/tzgg', + parameters: { caty: '主分类,可在 URL 找到', id: '子分类,可在 URL 找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['tfbd.ccf.org.cn/tfbd/:caty/:id', 'tfbd.ccf.org.cn/'], + }, + name: '大数据专家委员会', + maintainers: ['tudou027'], + handler, +}; + +async function handler(ctx) { const base = utils.urlBase(ctx.req.param('caty'), ctx.req.param('id')); const res = await got(base); const info = utils.fetchAllArticles(res.data); @@ -15,9 +37,9 @@ export default async (ctx) => { info, }); - ctx.set('data', { + return { title: '大数据专家委员会 - ' + $('.position a:last-child').text(), link: base, item: details, - }); -}; + }; +} diff --git a/lib/routes/ccnu/career.ts b/lib/routes/ccnu/career.ts index 24094a74a..41b445b28 100644 --- a/lib/routes/ccnu/career.ts +++ b/lib/routes/ccnu/career.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: '/career', + categories: ['forecast'], + example: '/ccnu/career', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ccnu.91wllm.com/news/index/tag/tzgg', 'ccnu.91wllm.com/'], + }, + name: '就业信息', + maintainers: ['jackyu1996'], + handler, +}; + +async function handler() { const host = 'https://ccnu.91wllm.com'; const link = `${host}/news/index/tag/tzgg`; @@ -23,9 +45,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: '华中师范大学就业信息', link, item: items, - }); -}; + }; +} diff --git a/lib/routes/ccnu/cs.ts b/lib/routes/ccnu/cs.ts index b83167dc0..a90f0c3d4 100644 --- a/lib/routes/ccnu/cs.ts +++ b/lib/routes/ccnu/cs.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: '/cs', + categories: ['forecast'], + example: '/ccnu/cs', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['cs.ccnu.edu.cn/xwzx/tzgg.htm', 'cs.ccnu.edu.cn/'], + }, + name: '计算机学院', + maintainers: ['shengmaosu'], + handler, +}; + +async function handler() { const link = 'http://cs.ccnu.edu.cn/xwzx/tzgg.htm'; const response = await got(link); const $ = load(response.data); const list = $('.list_box_07 li'); - ctx.set('data', { + return { title: '华中师范大学计算机学院', link, description: '华中师范大学计算机学院通知公告', @@ -24,5 +46,5 @@ export default async (ctx) => { pubDate: parseDate(item.find('.time').text(), 'DDYYYY-MM'), }; }), - }); -}; + }; +} diff --git a/lib/routes/ccnu/wu.ts b/lib/routes/ccnu/wu.ts index b7b9c985e..a31850e16 100644 --- a/lib/routes/ccnu/wu.ts +++ b/lib/routes/ccnu/wu.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: '/wu', + categories: ['forecast'], + example: '/ccnu/wu', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['uowji.ccnu.edu.cn/xwzx/tzgg.htm', 'uowji.ccnu.edu.cn/'], + }, + name: '伍论贡学院', + maintainers: ['shengmaosu'], + handler, +}; + +async function handler() { const link = 'http://uowji.ccnu.edu.cn/tzgg.htm'; const response = await got(link); const $ = load(response.data); const list = $('.zy-mainxrx li'); - ctx.set('data', { + return { title: '华中师范大学伍论贡学院', link, description: '华中师范大学伍论贡学院通知公告', @@ -23,5 +45,5 @@ export default async (ctx) => { pubDate: parseDate(item.find('small').text(), 'YYYY-MM-DD'), }; }), - }); -}; + }; +} diff --git a/lib/routes/ccnu/yjs.ts b/lib/routes/ccnu/yjs.ts index 7e157f671..1a34c0be8 100644 --- a/lib/routes/ccnu/yjs.ts +++ b/lib/routes/ccnu/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: '/ccnu/yjs', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gs.ccnu.edu.cn/zsgz/ssyjs.htm', 'gs.ccnu.edu.cn/'], + }, + name: '研究生通知公告', + maintainers: ['shengmaosu'], + handler, +}; + +async function handler() { const link = 'http://gs.ccnu.edu.cn/zsgz/ssyjs.htm'; const response = await got(link); const $ = load(response.data); const list = $('.main-zyrx li'); - ctx.set('data', { + return { title: '华中师范大学研究生院', link, description: '华中师范大学研究生院通知公告', @@ -23,5 +45,5 @@ export default async (ctx) => { pubDate: parseDate(item.find('small').text(), 'YYYY-MM-DD'), }; }), - }); -}; + }; +} diff --git a/lib/routes/ccreports/index.ts b/lib/routes/ccreports/index.ts index 9b78e725a..be4576b93 100644 --- a/lib/routes/ccreports/index.ts +++ b/lib/routes/ccreports/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,28 @@ import timezone from '@/utils/timezone'; const rootUrl = 'https://www.ccreports.com.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/article', + categories: ['game'], + example: '/ccreports/article', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.ccreports.com.cn/'], + }, + name: '要闻', + maintainers: ['EsuRt', 'Fatpandac'], + handler, +}; + +async function handler() { const listData = await got.get(rootUrl); const $ = load(listData.data); const list = $('div.index-four-content > div.article-box') @@ -34,9 +56,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '消费者报道', link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/cctv/category.ts b/lib/routes/cctv/category.ts index d0f4501a4..f69712eed 100644 --- a/lib/routes/cctv/category.ts +++ b/lib/routes/cctv/category.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import getMzzlbg from './utils/mzzlbg'; import xinwen1j1 from './utils/xinwen1j1'; import getNews from './utils/news'; -export default async (ctx) => { +export const route: Route = { + path: '/:category', + categories: ['bbs'], + example: '/cctv/world', + parameters: { category: '分类名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['news.cctv.com/:category'], + }, + name: '专题', + maintainers: ['idealclover', 'xyqfer'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category'); let responseData; @@ -18,4 +40,4 @@ export default async (ctx) => { } ctx.set('data', responseData); -}; +} diff --git a/lib/routes/cctv/jx.ts b/lib/routes/cctv/jx.ts index 40b899d0d..be29c6825 100644 --- a/lib/routes/cctv/jx.ts +++ b/lib/routes/cctv/jx.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: '/photo/jx', + categories: ['bbs'], + example: '/cctv/photo/jx', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['photo.cctv.com/jx', 'photo.cctv.com/'], + }, + name: '央视网图片《镜象》', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const rootUrl = 'https://photo.cctv.com'; const currentUrl = `${rootUrl}/jx/`; const response = await got({ @@ -44,9 +66,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '央视网图片《镜象》', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/cctv/lm.ts b/lib/routes/cctv/lm.ts index 4a7def778..f4656e9b9 100644 --- a/lib/routes/cctv/lm.ts +++ b/lib/routes/cctv/lm.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: '/lm/:id?', + categories: ['bbs'], + example: '/cctv/lm/xwzk', + parameters: { id: '栏目 id,可在对应栏目页 URL 中找到,默认为 `xwzk` 即 新闻周刊' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['news.cctv.com/:category'], + target: '/:category', + }, + name: '栏目', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? 'xwzk'; const rootUrl = 'https://tv.cctv.com'; @@ -69,10 +92,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, description: $('meta[name=description]').attr('content'), - }); -}; + }; +} diff --git a/lib/routes/cctv/xwlb.ts b/lib/routes/cctv/xwlb.ts index ef2c24f8b..1c765d43b 100644 --- a/lib/routes/cctv/xwlb.ts +++ b/lib/routes/cctv/xwlb.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'; @@ -7,7 +8,28 @@ import dayjs from 'dayjs'; import customParseFormat from 'dayjs/plugin/customParseFormat'; dayjs.extend(customParseFormat); -export default async (ctx) => { +export const route: Route = { + path: '/xwlb', + categories: ['bbs'], + example: '/cctv/xwlb', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['tv.cctv.com/lm/xwlb', 'tv.cctv.com/'], + }, + name: '新闻联播', + maintainers: ['zengxs'], + handler, +}; + +async function handler() { const res = await got({ method: 'get', url: 'https://tv.cctv.com/lm/xwlb/' }); const $ = load(res.data); // 解析最新一期新闻联播的日期 @@ -43,9 +65,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: 'CCTV 新闻联播', link: 'http://tv.cctv.com/lm/xwlb/', item: resultItems, - }); -}; + }; +} diff --git a/lib/routes/cde/index.ts b/lib/routes/cde/index.ts index 3876e583a..64af9a8de 100644 --- a/lib/routes/cde/index.ts +++ b/lib/routes/cde/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import utils from './utils'; import got from '@/utils/got'; @@ -75,7 +76,25 @@ const requestData = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/:channel/:category', + categories: ['study'], + example: '/cde/news/gzdt', + parameters: { channel: '频道', category: '类别' }, + 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'); const cate = ctx.req.param('category'); requestData[channel][cate].pageSize = ctx.req.param('limit') ?? 25; @@ -125,9 +144,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${utils.title} - ${cateTitleMap[channel][cate]}`, link: channelLinkMap[channel], item: items, - }); -}; + }; +} diff --git a/lib/routes/cde/xxgk.ts b/lib/routes/cde/xxgk.ts index 746d0c97d..fde65a561 100644 --- a/lib/routes/cde/xxgk.ts +++ b/lib/routes/cde/xxgk.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -49,7 +50,25 @@ const xxgkMap = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/xxgk/:category', + categories: ['study'], + example: '/cde/xxgk/priorityApproval', + parameters: { category: '类别,见下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '信息公开', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category'); const { data } = await got.post(`${baseUrl}/main${xxgkMap.xxgk[category].endPoint}`, { @@ -68,9 +87,9 @@ export default async (ctx) => { link: xxgkMap.xxgk[category].url, })); - ctx.set('data', { + return { title: `${xxgkMap.xxgk[category].title} - 国家药品监督管理局药品审评中心`, link: xxgkMap.xxgk[category].url, item: items, - }); -}; + }; +} diff --git a/lib/routes/cde/zdyz.ts b/lib/routes/cde/zdyz.ts index f37a63a79..8437e7bf8 100644 --- a/lib/routes/cde/zdyz.ts +++ b/lib/routes/cde/zdyz.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -37,7 +38,25 @@ const zdyzMap = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/zdyz/:category', + categories: ['study'], + example: '/cde/zdyz/domesticGuide', + parameters: { category: '类别,见下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '指导原则专栏', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category'); const { data } = await got.post(`${baseUrl}${zdyzMap.zdyz[category].endPoint}`, { @@ -70,9 +89,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${zdyzMap.zdyz[category].title} - 国家药品监督管理局药品审评中心`, link: zdyzMap.zdyz[category].url, item: items, - }); -}; + }; +} diff --git a/lib/routes/cdi/index.ts b/lib/routes/cdi/index.ts index 0aa30aece..d238ba5b4 100644 --- a/lib/routes/cdi/index.ts +++ b/lib/routes/cdi/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: ['traditional-media'], + example: '/cdi', + 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') ?? '152'; const rootUrl = 'http://www.cdi.com.cn'; @@ -54,9 +73,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('h1').text()} - 国家高端智库/综合开发研究院`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/cdzjryb/project-list.ts b/lib/routes/cdzjryb/project-list.ts index 418ca727a..1e45fdc2b 100644 --- a/lib/routes/cdzjryb/project-list.ts +++ b/lib/routes/cdzjryb/project-list.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 { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/zw/projectList', + categories: ['other'], + example: '/cdzjryb/zw/projectList', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['zw.cdzjryb.com/lottery/accept/projectList', 'zw.cdzjryb.com/'], + }, + name: '商品住房购房登记', + maintainers: ['TonyRL'], + handler, +}; + +async function handler() { const url = 'https://zw.cdzjryb.com/lottery/accept/projectList'; const { data: response } = await got(url); const $ = load(response); @@ -45,9 +67,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/cebbank/all.ts b/lib/routes/cebbank/all.ts index 79ebfeb37..2ec7482a6 100644 --- a/lib/routes/cebbank/all.ts +++ b/lib/routes/cebbank/all.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 { parseDate } from '@/utils/parse-date'; import md5 from '@/utils/md5'; -export default async (ctx) => { +export const route: Route = { + path: '/quotation/all', + categories: ['other'], + example: '/cebbank/quotation/all', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['cebbank.com/site/ygzx/whpj/index.html', 'cebbank.com/eportal/ui', 'cebbank.com/'], + }, + name: 'Unknown', + maintainers: ['linbuxiao'], + handler, +}; + +async function handler(ctx) { const link = 'https://www.cebbank.com/eportal/ui?pageId=477257'; const content = await got({ method: 'get', @@ -38,12 +60,12 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title: '中国光大银行', description: '中国光大银行 外汇牌价', link, item: items, - }); + }; ctx.set('json', { title: '中国光大银行', @@ -51,4 +73,4 @@ export default async (ctx) => { pubDate: timezone(parseDate($('#t_id span').text().substring(5), 'YYYY-MM-DD HH:mm', true), 0), item: items, }); -}; +} diff --git a/lib/routes/cebbank/history.ts b/lib/routes/cebbank/history.ts index d35724f1a..33b9d4af4 100644 --- a/lib/routes/cebbank/history.ts +++ b/lib/routes/cebbank/history.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 utils from './utils'; const { TYPE } = utils; -export default async (ctx) => { +export const route: Route = { + path: '/quotation/history/:type', + categories: ['other'], + example: '/cebbank/quotation/history/usd', + 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'); const url = `https://www.cebbank.com/eportal/ui?struts.portlet.action=/portlet/whpjFront!toView.action&moduleId=12094&pageId=477260&currcode=${TYPE[type].id}¤tPagebak=1¤tPage=1`; const res = await got({ @@ -38,16 +57,16 @@ export default async (ctx) => { }) .get(); items.pop(); - ctx.set('data', { + return { title: '中国光大银行', description: `中国光大银行 外汇牌价 ${TYPE[type].name}`, link: `https://www.cebbank.com/site/ygzx/whpj/rmbwhpjlspj/index.html?currcode=${TYPE[type].id}`, item: items, - }); + }; ctx.set('json', { title: '中国光大银行', description: `中国光大银行 外汇牌价 ${TYPE[type].name}`, item: items, }); -}; +} diff --git a/lib/routes/cfachina/analygarden.ts b/lib/routes/cfachina/analygarden.ts index d4c60d4a8..f6831ff2c 100644 --- a/lib/routes/cfachina/analygarden.ts +++ b/lib/routes/cfachina/analygarden.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: '/servicesupport/analygarden/:program?', + categories: ['other'], + example: '/cfachina/servicesupport/analygarden', + parameters: { program: '分类,见下表,留空为全部' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['cfachina.org/servicesupport/analygarden/:program?', 'cfachina.org/'], + }, + name: '分析师园地', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { let { program = '分析师园地' } = ctx.req.param(); const baseUrl = 'https://www.cfachina.org'; let pageData, @@ -52,9 +74,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `${pageData?.category.toReversed().join(' - ') ?? '分析师园地'} - 中国期货业协会`, link: pageUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/cfmmc/index.ts b/lib/routes/cfmmc/index.ts index 895f6dc12..974bb41cf 100644 --- a/lib/routes/cfmmc/index.ts +++ b/lib/routes/cfmmc/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: '/:id{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { id = 'main/noticeannouncement/cfmmcnotice' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 10; @@ -59,7 +67,7 @@ export default async (ctx) => { const author = '中国期货市场监控中心'; const image = new URL($('a.logo img').prop('src'), rootUrl).href; - ctx.set('data', { + return { item: items, title: `${author} - ${$('h3.SubPage_t3').text()}`, link: currentUrl, @@ -69,5 +77,5 @@ export default async (ctx) => { subtitle: $('meta[name="Keywords"]').prop('content'), author, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/cgtn/podcast.ts b/lib/routes/cgtn/podcast.ts index a425675ad..295be32e0 100644 --- a/lib/routes/cgtn/podcast.ts +++ b/lib/routes/cgtn/podcast.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; @@ -13,7 +14,28 @@ function combDate(date, time) { return timezone(parseDate(date + ' ' + time), +8); } -export default async (ctx) => { +export const route: Route = { + path: '/podcast/:category/:id', + categories: ['bbs'], + example: '/cgtn/podcast/ezfm/4', + parameters: { category: '类型名', id: '播客 id' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['cgtn.com/podcast/column/:category/*/:id'], + }, + name: '播客', + maintainers: ['5upernova-heng'], + handler, +}; + +async function handler(ctx) { const { category, id } = ctx.req.param(); const categoryMap = { ezfm: 1, other: 5 }; const data = await getData(categoryMap[category], id); @@ -26,10 +48,10 @@ export default async (ctx) => { enclosure_url: item.mediaUrl, enclosure_type: 'audio/mpeg', })); - ctx.set('data', { + return { title: `中国环球电视网 CGTN Podcast - ${data.info}`, link: 'https://www.cgtn.com/radio/', item: items, description: String(data.info), - }); -}; + }; +} diff --git a/lib/routes/chaincatcher/home.ts b/lib/routes/chaincatcher/home.ts index 63b208a85..425ea4d95 100644 --- a/lib/routes/chaincatcher/home.ts +++ b/lib/routes/chaincatcher/home.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 * as path from 'node:path'; const rootUrl = 'https://www.chaincatcher.com'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['chaincatcher.com/'], + target: '', + }, + name: 'Unknown', + maintainers: ['TonyRL'], + handler, +}; + +async function handler() { const { data } = await got.post(`${rootUrl}/api/article/lists`, { form: { page: 1, @@ -44,11 +56,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '链捕手ChainCatcher — 专业的区块链技术研究与资讯平台-Chain Catcher', description: '链捕手ChainCatcher为区块链技术爱好者与项目决策者提供NFT、Web3社交、DID、Layer2等专业的资讯与研究内容,Chain Catcher输出对Scroll、Sui、Aptos、ENS等项目的思考,拓宽读者对区块链与数字经济认知的边界。', image: `${rootUrl}/logo.png`, link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/chaincatcher/news.ts b/lib/routes/chaincatcher/news.ts index 497e358d0..3f7fe8256 100644 --- a/lib/routes/chaincatcher/news.ts +++ b/lib/routes/chaincatcher/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'; @@ -5,7 +6,28 @@ import timezone from '@/utils/timezone'; const rootUrl = 'https://www.chaincatcher.com'; -export default async (ctx) => { +export const route: Route = { + path: '/news', + categories: ['traditional-media'], + example: '/chaincatcher/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['chaincatcher.com/news', 'chaincatcher.com/'], + }, + name: '快讯', + maintainers: ['TonyRL'], + handler, +}; + +async function handler() { const { data } = await got.post(`${rootUrl}/index/content/lists`, { form: { page: 1, @@ -30,11 +52,11 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: '最新资讯-ChainCatcher', description: '链捕手ChainCatcher为区块链技术爱好者与项目决策者提供NFT、Web3社交、DID、Layer2等专业的资讯与研究内容,Chain Catcher输出对Scroll、Sui、Aptos、ENS等项目的思考,拓宽读者对区块链与数字经济认知的边界。', image: `${rootUrl}/logo.png`, link: `${rootUrl}/news`, item: items, - }); -}; + }; +} diff --git a/lib/routes/changba/user.ts b/lib/routes/changba/user.ts index b91e16a07..a5f31fc00 100644 --- a/lib/routes/changba/user.ts +++ b/lib/routes/changba/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 { art } from '@/utils/render'; import * as path from 'node:path'; const headers = { 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1' }; -export default async (ctx) => { +export const route: Route = { + path: '/:userid', + categories: ['new-media'], + example: '/changba/skp6hhF59n48R-UpqO3izw', + parameters: { userid: '用户ID, 可在对应分享页面的 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: true, + supportScihub: false, + }, + radar: { + source: ['changba.com/s/:userid'], + }, + name: '用户', + maintainers: [], + handler, +}; + +async function handler(ctx) { const userid = ctx.req.param('userid'); const url = `https://changba.com/wap/index.php?s=${userid}`; const response = await got({ @@ -76,7 +98,7 @@ export default async (ctx) => { items = items.filter(Boolean); - ctx.set('data', { + return { title: $('title').text(), link: url, description: $('meta[name="description"]').attr('content') || $('title').text(), @@ -84,5 +106,5 @@ export default async (ctx) => { image: authorimg, itunes_author: author, itunes_category: '唱吧', - }); -}; + }; +} diff --git a/lib/routes/chaoxing/qk.ts b/lib/routes/chaoxing/qk.ts index 1db2db594..8d604607d 100644 --- a/lib/routes/chaoxing/qk.ts +++ b/lib/routes/chaoxing/qk.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: '/qk/:id/:needContent?', + categories: ['government'], + example: '/chaoxing/qk/6b5c39b3dd84352be512e29df0297437', + parameters: { id: '期刊 id,可在期刊页 URL 中找到', needContent: '需要获取文章全文,填写 true/yes 表示需要,默认需要' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '期刊', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const needContent = /t|y/i.test(ctx.req.param('needContent') ?? 'true'); @@ -55,9 +74,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: response.data.list[0].infos.C307, link: `${rootUrl}/mqk/list?mags=${id}&isort=20&from=space`, item: items, - }); -}; + }; +} diff --git a/lib/routes/chaping/banner.ts b/lib/routes/chaping/banner.ts index e8504ac87..37218e292 100644 --- a/lib/routes/chaping/banner.ts +++ b/lib/routes/chaping/banner.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: '/banner', + categories: ['traditional-media'], + example: '/chaping/banner', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['chaping.cn/'], + }, + name: '图片墙', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = `https://chaping.cn/`; const response = await got({ method: 'get', @@ -33,9 +55,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '差评 - 首页图片墙', link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/chaping/news.ts b/lib/routes/chaping/news.ts index 529e39674..714ce75e8 100644 --- a/lib/routes/chaping/news.ts +++ b/lib/routes/chaping/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'; @@ -14,7 +15,25 @@ const titles = { 9: '公里每小时', }; -export default async (ctx) => { +export const route: Route = { + path: '/news/:caty?', + categories: ['traditional-media'], + example: '/chaping/news/15', + parameters: { caty: '分类,默认为全部资讯' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '资讯', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const caty = ctx.req.param('caty') ?? ''; const targetUrl = `https://chaping.cn/news?cate=${caty}`; @@ -46,9 +65,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `差评资讯 - ${titles[ctx.req.param('caty') === '' ? 0 : ctx.req.param('caty')]}`, link: targetUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/chaping/newsflash.ts b/lib/routes/chaping/newsflash.ts index d47f73c44..f1d9fa3d1 100644 --- a/lib/routes/chaping/newsflash.ts +++ b/lib/routes/chaping/newsflash.ts @@ -1,14 +1,36 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; const host = 'https://chaping.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/newsflash', + categories: ['traditional-media'], + example: '/chaping/newsflash', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['chaping.cn/newsflash'], + }, + name: '快讯', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler() { const newflashAPI = `${host}/api/official/information/newsflash?page=1&limit=21`; const response = await got(newflashAPI).json(); const data = response.data; - ctx.set('data', { + return { title: '差评 快讯', link: `${host}/newsflash`, item: @@ -19,5 +41,5 @@ export default async (ctx) => { pubDate: parseDate(item.time_publish_timestamp * 1000), link: item.origin_url, })), - }); -}; + }; +} diff --git a/lib/routes/chiculture/topic.ts b/lib/routes/chiculture/topic.ts index cd9dc6066..b563f4c00 100644 --- a/lib/routes/chiculture/topic.ts +++ b/lib/routes/chiculture/topic.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: '/topic/:category?', + categories: ['traditional-media'], + example: '/chiculture/topic', + 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 = 'https://ls.chiculture.org.hk'; @@ -54,9 +73,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '議題熱話 | 通識·現代中國', link: `${rootUrl}/tc/hot-topics${category ? `?category=${category}` : ''}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/china/finance/finance.ts b/lib/routes/china/finance/finance.ts index f0293e896..7e525934a 100644 --- a/lib/routes/china/finance/finance.ts +++ b/lib/routes/china/finance/finance.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { load } from 'cheerio'; import got from '@/utils/got'; @@ -15,7 +16,28 @@ const CATEGORY_MAP = { IPO: 'IPO', // IPO观察 }; -export default async (ctx) => { +export const route: Route = { + path: '/finance/:category?', + categories: ['traditional-media'], + example: '/china/finance', + parameters: { category: 'Category of news. See the form below for details, default is suggest news.' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['finance.china.com/:category'], + }, + name: 'Finance News 财经 - 财经新闻', + maintainers: ['KingJem'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://finance.china.com'; const category = CATEGORY_MAP[ctx.req.param('category')] ?? CATEGORY_MAP.tuijian; const websiteUrl = `${baseUrl}/${category}`; @@ -53,10 +75,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), link: websiteUrl, category: listCategory, item: items, - }); -}; + }; +} diff --git a/lib/routes/china/news/highlights/news.ts b/lib/routes/china/news/highlights/news.ts index 80e799986..42eac1182 100644 --- a/lib/routes/china/news/highlights/news.ts +++ b/lib/routes/china/news/highlights/news.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { load } from 'cheerio'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; @@ -9,7 +10,28 @@ const CATEGORY_MAP = { news100: 'news100', }; -export default async (ctx) => { +export const route: Route = { + path: '/news/:category?', + categories: ['traditional-media'], + example: '/china/news', + parameters: { category: 'Category of news. See the form below for details, default is china news.' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['news.china.com/:category'], + }, + name: 'News and current affairs 时事新闻', + maintainers: ['jiaaoMario'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://news.china.com'; const category = CATEGORY_MAP[ctx.req.param('category')] ?? CATEGORY_MAP.domestic; const websiteUrl = `${baseUrl}/${category}`; @@ -18,7 +40,7 @@ export default async (ctx) => { const $ = load(data); const categoryTitle = $('.wp_title').text(); const news = $('.item_list li'); - ctx.set('data', { + return { title: `中华网-${categoryTitle}新闻`, link: websiteUrl, item: @@ -36,5 +58,5 @@ export default async (ctx) => { }; }) .get(), - }); -}; + }; +} diff --git a/lib/routes/china/news/military/news.ts b/lib/routes/china/news/military/news.ts index 04356eab1..882a1b669 100644 --- a/lib/routes/china/news/military/news.ts +++ b/lib/routes/china/news/military/news.ts @@ -1,14 +1,36 @@ +import { Route } from '@/types'; import { load } from 'cheerio'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/news/military', + categories: ['traditional-media'], + example: '/china/news/military', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['military.china.com/news'], + }, + name: 'Military - Military News 军事 - 军事新闻', + maintainers: ['jiaaoMario'], + handler, +}; + +async function handler() { const websiteUrl = 'https://military.china.com/news/'; const response = await got(websiteUrl); const data = response.data; const $ = load(data); const commonList = $('.item_list li'); - ctx.set('data', { + return { title: '中华网-军事新闻', link: 'https://military.china.com/news/', item: @@ -26,5 +48,5 @@ export default async (ctx) => { }; }) .get(), - }); -}; + }; +} diff --git a/lib/routes/chinadegrees/province.ts b/lib/routes/chinadegrees/province.ts index 9fd8c2337..a71b7a0d0 100644 --- a/lib/routes/chinadegrees/province.ts +++ b/lib/routes/chinadegrees/province.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 { config } from '@/config'; import puppeteer from '@/utils/puppeteer'; const baseUrl = 'http://www.chinadegrees.com.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/:province?', + categories: ['journal'], + example: '/chinadegrees/11', + parameters: { province: '省市代号,见下表,亦可在 [这里](http://www.chinadegrees.com.cn/help/provinceSwqk.html) 找到,默认为 `11`' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Unknown', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { province = '11' } = ctx.req.param(); const url = `${baseUrl}/help/unitSwqk${province}.html`; @@ -63,9 +82,9 @@ export default async (ctx) => { return item; }); - ctx.set('data', { + return { title: data.title, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/chinafactcheck/index.ts b/lib/routes/chinafactcheck/index.ts index fbec13556..2cb77837a 100644 --- a/lib/routes/chinafactcheck/index.ts +++ b/lib/routes/chinafactcheck/index.ts @@ -1,9 +1,21 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import utils from './utils'; import { load } from 'cheerio'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['chinafactcheck.com/'], + target: '', + }, + name: 'Unknown', + maintainers: ['kdanfly'], + handler, +}; + +async function handler() { const response = await got(utils.siteLink, { headers: { 'user-agent': utils.trueUA, @@ -30,9 +42,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), link: utils.siteLink, item: articles, - }); -}; + }; +} diff --git a/lib/routes/chinaisa/index.ts b/lib/routes/chinaisa/index.ts index 540e97ed3..8576fc3d3 100644 --- a/lib/routes/chinaisa/index.ts +++ b/lib/routes/chinaisa/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: '/:id?', + categories: ['traditional-media'], + example: '/chinaisa', + 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 = '58af05dfb6b4300151760176d2aad0a04c275aaadbb1315039263f021f920dcd' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 15; @@ -69,7 +88,7 @@ export default async (ctx) => { const icon = new URL($('link[rel="shortcut icon"]').prop('href'), rootUrl).href; - ctx.set('data', { + return { item: items, title: `${$('title').text()} - ${subtitle}`, link: currentUrl, @@ -80,5 +99,5 @@ export default async (ctx) => { logo: icon, subtitle, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/chinamoney/notice.ts b/lib/routes/chinamoney/notice.ts index 78bde4ba1..8025e4890 100644 --- a/lib/routes/chinamoney/notice.ts +++ b/lib/routes/chinamoney/notice.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'; import { channels } from './channels'; -export default async (ctx) => { +export const route: Route = { + path: '/:channelId?', + categories: ['other'], + example: '/chinamoney', + parameters: { channelId: '分类,见下表,默认为 `2834`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '公告', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { channelId = 2834 } = ctx.req.param(); const baseUrl = 'https://www.chinamoney.com.cn'; @@ -54,9 +73,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${channels[channelId] ? channels[channelId].title + ' - ' : ''}中国货币网`, link: `${baseUrl}${channels[channelId]?.urlPath ?? ''}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/chinanews/index.ts b/lib/routes/chinanews/index.ts index 0f27b53e1..c5780a538 100644 --- a/lib/routes/chinanews/index.ts +++ b/lib/routes/chinanews/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import timezone from '@/utils/timezone'; @@ -6,7 +7,18 @@ import { load } from 'cheerio'; const rootUrl = 'https://www.chinanews.com.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['chinanews.com.cn/'], + target: '', + }, + name: 'Unknown', + maintainers: ['yuxinliu-alex'], + handler, +}; + +async function handler(ctx) { const currentUrl = `${rootUrl}/scroll-news/news1.html`; const response = await got({ method: 'get', @@ -57,11 +69,11 @@ export default async (ctx) => { }) ) ); - ctx.set('data', { + return { title: '中国新闻网', link: currentUrl, description: '中国新闻网(简称“中新网”),由中国新闻社主办,为中央重点新闻网站。', language: 'zh-cn', item: items, - }); -}; + }; +} diff --git a/lib/routes/chinathinktanks/viewpoint.ts b/lib/routes/chinathinktanks/viewpoint.ts index 155e3a732..117949138 100644 --- a/lib/routes/chinathinktanks/viewpoint.ts +++ b/lib/routes/chinathinktanks/viewpoint.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 @@ import { CookieJar } from 'tough-cookie'; const cookieJar = new CookieJar(); const baseUrl = 'https://www.chinathinktanks.org.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/:id', + categories: ['journal'], + example: '/chinathinktanks/57', + parameters: { id: '见下表,亦可在网站 url 里找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '观点与实践', + maintainers: ['Aeliu'], + handler, +}; + +async function handler(ctx) { const link = `https://www.chinathinktanks.org.cn/content/list?id=${ctx.req.param('id')}&pt=1`; const response = await got(link, { @@ -45,9 +64,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `中国智库网 —— ${$('title').text().split('_中国智库网')[0]}`, link, item: items, - }); -}; + }; +} diff --git a/lib/routes/chinaventure/index.ts b/lib/routes/chinaventure/index.ts index 617ba832b..73746b273 100644 --- a/lib/routes/chinaventure/index.ts +++ b/lib/routes/chinaventure/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,29 @@ const nodes = { 116: '新消费', }; -export default async (ctx) => { +export const route: Route = { + path: '/news/:id?', + categories: ['traditional-media'], + example: '/chinaventure/news/78', + parameters: { id: '分类,见下表,默认为推荐' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['chinaventure.com.cn/'], + target: '', + }, + name: '分类', + maintainers: ['yuxinliu-alex'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const currentUrl = rootUrl.concat(id ? `/news/${id}.html` : '/index.html'); @@ -54,11 +77,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${nodes[id] ?? '推荐'}-投中网`, link: currentUrl, description: '投中网是国内领先的创新经济信息服务平台,拥有立体化媒体矩阵,十多年行业深耕,为创新经济领域核心人群提供深入、独到的智识和洞见,在私募股权投资行业和创新商业领域均拥有权威影响力。', language: 'zh-cn', item: items, - }); -}; + }; +} diff --git a/lib/routes/chinawriter/index.ts b/lib/routes/chinawriter/index.ts index 53a234739..5f583dca8 100644 --- a/lib/routes/chinawriter/index.ts +++ b/lib/routes/chinawriter/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: '/:id{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 40; @@ -74,7 +82,7 @@ export default async (ctx) => { const icon = new URL($('link[rel="icon"]').prop('href'), rootUrl); - ctx.set('data', { + return { item: items, title: $('title').text().replaceAll('--', ' - '), link: currentUrl, @@ -85,5 +93,5 @@ export default async (ctx) => { logo: icon, subtitle: $('meta[name="keywords"]').prop('content'), allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/chsi/hotnews.ts b/lib/routes/chsi/hotnews.ts index dbe53e835..9c56f0345 100644 --- a/lib/routes/chsi/hotnews.ts +++ b/lib/routes/chsi/hotnews.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 host = 'https://yz.chsi.com.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/hotnews', + categories: ['journal'], + example: '/chsi/hotnews', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['yz.chsi.com.cn/'], + }, + name: '考研热点新闻', + maintainers: ['yanbot-team'], + handler, +}; + +async function handler() { const response = await got(host); const $ = load(response.data); const list = $('.focus-part .index-hot a'); @@ -40,10 +62,10 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `中国研究生招生信息网 - 热点`, link: host, description: '中国研究生招生信息网 - 热点', item: items, - }); -}; + }; +} diff --git a/lib/routes/chsi/kydt.ts b/lib/routes/chsi/kydt.ts index 06c4d3ca1..ca82fa2cc 100644 --- a/lib/routes/chsi/kydt.ts +++ b/lib/routes/chsi/kydt.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://yz.chsi.com.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/kydt', + categories: ['journal'], + example: '/chsi/kydt', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['yz.chsi.com.cn/kyzx/kydt'], + }, + name: '考研动态', + maintainers: ['SunBK201'], + handler, +}; + +async function handler() { const response = await got(`${host}/kyzx/kydt`); const $ = load(response.data); @@ -38,10 +60,10 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `中国研究生招生信息网 - 考研动态`, link: `${host}/kyzx/kydt/`, description: '中国研究生招生信息网 - 考研动态', item: items, - }); -}; + }; +} diff --git a/lib/routes/chsi/kyzx.ts b/lib/routes/chsi/kyzx.ts index 61a360270..4b8432793 100644 --- a/lib/routes/chsi/kyzx.ts +++ b/lib/routes/chsi/kyzx.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://yz.chsi.com.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/kyzx/:type', + categories: ['journal'], + example: '/chsi/kyzx/fstj', + parameters: { type: ' type 见下表,亦可在网站 URL 找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['yz.chsi.com.cn/kyzx/:type'], + }, + name: '考研资讯', + maintainers: ['yanbot-team'], + handler, +}; + +async function handler(ctx) { const { type } = ctx.req.param(); const response = await got(`${host}/kyzx/${type}`); const $ = load(response.data); @@ -39,10 +61,10 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `中国研究生招生信息网 - ${typeName}`, link: `${host}/kyzx/${type}/`, description: `中国研究生招生信息网 - ${typeName}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/chuanliu/nice.ts b/lib/routes/chuanliu/nice.ts index 73e659677..587662264 100644 --- a/lib/routes/chuanliu/nice.ts +++ b/lib/routes/chuanliu/nice.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 md = MarkdownIt({ html: true, }); -export default async (ctx) => { +export const route: Route = { + path: '/nice', + categories: ['programming'], + example: '/chuanliu/nice', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['chuanliu.org/nice'], + }, + name: '严选', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 100; const rootUrl = 'https://chuanliu.org'; @@ -65,7 +87,7 @@ export default async (ctx) => { const icon = new URL($('link[rel="shortcut icon"]').prop('href'), currentUrl).href; - ctx.set('data', { + return { item: items, title: $('title').text(), link: currentUrl, @@ -76,5 +98,5 @@ export default async (ctx) => { subtitle: $('title').text(), author: $('meta[name="author"]').prop('content'), allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/cib/whpj.ts b/lib/routes/cib/whpj.ts index 831736f2e..b3ee81a28 100644 --- a/lib/routes/cib/whpj.ts +++ b/lib/routes/cib/whpj.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 https from 'https'; import crypto from 'crypto'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/whpj/:format?', + categories: ['other'], + example: '/cib/whpj/xh?filter_title=USD', + parameters: { format: '输出的标题格式,默认为标题 + 所有价格。短格式仅包含货币名称。' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['cib.com.cn/'], + target: '/whpj', + }, + name: '外汇牌价', + maintainers: ['Qixingchen'], + handler, +}; + +async function handler(ctx) { // fix unsafe legacy renegotiation disabled const agent = new https.Agent({ secureOptions: crypto.constants.SSL_OP_LEGACY_SERVER_CONNECT, @@ -38,7 +61,7 @@ export default async (ctx) => { const format = ctx.req.param('format'); - ctx.set('data', { + return { title: '中国兴业银行外汇牌价', link: 'https://personalbank.cib.com.cn/pers/main/pubinfo/ifxQuotationQuery.do', item: data.rows.map((item) => { @@ -61,8 +84,8 @@ export default async (ctx) => { guid: `${item.cell[0]} ${item.cell[1]} ${date}`, }; }), - }); -}; + }; +} function formatTitle(name, xhmr, xcmr, xhmc, xcmc, content, format) { switch (format) { diff --git a/lib/routes/ciidbnu/index.ts b/lib/routes/ciidbnu/index.ts index 123ffaf70..010740346 100644 --- a/lib/routes/ciidbnu/index.ts +++ b/lib/routes/ciidbnu/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 { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -export default async (ctx) => { +export const route: Route = { + path: '/:id?', + categories: ['traditional-media'], + example: '/ciidbnu', + 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') ?? '1'; const rootUrl = 'http://www.ciidbnu.org'; @@ -50,9 +69,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('h3').text()} - 中国收入分配研究院`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/civitai/discussions.ts b/lib/routes/civitai/discussions.ts index 16deacddb..f978d37c3 100644 --- a/lib/routes/civitai/discussions.ts +++ b/lib/routes/civitai/discussions.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { config } from '@/config'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/discussions/:modelId', + categories: ['university'], + example: '/civitai/discussions/4384', + parameters: { modelId: 'N' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['civitai.com/models/:modelId'], + }, + name: 'Model discussions', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { const params = ctx.req.param(); const modelId = Number.parseInt(params.modelId); @@ -34,9 +56,9 @@ export default async (ctx) => { ) .filter(Boolean); - ctx.set('data', { + return { title: `Civitai model ${params.modelId} discussions`, link: `https://civitai.com/`, item: items, - }); -}; + }; +} diff --git a/lib/routes/civitai/models.ts b/lib/routes/civitai/models.ts index 0e66db68b..75956612b 100644 --- a/lib/routes/civitai/models.ts +++ b/lib/routes/civitai/models.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: '/models', + categories: ['university'], + example: '/civitai/models', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['civitai.com/'], + }, + name: 'Latest models', + maintainers: ['DIYgod'], + handler, +}; + +async function handler() { const { data } = await got(`https://civitai.com/api/v1/models`, { searchParams: { limit: 20, @@ -18,9 +40,9 @@ export default async (ctx) => { category: item.tags, })); - ctx.set('data', { + return { title: `Civitai latest models`, link: `https://civitai.com/`, item: items, - }); -}; + }; +} diff --git a/lib/routes/ciweimao/chapter.ts b/lib/routes/ciweimao/chapter.ts index ebe69bded..d9ac44875 100644 --- a/lib/routes/ciweimao/chapter.ts +++ b/lib/routes/ciweimao/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: '/chapter/:id', + categories: ['government'], + example: '/ciweimao/chapter/100043404', + parameters: { id: '小说 id, 可在对应小说页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['wap.ciweimao.com/book/:id'], + }, + name: '章节', + maintainers: ['keocheung'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const limit = Number.parseInt(ctx.req.query('limit')) || 10; @@ -52,11 +74,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `刺猬猫 ${$('.book-name').text()}`, link: `${baseUrl}/book/${id}`, description: $('.book-desc div p').text(), image: $('meta[name=image]').attr('content'), item: items, - }); -}; + }; +} diff --git a/lib/routes/clickme/index.ts b/lib/routes/clickme/index.ts index 95926ceb5..6c6350180 100644 --- a/lib/routes/clickme/index.ts +++ b/lib/routes/clickme/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: '/:site/:grouping/:name', + categories: ['other'], + example: '/clickme/default/category/beauty', + parameters: { site: '站点,`default`为普通站,`r18`为成人站,其它值默认为普通站', grouping: '分组方式,`category`为分类,`tag`为标签,其他值默认为分类', name: '分类名或标签名,分类名为英文,可以在分类 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '文章', + maintainers: ['hoilc'], + handler, +}; + +async function handler(ctx) { const site = ctx.req.param('site') === 'r18' ? 'r18' : ''; const grouping = ctx.req.param('grouping') === 'tag' ? 'tag' : 'category'; const name = ctx.req.param('name'); @@ -49,9 +68,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `ClickMe ${site ? 'R18 ' : ''}- ${displayed_name}`, link: url, item: out, - }); -}; + }; +} diff --git a/lib/routes/cloudnative/blog.ts b/lib/routes/cloudnative/blog.ts index 2ff6f9d14..ebf4e4b20 100644 --- a/lib/routes/cloudnative/blog.ts +++ b/lib/routes/cloudnative/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'; @@ -24,11 +25,29 @@ async function getArticles() { }); } -export default async (ctx) => { +export const route: Route = { + path: '/blog', + categories: ['programming'], + example: '/cloudnative/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(); - ctx.set('data', { + return { title: '博客 | 云原生社区(中国)', link: 'https://cloudnative.to/blog/', item: articles, - }); -}; + }; +} diff --git a/lib/routes/cls/depth.ts b/lib/routes/cls/depth.ts index e7c075f7d..a19aa5905 100644 --- a/lib/routes/cls/depth.ts +++ b/lib/routes/cls/depth.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 categories = { 1176: '投教', }; -export default async (ctx) => { +export const route: Route = { + path: '/depth/:category?', + categories: ['other'], + example: '/cls/depth/1000', + parameters: { category: '分类代码,可在首页导航栏的目标网址 URL 中找到' }, + 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') ?? '1000'; const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 50; @@ -77,9 +96,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `财联社 - ${title}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/cls/hot.ts b/lib/routes/cls/hot.ts index 0f637d78f..4a9b5d263 100644 --- a/lib/routes/cls/hot.ts +++ b/lib/routes/cls/hot.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 * as path from 'node:path'; import { rootUrl, getSearchParams } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/hot', + categories: ['other'], + example: '/cls/hot', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['cls.cn/'], + }, + name: '热门文章排行榜', + maintainers: ['5upernova-heng', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 50; const apiUrl = `${rootUrl}/v2/article/hot/list`; @@ -50,9 +72,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '财联社 - 热门文章排行榜', link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/cls/telegraph.ts b/lib/routes/cls/telegraph.ts index 9893655b8..8556a6512 100644 --- a/lib/routes/cls/telegraph.ts +++ b/lib/routes/cls/telegraph.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -19,7 +20,29 @@ const categories = { hk: '港股', }; -export default async (ctx) => { +export const route: Route = { + path: '/telegraph/:category?', + categories: ['other'], + example: '/cls/telegraph', + parameters: { category: '分类,见下表,默认为全部' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['cls.cn/telegraph', 'cls.cn/'], + target: '/telegraph', + }, + 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')) : 50; @@ -49,9 +72,9 @@ export default async (ctx) => { category: item.subjects?.map((s) => s.subject_name), })); - ctx.set('data', { + return { title: `财联社 - 电报${category === '' ? '' : ` - ${categories[category]}`}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/cma/channel.ts b/lib/routes/cma/channel.ts index fe29423c1..df6a2a1d0 100644 --- a/lib/routes/cma/channel.ts +++ b/lib/routes/cma/channel.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/:id?', + categories: ['travel'], + example: '/cma/channel/380', + parameters: { id: '分类,见下表,可在对应频道页 URL 中找到,默认为 380,即每日天气提示' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '天气预报频道', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const { id = '380' } = ctx.req.param(); const author = '中国气象局·天气预报'; @@ -74,7 +93,7 @@ export default async (ctx) => { ] : []; - ctx.set('data', { + return { item: items, title: `${author} - ${title}`, link: currentUrl, @@ -86,5 +105,5 @@ export default async (ctx) => { subtitle: $('meta[name="keywords"]').prop('content'), author, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/cmde/index.ts b/lib/routes/cmde/index.ts index 2e40d5610..c0d375e22 100644 --- a/lib/routes/cmde/index.ts +++ b/lib/routes/cmde/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; @@ -6,7 +7,14 @@ import puppeteer from '@/utils/puppeteer'; const rootURL = 'https://www.cmde.org.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/:cate{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const cate = ctx.req.param('cate') ?? 'xwdt/zxyw'; const url = `${rootURL}/${cate}/`; const browser = await puppeteer({ stealth: true }); @@ -65,10 +73,10 @@ export default async (ctx) => { await browser.close(); - ctx.set('data', { + return { title: data.title, description: data.description, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/cmpxchg8b/articles.ts b/lib/routes/cmpxchg8b/articles.ts index 4b3e22ea7..1f5a0fecc 100644 --- a/lib/routes/cmpxchg8b/articles.ts +++ b/lib/routes/cmpxchg8b/articles.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 { load } from 'cheerio'; const baseUrl = 'https://lock.cmpxchg8b.com/'; const title = 'cmpxchg8b'; -export default async (ctx) => { +export const route: Route = { + path: '/articles', + categories: ['programming'], + example: '/cmpxchg8b/articles', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['lock.cmpxchg8b.com/articles'], + }, + name: 'Articles', + maintainers: ['yuguorui'], + handler, +}; + +async function handler() { const { data: response } = await got(baseUrl); const $ = load(response); @@ -36,9 +58,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title, link: new URL('#articles', baseUrl).toString(), item: items, - }); -}; + }; +} diff --git a/lib/routes/cn-healthcare/index.ts b/lib/routes/cn-healthcare/index.ts index 47201f03f..920b6d8e5 100644 --- a/lib/routes/cn-healthcare/index.ts +++ b/lib/routes/cn-healthcare/index.ts @@ -1,15 +1,37 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; const baseUrl = 'https://www.cn-healthcare.com'; -export default async (ctx) => { +export const route: Route = { + path: '/index', + categories: ['traditional-media'], + example: '/cn-healthcare/index', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['cn-healthcare.com/'], + }, + name: '首页', + maintainers: ['qnloft'], + handler, +}; + +async function handler() { const response = await got({ method: 'get', url: `${baseUrl}/api/article/articlelist?data={%22start%22:%221%22,%22size%22:%2250%22,%22arctype%22:%220%22,%22wmstart%22:%220%22,%22flag%22:%222%22}`, }); const data = response.data.data; const name = '健康界 [cn-healthcare] '; - ctx.set('data', { + return { // 源标题 title: name, // 源链接 @@ -27,5 +49,5 @@ export default async (ctx) => { // 文章链接 link: `${baseUrl}${item.url}`, })), - }); -}; + }; +} diff --git a/lib/routes/cna/index.ts b/lib/routes/cna/index.ts index 54ff8dfd3..85c1947d9 100644 --- a/lib/routes/cna/index.ts +++ b/lib/routes/cna/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: '/:id?', + categories: ['bbs'], + example: '/cna/aall', + parameters: { id: '分类 id 或新闻专题 id。分类 id 见下表,新闻专题 id 為 https://www.cna.com.tw/list/newstopic.aspx 中,連結的數字部份。此參數默认为 aall' }, + 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') || 'aall'; const isTopic = /^\d+$/.test(id); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 20; @@ -55,11 +74,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: metadata.Title, description: metadata.Description, link: metadata.CanonicalUrl, image: metadata.Image, item: items, - }); -}; + }; +} diff --git a/lib/routes/cna/web/index.ts b/lib/routes/cna/web/index.ts index 412a4d036..fe4b7a6fc 100644 --- a/lib/routes/cna/web/index.ts +++ b/lib/routes/cna/web/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: '/web/:id?', + categories: ['bbs'], + example: '/cna/web/aall', + parameters: { id: '分类 id,见上表。此參數默认为 aall' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Unknown', + maintainers: ['dzx-dzx'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') || 'aall'; const rootUrl = /^\d+$/.test(id) ? `https://www.cna.com.tw/topic/newstopic/${id}.aspx` : `https://www.cna.com.tw/list/${id}.aspx`; @@ -49,9 +68,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/cnbc/rss.ts b/lib/routes/cnbc/rss.ts index 4348d5ba3..be4a80c80 100644 --- a/lib/routes/cnbc/rss.ts +++ b/lib/routes/cnbc/rss.ts @@ -1,9 +1,32 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import parser from '@/utils/rss-parser'; -export default async (ctx) => { +export const route: Route = { + path: '/rss/:id?', + categories: ['bbs'], + example: '/cnbc/rss', + parameters: { id: 'Channel ID, can be found in Official RSS URL, `100003114` (Top News) by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.cnbc.com/id/:id/device/rss/rss.html'], + target: '/rss/:id', + }, + name: 'Full article RSS', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { id = '100003114' } = ctx.req.param(); const feed = await parser.parseURL(`https://search.cnbc.com/rs/search/combinedcms/view.xml?partnerId=wrss01&id=${id}`); @@ -45,11 +68,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: feed.title, link: feed.link, description: feed.description, item: items, language: feed.language, - }); -}; + }; +} diff --git a/lib/routes/cnbeta/type.ts b/lib/routes/cnbeta/type.ts index fa1f5e711..5c78e8d60 100644 --- a/lib/routes/cnbeta/type.ts +++ b/lib/routes/cnbeta/type.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 { parseDate } from '@/utils/parse-date'; import { rootUrl, ProcessItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: ['/:type/:id', '/'], + radar: { + source: ['cnbeta.com.tw/'], + target: '', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { type, id } = ctx.req.param(); const currentUrl = type ? `${rootUrl}/${type}/${id}.htm` : rootUrl; @@ -35,9 +47,9 @@ export default async (ctx) => { category: item.label.name, })); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: await ProcessItems(items, ctx.req.query('limit'), cache.tryGet), - }); -}; + }; +} diff --git a/lib/routes/cnblogs/common.ts b/lib/routes/cnblogs/common.ts index cb6c82cad..cb16d86b8 100644 --- a/lib/routes/cnblogs/common.ts +++ b/lib/routes/cnblogs/common.ts @@ -1,10 +1,32 @@ +import { Route } from '@/types'; import { getSubPath } from '@/utils/common-utils'; 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: ['/aggsite/topdiggs', '/aggsite/topviews', '/aggsite/headline', '/cate/:type', '/pick'], + categories: ['programming'], + example: '/cnblogs/aggsite/topdiggs', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.cnblogs.com/aggsite/topdiggs'], + }, + name: '10 天推荐排行榜', + maintainers: ['hujingnb'], + handler, +}; + +async function handler(ctx) { const link = `https://www.cnblogs.com${getSubPath(ctx)}`; const response = await got(link); const data = response.data; @@ -23,9 +45,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('title').text(), link, item: list, - }); -}; + }; +} diff --git a/lib/routes/cncf/index.ts b/lib/routes/cncf/index.ts index e09a11683..f8f5d689b 100644 --- a/lib/routes/cncf/index.ts +++ b/lib/routes/cncf/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.cncf.io'; -export default async (ctx) => { +export const route: Route = { + path: '/:cate?', + categories: ['design'], + example: '/cncf', + parameters: { cate: 'blog by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Category', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const cate = ctx.req.param('cate') ?? 'blog'; const url = `${rootURL}/${cate}/`; @@ -36,9 +55,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `CNCF - ${title}`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/cncf/reports.ts b/lib/routes/cncf/reports.ts index 4fb631623..8750cd3c7 100644 --- a/lib/routes/cncf/reports.ts +++ b/lib/routes/cncf/reports.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,17 @@ import { parseDate } from '@/utils/parse-date'; const rootURL = 'https://www.cncf.io'; -export default async (ctx) => { +export const route: Route = { + path: '/reports', + radar: { + source: ['cncf.io/reports'], + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler() { const url = `${rootURL}/reports/`; const response = await got(url); @@ -31,9 +42,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `CNCF - Reports`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/cneb/yjxw.ts b/lib/routes/cneb/yjxw.ts index 4a99c52db..645174ced 100644 --- a/lib/routes/cneb/yjxw.ts +++ b/lib/routes/cneb/yjxw.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -9,7 +10,28 @@ const indexs = { gjxw: 1, }; -export default async (ctx) => { +export const route: Route = { + path: '/yjxw/:category?', + categories: ['travel'], + example: '/cneb/yjxw', + parameters: { category: '分类,见下表,默认为全部' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['cneb.gov.cn/yjxw/:category?', 'cneb.gov.cn/'], + }, + name: '应急新闻', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 400; const category = ctx.req.param('category') ?? ''; @@ -65,9 +87,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `国家应急广播 - ${index === -1 ? '新闻' : $('.select').text()}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/cneb/yjxx.ts b/lib/routes/cneb/yjxx.ts index 2b96944c9..5df457ab1 100644 --- a/lib/routes/cneb/yjxx.ts +++ b/lib/routes/cneb/yjxx.ts @@ -1,9 +1,21 @@ +import { Route } from '@/types'; import { getSubPath } from '@/utils/common-utils'; 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: '/yjxx/*', + radar: { + source: ['cneb.gov.cn/yjxx', 'cneb.gov.cn/'], + target: '/yjxx', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 200; const options = decodeURI(getSubPath(ctx)) @@ -42,10 +54,10 @@ export default async (ctx) => { pubDate: timezone(parseDate(item.docpubtime), +8), })); - ctx.set('data', { + return { title: `国家应急广播 - ${title}预警信息`, link: currentUrl, item: items, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/cngal/entry.ts b/lib/routes/cngal/entry.ts index f77536950..173e246cf 100644 --- a/lib/routes/cngal/entry.ts +++ b/lib/routes/cngal/entry.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -7,14 +8,35 @@ import * as path from 'node:path'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -export default async (ctx) => { +export const route: Route = { + path: '/entry/:id', + categories: ['program-update'], + example: '/cngal/entry/2693', + parameters: { id: '词条ID,游戏或制作者页面URL的最后一串数字' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.cngal.org/entries/index/:id'], + }, + name: '制作者 / 游戏新闻', + maintainers: ['chengyuhui'], + handler, +}; + +async function handler(ctx) { const entryId = ctx.req.param('id'); const response = await got(`https://www.cngal.org/api/entries/GetEntryView/${entryId}`); const data = response.data; - ctx.set('data', { + return { title: `CnGal - ${data.name} 的动态`, link: `https://www.cngal.org/entries/index/${entryId}`, item: data.newsOfEntry.map((item) => ({ @@ -23,6 +45,6 @@ export default async (ctx) => { pubDate: timezone(parseDate(item.happenedTime), +8), link: item.link, })), - }); + }; ctx.set('json', response.data); -}; +} diff --git a/lib/routes/cngal/weekly.ts b/lib/routes/cngal/weekly.ts index 9d27b0764..8a0a0e776 100644 --- a/lib/routes/cngal/weekly.ts +++ b/lib/routes/cngal/weekly.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -6,10 +7,31 @@ import { art } from '@/utils/render'; import * as path from 'node:path'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/weekly', + categories: ['program-update'], + example: '/cngal/weekly', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.cngal.org/', 'www.cngal.org/weeklynews'], + }, + name: '每周速报', + maintainers: ['chengyuhui'], + handler, +}; + +async function handler(ctx) { const response = await got('https://www.cngal.org/api/news/GetWeeklyNewsOverview'); - ctx.set('data', { + return { title: 'CnGal - 每周速报', link: 'https://www.cngal.org/weeklynews', item: response.data.map((item) => ({ @@ -18,6 +40,6 @@ export default async (ctx) => { pubDate: parseDate(item.lastEditTime), link: `https://www.cngal.org/articles/index/${item.id}`, })), - }); + }; ctx.state.json = response.data; -}; +} diff --git a/lib/routes/cnjxol/index.ts b/lib/routes/cnjxol/index.ts index 605ab76c6..aa4734859 100644 --- a/lib/routes/cnjxol/index.ts +++ b/lib/routes/cnjxol/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -13,7 +14,14 @@ const categories = { nhwb: '南湖晚报', }; -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') ?? 'jxrb'; const id = ctx.req.param('id'); if (!Object.keys(categories).includes(category)) { @@ -93,9 +101,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${categories[category]}${id ? ` - ${$('#layout').text()}` : ''}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/cnki/author.ts b/lib/routes/cnki/author.ts index ecbe40fc3..3e667c4a0 100644 --- a/lib/routes/cnki/author.ts +++ b/lib/routes/cnki/author.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 utils from './utils'; const rootUrl = 'https://kns.cnki.net'; -export default async (ctx) => { +export const route: Route = { + path: '/author/:code', + categories: ['finance'], + example: '/cnki/author/000042423923', + parameters: { code: '作者对应code,可以在网址中得到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Unknown', + maintainers: ['harveyqiu', 'Derekmini'], + handler, +}; + +async function handler(ctx) { const code = ctx.req.param('code'); const authorInfoUrl = `${rootUrl}/kcms/detail/knetsearch.aspx?sfield=au&code=${code}`; @@ -50,9 +69,9 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => cache.tryGet(item.link, () => utils.ProcessItem(item)))); - ctx.set('data', { + return { title: `知网 ${authorName} ${companyName}`, link: authorInfoUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/cnki/debut.ts b/lib/routes/cnki/debut.ts index 9f5fd3799..20f5686c4 100644 --- a/lib/routes/cnki/debut.ts +++ b/lib/routes/cnki/debut.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 * as path from 'node:path'; const rootUrl = 'https://chn.oversea.cnki.net'; -export default async (ctx) => { +export const route: Route = { + path: '/journals/debut/:name', + categories: ['finance'], + example: '/cnki/journals/debut/LKGP', + parameters: { name: '期刊缩写,可以在网址中得到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['navi.cnki.net/knavi/journals/:name/detail'], + }, + name: '网络首发', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const name = ctx.req.param('name'); const journalUrl = `${rootUrl}/knavi/JournalDetail?pcode=CjFD&pykm=${name}`; @@ -58,9 +80,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${title} - 全网首发`, link: `https://navi.cnki.net/knavi/journals/${name}/detail`, item: items, - }); -}; + }; +} diff --git a/lib/routes/cnki/journals.ts b/lib/routes/cnki/journals.ts index afda5fdfb..d4af0a05f 100644 --- a/lib/routes/cnki/journals.ts +++ b/lib/routes/cnki/journals.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 { ProcessItem } from './utils'; const rootUrl = 'https://navi.cnki.net'; -export default async (ctx) => { +export const route: Route = { + path: '/journals/:name', + categories: ['finance'], + example: '/cnki/journals/LKGP', + parameters: { name: '期刊缩写,可以在网址中得到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['navi.cnki.net/knavi/journals/:name/detail'], + }, + name: '期刊', + maintainers: ['Fatpandac', 'Derekmini'], + handler, +}; + +async function handler(ctx) { const name = ctx.req.param('name'); const journalUrl = `${rootUrl}/knavi/journals/${name}/detail`; const title = await got.get(journalUrl).then((res) => load(res.data)('head > title').text()); @@ -42,9 +64,9 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => cache.tryGet(item.link, () => ProcessItem(item)))); - ctx.set('data', { + return { title: String(title), link: journalUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/cnljxh/index.ts b/lib/routes/cnljxh/index.ts index c508827e3..b7d3297f9 100644 --- a/lib/routes/cnljxh/index.ts +++ b/lib/routes/cnljxh/index.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,14 @@ const defaultIds = { price: '299', }; -export default async (ctx) => { +export const route: Route = { + path: '/:category?/:id?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { category = 'news', id = Object.hasOwn(defaultIds, category) ? defaultIds[category] : '10' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 10; @@ -57,7 +65,7 @@ export default async (ctx) => { const image = new URL($('div.logo a img').prop('src'), currentUrl).href; const icon = new URL($('link[rel="shortcut icon"]').prop('href'), rootUrl).href; - ctx.set('data', { + return { item: items, title: `${author} - ${subtitle}`, link: currentUrl, @@ -68,5 +76,5 @@ export default async (ctx) => { logo: icon, subtitle, author, - }); -}; + }; +} diff --git a/lib/routes/cntheory/paper.ts b/lib/routes/cntheory/paper.ts index 0702bcea9..5a4cdc59c 100644 --- a/lib/routes/cntheory/paper.ts +++ b/lib/routes/cntheory/paper.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: '/paper/:id?', + categories: ['bbs'], + example: '/cntheory/paper', + 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'); const rootUrl = 'https://paper.cntheory.com'; @@ -82,10 +101,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `学习时报${id ? ` - ${id}` : ''}`, link: rootUrl, item: items, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/cntv/column.ts b/lib/routes/cntv/column.ts index c5a75fe14..5c5b35c95 100644 --- a/lib/routes/cntv/column.ts +++ b/lib/routes/cntv/column.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 { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:column', + categories: ['picture'], + example: '/cntv/TOPC1451528971114112', + parameters: { column: '栏目ID, 可在对应CNTV栏目页面找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['navi.cctv.com/'], + }, + name: '栏目', + maintainers: ['WhoIsSure', 'Fatpandac'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('column'); const limit = isNaN(Number.parseInt(ctx.req.query('limit'))) ? 25 : Number.parseInt(ctx.req.query('limit')); @@ -17,7 +39,7 @@ export default async (ctx) => { const data = response.data.data.list; const name = data[0].title.match(/《(.*?)》/)[1]; - ctx.set('data', { + return { title: `CNTV 栏目 - ${name}`, description: `${name} 栏目的视频更新`, item: data.map((item) => ({ @@ -28,5 +50,5 @@ export default async (ctx) => { pubDate: parseDate(item.time), link: item.url, })), - }); -}; + }; +} diff --git a/lib/routes/codeforces/contests.ts b/lib/routes/codeforces/contests.ts index 57a56fafa..745c1dc3c 100644 --- a/lib/routes/codeforces/contests.ts +++ b/lib/routes/codeforces/contests.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -20,7 +21,28 @@ const sec2str = (sec) => dayjs.duration(Number.parseInt(sec), 'seconds').humaniz const contestAPI = 'https://codeforces.com/api/contest.list'; -export default async (ctx) => { +export const route: Route = { + path: '/contests', + categories: ['design'], + example: '/codeforces/contests', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.codeforces.com/contests'], + }, + name: 'Latest contests', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler() { const contestsData = await got.get(contestAPI).json(); const contests = contestsData.result; @@ -44,9 +66,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: 'Codeforces - Contests', link: 'https://codeforces.com/contests', item: items, - }); -}; + }; +} diff --git a/lib/routes/codeforces/recent-actions.ts b/lib/routes/codeforces/recent-actions.ts index fa02d29a6..c3a9cea0a 100644 --- a/lib/routes/codeforces/recent-actions.ts +++ b/lib/routes/codeforces/recent-actions.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: '/recent-actions/:minrating?', + categories: ['design'], + example: '/codeforces/recent-actions', + parameters: { minrating: 'The minimum blog/comment rating required. Default: 1' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['codeforces.com/recent-actions'], + target: '/recent-actions', + }, + name: 'Recent actions', + maintainers: [], + handler, +}; + +async function handler(ctx) { const minRating = ctx.req.param('minrating') || 1; const rsp = await got.get('https://codeforces.com/api/recentActions?maxCount=100').json(); @@ -32,7 +55,7 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: 'Codeforces - Recent actions', link: 'https://codeforces.com/recent-actions', item: actions @@ -43,5 +66,5 @@ export default async (ctx) => { pubDate: a.pubDate, link: a.link, })), - }); -}; + }; +} diff --git a/lib/routes/coindesk/index.ts b/lib/routes/coindesk/index.ts index f15faccc5..0a9c1f2c5 100644 --- a/lib/routes/coindesk/index.ts +++ b/lib/routes/coindesk/index.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; const rootUrl = 'https://www.coindesk.com'; -export default async (ctx) => { +export const route: Route = { + path: '/consensus-magazine', + categories: ['traditional-media'], + example: '/coindesk/consensus-magazine', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['coindesk.com/'], + }, + name: '新闻周刊', + maintainers: ['jameshih'], + handler, +}; + +async function handler(ctx) { const channel = ctx.req.param('channel') ?? 'consensus-magazine'; const response = await got.get(`${rootUrl}/${channel}/`); @@ -26,9 +48,9 @@ export default async (ctx) => { pubDate: item.display_date, })); - ctx.set('data', { + return { title: 'CoinDesk Consensus Magazine', link: `${rootUrl}/${channel}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/comicat/search.ts b/lib/routes/comicat/search.ts index 0c7d80208..a59ffb02e 100644 --- a/lib/routes/comicat/search.ts +++ b/lib/routes/comicat/search.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 baseUrl = 'https://comicat.org'; -export default async (ctx) => { +export const route: Route = { + path: '/search/:keyword', + categories: ['program-update'], + example: '/comicat/search/喵萌奶茶屋+跃动青春+720P+简日', + parameters: { keyword: '关键词,请用`+`号连接' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: true, + supportPodcast: false, + supportScihub: false, + }, + name: '搜索关键词', + maintainers: ['Cyang39'], + handler, +}; + +async function handler(ctx) { const keyword = ctx.req.param('keyword'); const { data: response } = await got(`${baseUrl}/search.php?keyword=${encodeURIComponent(keyword)}`); const $ = load(response); @@ -35,9 +54,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `Comicat - ${keyword}`, link: `${baseUrl}/search.php?keyword=${encodeURIComponent(keyword)}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/comicskingdom/index.ts b/lib/routes/comicskingdom/index.ts index 6ce3d402b..20f99d719 100644 --- a/lib/routes/comicskingdom/index.ts +++ b/lib/routes/comicskingdom/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: '/:name', + categories: ['program-update'], + example: '/comicskingdom/pardon-my-planet', + parameters: { name: 'URL path of the strip on comicskingdom.com' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['comicskingdom.com/:name/*', 'comicskingdom.com/:name'], + }, + name: 'Archive', + maintainers: ['stjohnjohnson'], + handler, +}; + +async function handler(ctx) { const baseURL = 'https://comicskingdom.com'; const name = ctx.req.param('name'); const url = `${baseURL}/${name}/archive`; @@ -54,11 +76,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: comic, link: url, image: $('.feature-logo').attr('src'), item: items, language: 'en-US', - }); -}; + }; +} diff --git a/lib/routes/consumer/index.ts b/lib/routes/consumer/index.ts index 7283f7029..faf0d2314 100644 --- a/lib/routes/consumer/index.ts +++ b/lib/routes/consumer/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?/:language?/:keyword?', + categories: ['traditional-media'], + example: '/consumer', + parameters: { category: '分类,见下表,默认为測試及調查', language: '语言,见下表,默认为繁体中文', keyword: '关键字,默认为空' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['consumer.org.hk/'], + }, + name: '文章', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'test'; const language = ctx.req.param('language') ?? 'tc'; const keyword = ctx.req.param('keyword') ?? ''; @@ -48,9 +70,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/consumer/shopping-guide.ts b/lib/routes/consumer/shopping-guide.ts index 1bfe70d5e..b50d27040 100644 --- a/lib/routes/consumer/shopping-guide.ts +++ b/lib/routes/consumer/shopping-guide.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: '/shopping-guide/:category?/:language?', + categories: ['traditional-media'], + example: '/consumer/shopping-guide', + parameters: { category: '分类,见下表,默认为 `trivia`', language: '语言,见上表,默认为 `tc`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '消費全攻略', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { category = 'trivia', language = 'tc' } = ctx.req.param(); const rootUrl = 'https://www.consumer.org.hk'; const currentUrl = `${rootUrl}/${language}/shopping-guide/${category}`; @@ -56,12 +75,12 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), link: currentUrl, image: $('meta[property="og:image"]').attr('content'), logo: $('link[rel="apple-touch-icon"]').attr('href'), icon: $('link[rel="apple-touch-icon"]').attr('href'), item: items, - }); -}; + }; +} diff --git a/lib/routes/cool18/index.ts b/lib/routes/cool18/index.ts index 7ce45f0c7..4c1242697 100644 --- a/lib/routes/cool18/index.ts +++ b/lib/routes/cool18/index.ts @@ -1,9 +1,20 @@ +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?/:type?/:keyword?', + radar: { + source: ['cool18.com/'], + }, + name: 'Unknown', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? 'bbs4'; const type = ctx.req.param('type') ?? ''; const keyword = ctx.req.param('keyword') ?? ''; @@ -54,9 +65,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/coolapk/dyh.ts b/lib/routes/coolapk/dyh.ts index 3ef4533c4..69d85890e 100644 --- a/lib/routes/coolapk/dyh.ts +++ b/lib/routes/coolapk/dyh.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/dyh/:dyhId', + categories: ['new-media'], + example: '/coolapk/dyh/1524', + parameters: { dyhId: '看看号ID' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '看看号', + maintainers: ['xizeyoupan'], + handler, +}; + +async function handler(ctx) { const dyhId = ctx.req.param('dyhId'); const full_url = utils.base_url + `/v6/dyhArticle/list?dyhId=${dyhId}&type=all&page=1`; const r = await got(`${utils.base_url}/v6/dyh/detail?dyhId=${dyhId}`, { @@ -28,10 +47,10 @@ export default async (ctx) => { if (out.length === 0) { throw new Error('仅限于采集站内订阅的看看号的图文及动态内容。这个ID可能是站外订阅。'); } - ctx.set('data', { + return { title: `酷安看看号-${targetTitle}`, link: `https://www.coolapk.com/dyh/${dyhId}`, description: feedDescription, item: out, - }); -}; + }; +} diff --git a/lib/routes/coolapk/hot.ts b/lib/routes/coolapk/hot.ts index d59cd0577..06d0a1420 100644 --- a/lib/routes/coolapk/hot.ts +++ b/lib/routes/coolapk/hot.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import utils from './utils'; const getLinkAndTitle = (type, period) => { @@ -65,7 +66,25 @@ const getLinkAndTitle = (type, period) => { return res; }; -export default async (ctx) => { +export const route: Route = { + path: '/hot/:type?/:period?', + categories: ['new-media'], + example: '/coolapk/hot', + parameters: { type: '默认为`jrrm`', period: '默认为`daily`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '热榜', + maintainers: ['xizeyoupan'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') || 'jrrm'; const period = ctx.req.param('period') || 'daily'; const { link, title } = getLinkAndTitle(type, period); @@ -88,10 +107,10 @@ export default async (ctx) => { out = out.filter(Boolean); - ctx.set('data', { + return { title, link: 'https://www.coolapk.com/', description: `热榜-` + title, item: out, - }); -}; + }; +} diff --git a/lib/routes/coolapk/huati.ts b/lib/routes/coolapk/huati.ts index f01904736..9efa097b1 100644 --- a/lib/routes/coolapk/huati.ts +++ b/lib/routes/coolapk/huati.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/huati/:tag', + categories: ['new-media'], + example: '/coolapk/huati/iPhone', + parameters: { tag: '话题名称' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '话题', + maintainers: ['xizeyoupan'], + handler, +}; + +async function handler(ctx) { const tag = ctx.req.param('tag'); const full_url = utils.base_url + `/v6/page/dataList?url=%23%2Ffeed%2FmultiTagFeedList%3FlistType%3Ddateline_desc%26tag=${tag}&title=%E6%9C%80%E6%96%B0%E5%8F%91%E5%B8%83&subTitle=&page=1`; const response = await got(full_url, { @@ -15,10 +34,10 @@ export default async (ctx) => { if (out.length === 0) { throw new Error('这个话题还没有被创建或现在没有图文及动态内容。'); } - ctx.set('data', { + return { title: `酷安话题-${tag}`, link: `https://www.coolapk.com/`, description: `酷安话题-${tag}`, item: out, - }); -}; + }; +} diff --git a/lib/routes/coolapk/toutiao.ts b/lib/routes/coolapk/toutiao.ts index 8530d7051..c9b9b7309 100644 --- a/lib/routes/coolapk/toutiao.ts +++ b/lib/routes/coolapk/toutiao.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/toutiao/:type?', + categories: ['new-media'], + example: '/coolapk/toutiao', + parameters: { type: '默认为history' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '头条', + maintainers: ['xizeyoupan'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') || 'history'; const urls = { history: { @@ -20,10 +39,10 @@ export default async (ctx) => { const out = await Promise.all(data.map((item) => utils.parseDynamic(item))); - ctx.set('data', { + return { title: urls[type].title, link: 'https://www.coolapk.com/', description: urls[type].title, item: out, - }); -}; + }; +} diff --git a/lib/routes/coolapk/tuwen.ts b/lib/routes/coolapk/tuwen.ts index c27b2f15d..478749584 100644 --- a/lib/routes/coolapk/tuwen.ts +++ b/lib/routes/coolapk/tuwen.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: ['/tuwen/:type?', '/tuwen-xinxian'], + categories: ['new-media'], + example: '/coolapk/tuwen', + parameters: { type: '默认为hot' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '图文', + maintainers: ['xizeyoupan'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') || 'hot'; const requestPath = ctx.req.path; let feedTitle; @@ -28,10 +47,10 @@ export default async (ctx) => { const out = await Promise.all(data.map((item) => utils.parseDynamic(item))); - ctx.set('data', { + return { title: feedTitle, link: 'https://www.coolapk.com/', description: feedTitle, item: out, - }); -}; + }; +} diff --git a/lib/routes/coolapk/user-dynamic.ts b/lib/routes/coolapk/user-dynamic.ts index 5c794bd77..d0b4e1eaa 100644 --- a/lib/routes/coolapk/user-dynamic.ts +++ b/lib/routes/coolapk/user-dynamic.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/user/:uid/dynamic', + categories: ['new-media'], + example: '/coolapk/user/3177668/dynamic', + parameters: { uid: '在个人界面右上分享-复制链接获取' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '用户', + maintainers: ['xizeyoupan'], + handler, +}; + +async function handler(ctx) { const uid = ctx.req.param('uid'); const full_url = utils.base_url + `/v6/user/feedList?uid=${uid}&page=1&showAnonymous=0&isIncludeTop=1&showDoing=1`; let username; @@ -26,10 +45,10 @@ export default async (ctx) => { if (out.length === 0) { throw new Error('这个人还没有图文或动态。'); } - ctx.set('data', { + return { title: `酷安个人动态-${username}`, link: `https://www.coolapk.com/u/${uid}`, description: `酷安个人动态-${username}`, item: out, - }); -}; + }; +} diff --git a/lib/routes/coomer/artist.ts b/lib/routes/coomer/artist.ts index 70694836f..b300aeab2 100644 --- a/lib/routes/coomer/artist.ts +++ b/lib/routes/coomer/artist.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import fetchItems from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/artist/:id', + categories: ['picture'], + example: '/coomer/artist/belledelphine', + parameters: { id: 'Artist id, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['coomer.party/onlyfans/user/:id', 'coomer.party/'], + }, + name: 'Artist', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const currentUrl = `onlyfans/user/${id}`; ctx.set('data', await fetchItems(ctx, currentUrl)); -}; +} diff --git a/lib/routes/coomer/posts.ts b/lib/routes/coomer/posts.ts index fea1c9098..d1c7d387c 100644 --- a/lib/routes/coomer/posts.ts +++ b/lib/routes/coomer/posts.ts @@ -1,7 +1,29 @@ +import { Route } from '@/types'; import fetchItems from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/posts', + categories: ['picture'], + example: '/coomer/posts', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['coomer.party/posts', 'coomer.party/'], + }, + name: 'Recent Posts', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const currentUrl = 'posts'; ctx.set('data', await fetchItems(ctx, currentUrl)); -}; +} diff --git a/lib/routes/copymanga/comic.ts b/lib/routes/copymanga/comic.ts index e1da19a53..68196d364 100644 --- a/lib/routes/copymanga/comic.ts +++ b/lib/routes/copymanga/comic.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 * as path from 'node:path'; import { config } from '@/config'; import asyncPool from 'tiny-async-pool'; -export default async (ctx) => { +export const route: Route = { + path: '/comic/:id/:chapterCnt?', + categories: ['program-update'], + example: '/copymanga/comic/dianjuren/5', + parameters: { id: '漫画ID', chapterCnt: '返回章节的数量,默认为 `10`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '漫画更新', + maintainers: ['btdwv', 'marvolo666', 'yan12125'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); // 用于控制返回的章节数量 const chapterCnt = Number(ctx.req.param('chapterCnt') || 10); @@ -118,10 +137,10 @@ export default async (ctx) => { const result = await asyncPoolAll(3, chapterArray.slice(0, chapterCnt), (chapter) => cache.tryGet(chapter.link, () => genResult(chapter))); const items = [...result, ...chapterArray.slice(chapterCnt)]; - ctx.set('data', { + return { title: `拷贝漫画 - ${bookTitle}`, link: `${baseUrl}/comic/${id}`, description: bookIntro, item: items, - }); -}; + }; +} diff --git a/lib/routes/cpcey/index.ts b/lib/routes/cpcey/index.ts index 990ed2b44..7dfe4f201 100644 --- a/lib/routes/cpcey/index.ts +++ b/lib/routes/cpcey/index.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 = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/:type?', + categories: ['study'], + example: '/cpcey/xwg', + parameters: { type: '默认为 `xwg`' }, + 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') ?? 'xwg'; const url = rootUrl + typeMap[type].url; @@ -53,9 +72,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `行政院消费者保护会-${typeMap[type].name}`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/cpuid/news.ts b/lib/routes/cpuid/news.ts index ffebecddf..0723caaf7 100644 --- a/lib/routes/cpuid/news.ts +++ b/lib/routes/cpuid/news.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 newsUrl = 'https://www.cpuid.com/news.html'; -export default async (ctx) => { +export const route: Route = { + path: '/news', + categories: ['university'], + example: '/cpuid/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['cpuid.com/news.html', 'cpuid.com/'], + }, + name: 'News', + maintainers: [], + handler, +}; + +async function handler() { const response = await got(newsUrl); const $ = load(response.data); @@ -19,12 +41,12 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('head title').text(), description: $('head description').attr('content'), link: newsUrl, image: $('link[rel=apple-touch-icon-precomposed]').attr('href'), item: items, language: $('html').attr('lang'), - }); -}; + }; +} diff --git a/lib/routes/cqgas/tqtz.ts b/lib/routes/cqgas/tqtz.ts index 5c29e60f1..a54fe1533 100644 --- a/lib/routes/cqgas/tqtz.ts +++ b/lib/routes/cqgas/tqtz.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'; const url = 'http://www.cqgas.cn/portal/article/page?cateId=1082&pageNo=1'; -export default async (ctx) => { +export const route: Route = { + path: '/tqtz', + categories: ['travel'], + example: '/cqgas/tqtz', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['cqgas.cn/'], + }, + name: '停气检修通知', + maintainers: ['Mai19930513'], + handler, +}; + +async function handler() { const { data: response } = await got(url); const $ = load(response); const contentUrl = (id) => `http://www.cqgas.cn/portal/article/content?contentId=${id}`; @@ -29,9 +51,9 @@ export default async (ctx) => { }) ) ); - ctx.set('data', { + return { title: '重庆燃气——停气检修通知', link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/cqwu/index.ts b/lib/routes/cqwu/index.ts index 2030bfd42..c176660be 100644 --- a/lib/routes/cqwu/index.ts +++ b/lib/routes/cqwu/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { load } from 'cheerio'; import got from '@/utils/got'; @@ -13,7 +14,25 @@ const titleMap = { academiceve: '学术活动', }; -export default async (ctx) => { +export const route: Route = { + path: '/news/:type?', + categories: ['forecast'], + example: '/cqwu/news/academiceve', + parameters: { type: '可选,默认为 academiceve ' }, + 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') ?? 'academiceve'; const link = host + map[type]; const title = '重文理' + titleMap[type] + '公告'; @@ -42,9 +61,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title, link, item: items, - }); -}; + }; +} diff --git a/lib/routes/crac/index.ts b/lib/routes/crac/index.ts index e75fbd6e3..9d4e9b2fe 100644 --- a/lib/routes/crac/index.ts +++ b/lib/routes/crac/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: '/:type?', + categories: ['study'], + example: '/crac/2', + parameters: { type: '类型,见下表,默认为全部' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '最新资讯', + maintainers: ['Misaka13514'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'http://www.crac.org.cn'; const type = ctx.req.param('type'); const link = type ? `${baseUrl}/News/List?type=${type}` : `${baseUrl}/News/List`; @@ -39,9 +58,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link, item: list, - }); -}; + }; +} diff --git a/lib/routes/creative-comic/book.ts b/lib/routes/creative-comic/book.ts index 2d3350cca..5e25ab9c5 100644 --- a/lib/routes/creative-comic/book.ts +++ b/lib/routes/creative-comic/book.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 { art } from '@/utils/render'; import * as path from 'node:path'; import { getUuid, getBook, getChapter, getChapters, getImgEncrypted, getImgKey, decrypt, getRealKey, siteHost } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/book/:id/:coverOnly?/:quality?', + categories: ['program-update'], + example: '/creative-comic/book/117', + parameters: { id: '漫畫 ID,可在 URL 中找到', coverOnly: '僅獲取封面,非 `true` 時將獲取**全部**頁面,預設 `true`', quality: '閱讀品質,標準畫質 `1`,高畫質 `2`,預設 `1`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['creative-comic.tw/book/:id/*'], + target: '/:id', + }, + name: '漫畫', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { id, coverOnly = 'true', quality = '1' } = ctx.req.param(); const uuid = await getUuid(cache.tryGet); const { @@ -59,12 +82,12 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `${book.name} | CCC創作集`, description: `${book.brief} ${book.description}`, link: book.share_link, image: book.image1, item: items, language: 'zh-hant', - }); -}; + }; +} diff --git a/lib/routes/crossbell/feeds/following.ts b/lib/routes/crossbell/feeds/following.ts index 05675bf27..98e4ff162 100644 --- a/lib/routes/crossbell/feeds/following.ts +++ b/lib/routes/crossbell/feeds/following.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/feeds/following/:characterId', + categories: ['new-media'], + example: '/crossbell/feeds/following/10', + parameters: { characterId: 'N' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Feeds of following', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { const characterId = ctx.req.param('characterId'); const response = await got(`https://indexer.crossbell.io/v1/characters/${characterId}/feed/follow`); - ctx.set('data', { + return { title: 'Crossbell Feeds of ' + characterId, link: 'https://crossbell.io/', item: response.data?.list?.map((item) => { @@ -25,5 +44,5 @@ export default async (ctx) => { category: [...(item.note?.metadata?.content?.sources || []), ...(item.note?.metadata?.content?.tags || [])], }; }), - }); -}; + }; +} diff --git a/lib/routes/crossbell/notes/character.ts b/lib/routes/crossbell/notes/character.ts index 7958b0709..bc010f949 100644 --- a/lib/routes/crossbell/notes/character.ts +++ b/lib/routes/crossbell/notes/character.ts @@ -1,7 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { getItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/notes/character/:characterId', + categories: ['new-media'], + example: '/crossbell/notes/character/10', + parameters: { characterId: 'N' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['crossbell.io/*'], + target: '/notes', + }, + name: 'Notes of character', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { const characterId = ctx.req.param('characterId'); const response = await got('https://indexer.crossbell.io/v1/notes', { @@ -14,9 +37,9 @@ export default async (ctx) => { const name = response.data?.list?.[0]?.character?.metadata?.content?.name || response.data?.list?.[0]?.character?.handle || characterId; const handle = response.data?.list?.[0]?.character?.handle; - ctx.set('data', { + return { title: 'Crossbell Notes from ' + name, link: 'https://xchar.app/' + handle, item: response.data?.list?.map((item) => getItem(item)), - }); -}; + }; +} diff --git a/lib/routes/crossbell/notes/index.ts b/lib/routes/crossbell/notes/index.ts index ac2c681da..da8aee127 100644 --- a/lib/routes/crossbell/notes/index.ts +++ b/lib/routes/crossbell/notes/index.ts @@ -1,16 +1,38 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { getItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/notes', + categories: ['new-media'], + example: '/crossbell/notes', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['crossbell.io/*'], + }, + name: 'Notes', + maintainers: ['DIYgod'], + handler, +}; + +async function handler() { const response = await got('https://indexer.crossbell.io/v1/notes', { searchParams: { includeCharacter: true, }, }); - ctx.set('data', { + return { title: 'Crossbell Notes', link: 'https://crossbell.io/', item: response.data?.list?.map((item) => getItem(item)), - }); -}; + }; +} diff --git a/lib/routes/crossbell/notes/source.ts b/lib/routes/crossbell/notes/source.ts index 7c8563ce0..971fb5c09 100644 --- a/lib/routes/crossbell/notes/source.ts +++ b/lib/routes/crossbell/notes/source.ts @@ -1,7 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { getItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/notes/source/:source', + categories: ['new-media'], + example: '/crossbell/notes/source/xlog', + parameters: { source: 'N' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['crossbell.io/*'], + target: '/notes', + }, + name: 'Notes of source', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { const source = ctx.req.param('source'); const response = await got('https://indexer.crossbell.io/v1/notes', { @@ -11,9 +34,9 @@ export default async (ctx) => { }, }); - ctx.set('data', { + return { title: 'Crossbell Notes from ' + source, link: 'https://crossbell.io/', item: response.data?.list?.map((item) => getItem(item)), - }); -}; + }; +} diff --git a/lib/routes/cs/index.ts b/lib/routes/cs/index.ts index 87872a745..b5c29a328 100644 --- a/lib/routes/cs/index.ts +++ b/lib/routes/cs/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,14 @@ const decodeBufferByCharset = (buffer) => { return iconv.decode(buffer, encoding); }; -export default async (ctx) => { +export const route: Route = { + path: '/:category{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { category = 'xwzx' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; @@ -69,7 +77,7 @@ export default async (ctx) => { const image = new URL($('div.logo_cs a img').prop('src'), currentUrl).href; const icon = new URL('favicon.ico', rootUrl).href; - ctx.set('data', { + return { item: items, title, link: currentUrl, @@ -80,5 +88,5 @@ export default async (ctx) => { logo: icon, subtitle: $('meta[name="Keywords"]').prop('content'), author: title.split('-').pop().trim(), - }); -}; + }; +} diff --git a/lib/routes/cs/video.ts b/lib/routes/cs/video.ts index 26e9832ab..2268b8d9a 100644 --- a/lib/routes/cs/video.ts +++ b/lib/routes/cs/video.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: '/video/:category?', + categories: ['other'], + example: '/cs/video/今日聚焦', + parameters: { category: '分类,见下表,默认为今日聚焦' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Unknown', + 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; @@ -54,7 +73,7 @@ export default async (ctx) => { const image = selected.image; const icon = new URL($('link[rel="icon"]').prop('href'), rootUrl).href; - ctx.set('data', { + return { item: items, title: `${title} | ${selected.title}`, link: currentUrl, @@ -66,5 +85,5 @@ export default async (ctx) => { subtitle: $('meta[name="Keywords"]').prop('content'), author: title.split('-').pop().trim(), allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/cs/zzkx.ts b/lib/routes/cs/zzkx.ts index 38532bde9..0786a2559 100644 --- a/lib/routes/cs/zzkx.ts +++ b/lib/routes/cs/zzkx.ts @@ -1,6 +1,14 @@ -export default (ctx) => { +import { Route } from '@/types'; +export const route: Route = { + path: ['/news/zzkx', '/zzkx'], + name: 'Unknown', + maintainers: [], + handler, +}; + +function handler(ctx) { // https://www.cs.com.cn/sylm/jsbd/ const redirectTo = '/cs/sylm/jsbd'; ctx.redirect(redirectTo); -}; +} diff --git a/lib/routes/csdn/blog.ts b/lib/routes/csdn/blog.ts index 6c806bd0e..a24184c23 100644 --- a/lib/routes/csdn/blog.ts +++ b/lib/routes/csdn/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 rssParser from '@/utils/rss-parser'; -export default async (ctx) => { +export const route: Route = { + path: '/blog/:user', + categories: ['programming'], + example: '/csdn/blog/csdngeeknews', + parameters: { user: '`user` is the username of a CSDN blog which can be found in the url of the home page' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['blog.csdn.net/:user'], + }, + name: 'User Feed', + maintainers: [], + handler, +}; + +async function handler(ctx) { const user = ctx.req.param('user'); const rootUrl = 'https://blog.csdn.net'; @@ -32,9 +54,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { ...feed, title: `${feed.title} - CSDN博客`, item: items, - }); -}; + }; +} diff --git a/lib/routes/cssn/iolaw.ts b/lib/routes/cssn/iolaw.ts index f51c2d8a2..b6b34735e 100644 --- a/lib/routes/cssn/iolaw.ts +++ b/lib/routes/cssn/iolaw.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: '/iolaw/:section?', + categories: ['journal'], + example: '/cssn/iolaw/zxzp', + parameters: { section: 'Section ID, can be found in the URL. For example, the Section ID of URL `http://iolaw.cssn.cn/zxzp/` is `zxzp`. The default value is `zxzp`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Institute of Law', + maintainers: ['HankChow'], + handler, +}; + +async function handler(ctx) { const section = ctx.req.param('section') ?? 'zxzp'; const domain = 'iolaw.cssn.cn'; const response = await got(`http://${domain}/${section}/`); @@ -24,7 +43,7 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title: '中国法学网', url: `http://${domain}/${section}/`, description: '中国法学网', @@ -34,5 +53,5 @@ export default async (ctx) => { link: item.link, author: item.author, })), - }); -}; + }; +} diff --git a/lib/routes/cste/index.ts b/lib/routes/cste/index.ts index 104357003..75012c6a1 100644 --- a/lib/routes/cste/index.ts +++ b/lib/routes/cste/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: '/:id?', + categories: ['journal'], + example: '/cste', + parameters: { id: '分类,见下表,默认为 16,即通知公告' }, + 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') ?? '16'; const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 10; @@ -49,9 +68,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `中国技术经济学会 - ${$('.leftTop').text()}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/csu/career.ts b/lib/routes/csu/career.ts index b9e91ec98..1efc57468 100644 --- a/lib/routes/csu/career.ts +++ b/lib/routes/csu/career.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 { unzip } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/career', + categories: ['forecast'], + example: '/csu/career', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['career.csu.edu.cn/campus/index/category/1', 'career.csu.edu.cn/campus', 'career.csu.edu.cn/'], + }, + name: '就业信息网招聘信息', + maintainers: ['TonyRL'], + handler, +}; + +async function handler() { const baseUrl = 'https://career.csu.edu.cn'; const link = `${baseUrl}/campus/index/category/1`; const { data: response } = await got(link); @@ -44,9 +66,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('.curr').text()} - ${$('head title').text()}`, link, item: items, - }); -}; + }; +} diff --git a/lib/routes/csu/cse.ts b/lib/routes/csu/cse.ts index 57553ccd9..0797723f3 100644 --- a/lib/routes/csu/cse.ts +++ b/lib/routes/csu/cse.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,25 @@ async function fetch(address) { }; } -export default async (ctx) => { +export const route: Route = { + path: '/cse/:type?', + categories: ['forecast'], + example: '/csu/cse', + parameters: { type: '类型' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '计算机学院', + maintainers: ['j1g5awi'], + handler, +}; + +async function handler(ctx) { const url = 'https://cse.csu.edu.cn/index/'; const type = ctx.req.param('type') ?? 'tzgg'; const link = url + type + '.htm'; @@ -34,9 +53,9 @@ export default async (ctx) => { }); }) ); - ctx.set('data', { + return { title: $('title').text(), link, item: out, - }); -}; + }; +} diff --git a/lib/routes/csu/mail.ts b/lib/routes/csu/mail.ts index 46de38c93..f86502db1 100644 --- a/lib/routes/csu/mail.ts +++ b/lib/routes/csu/mail.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,25 @@ async function fetch(address) { return $('.tb-ct-info').html(); } -export default async (ctx) => { +export const route: Route = { + path: '/mail/:type?', + categories: ['forecast'], + example: '/csu/mail', + parameters: { type: '类型' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '校长信箱', + maintainers: ['j1g5awi'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://oa.csu.edu.cn'; const { type = '01' } = ctx.req.param(); const link = `${baseUrl}/mailbox/NoAuth/MailList_Pub?tp=${type}`; @@ -40,9 +59,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `中南大学学校信箱 - ${type === '01' ? '校长信箱' : '党委信箱'}`, link, item: out, - }); -}; + }; +} diff --git a/lib/routes/cts/news.ts b/lib/routes/cts/news.ts index 4cb9961dd..b4ff9b3bf 100644 --- a/lib/routes/cts/news.ts +++ b/lib/routes/cts/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 { parseDate } from '@/utils/parse-date'; import asyncPool from 'tiny-async-pool'; -export default async (ctx) => { +export const route: Route = { + path: '/:category', + categories: ['bbs'], + example: '/cts/real', + parameters: { category: '类别' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['news.cts.com.tw/:category/index.html'], + }, + name: '新聞', + maintainers: ['miles170'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category'); const currentUrl = `https://news.cts.com.tw/${category}/index.html`; const response = await got(currentUrl); @@ -32,10 +54,10 @@ export default async (ctx) => { items.push(data); } - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, description: $('meta[name="description"]').attr('content'), item: items, - }); -}; + }; +} diff --git a/lib/routes/cuc/yz.ts b/lib/routes/cuc/yz.ts index 7e094ab36..d44ef02d7 100644 --- a/lib/routes/cuc/yz.ts +++ b/lib/routes/cuc/yz.ts @@ -1,10 +1,32 @@ +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: '/yz', + categories: ['forecast'], + example: '/cuc/yz', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['yz.cuc.edu.cn/8549/list.htm', 'yz.cuc.edu.cn/'], + }, + name: '研究生招生网', + maintainers: ['niuyi1017'], + handler, +}; + +async function handler() { const host = 'https://yz.cuc.edu.cn'; const link = `${host}/8549/list.htm`; const response = await got({ @@ -24,10 +46,10 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('title').text(), link, description: '中国传媒大学研究生招生网 通知公告', item: items, - }); -}; + }; +} diff --git a/lib/routes/curiouscat/user.ts b/lib/routes/curiouscat/user.ts index 0814992c0..76d84eaff 100644 --- a/lib/routes/curiouscat/user.ts +++ b/lib/routes/curiouscat/user.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; const fetchAPIByUser = async (user) => { @@ -6,7 +7,17 @@ const fetchAPIByUser = async (user) => { return data; }; -export default async (ctx) => { +export const route: Route = { + path: '/user/:id', + radar: { + source: ['curiouscat.live/:id'], + }, + name: 'Unknown', + maintainers: ['lucasew'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const user = id; const data = await fetchAPIByUser(user); @@ -30,11 +41,11 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `CuriousCat - ${user}`, link: `https://curiouscat.live/${user}`, description: `Questions answered by ${user} using CuriousCat`, language: data.lang, item: items, - }); -}; + }; +} diff --git a/lib/routes/curius/links.ts b/lib/routes/curius/links.ts index 2b55bd139..a0b733753 100644 --- a/lib/routes/curius/links.ts +++ b/lib/routes/curius/links.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: '/links/:name', + categories: ['new-media'], + example: '/curius/links/yuu-yuu', + parameters: { name: 'Username, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['curius.app/:name'], + }, + name: 'User', + maintainers: ['Ovler-Young'], + handler, +}; + +async function handler(ctx) { const username = ctx.req.param('name'); const name_response = await got(`https://curius.app/api/users/${username}`, { @@ -36,11 +58,11 @@ export default async (ctx) => { guid: `curius:${username}:${item.id}`, })); - ctx.set('data', { + return { title: `${name} - Curius`, link: `https://curius.app/${username}`, description: `${name} - Curius`, allowEmpty: true, item: items, - }); -}; + }; +} diff --git a/lib/routes/cw/author.ts b/lib/routes/cw/author.ts index 0a560401a..c2f641ae1 100644 --- a/lib/routes/cw/author.ts +++ b/lib/routes/cw/author.ts @@ -1,19 +1,41 @@ +import { Route } from '@/types'; import { baseUrl, parsePage } from './utils'; import puppeteer from '@/utils/puppeteer'; -export default async (ctx) => { +export const route: Route = { + path: '/author/:channel', + categories: ['bbs'], + example: '/cw/author/57', + parameters: { channel: '作者 ID,可在 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['cw.com.tw/author/:channel'], + }, + name: '作者', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const browser = await puppeteer(); const { $, items } = await parsePage('author', browser, ctx); await browser.close(); - ctx.set('data', { + return { title: $('head title').text(), description: $('.authorTxt').text(), link: `${baseUrl}/author/${ctx.req.param('channel')}`, image: $('.authorPhoto img').attr('src') || `${baseUrl}/assets_new/img/fbshare.jpg'`, language: $('meta[property="og:locale"]').attr('content'), item: items, - }); -}; + }; +} diff --git a/lib/routes/cw/master.ts b/lib/routes/cw/master.ts index a11397b60..bb117035e 100644 --- a/lib/routes/cw/master.ts +++ b/lib/routes/cw/master.ts @@ -1,19 +1,38 @@ +import { Route } from '@/types'; import { baseUrl, parsePage } from './utils'; import puppeteer from '@/utils/puppeteer'; -export default async (ctx) => { +export const route: Route = { + path: '/master/:channel', + categories: ['bbs'], + example: '/cw/master/8', + parameters: { channel: '主頻道 ID,可在 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '主頻道', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const browser = await puppeteer(); const { $, items } = await parsePage('master', browser, ctx); await browser.close(); - ctx.set('data', { + return { title: $('head title').text(), description: $('meta[name=description]').attr('content'), link: `${baseUrl}/masterChannel.action?idMasterChannel=${ctx.req.param('channel')}`, image: `${baseUrl}/assets_new/img/fbshare.jpg`, language: $('meta[property="og:locale"]').attr('content'), item: items, - }); -}; + }; +} diff --git a/lib/routes/cw/sub.ts b/lib/routes/cw/sub.ts index 12b214fe1..222f978c4 100644 --- a/lib/routes/cw/sub.ts +++ b/lib/routes/cw/sub.ts @@ -1,19 +1,38 @@ +import { Route } from '@/types'; import { baseUrl, parsePage } from './utils'; import puppeteer from '@/utils/puppeteer'; -export default async (ctx) => { +export const route: Route = { + path: '/sub/:channel', + categories: ['bbs'], + example: '/cw/sub/615', + parameters: { channel: '子頻道 ID,可在 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '子頻道', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const browser = await puppeteer(); const { $, items } = await parsePage('sub', browser, ctx); await browser.close(); - ctx.set('data', { + return { title: $('head title').text(), description: $('meta[name=description]').attr('content'), link: `${baseUrl}/subchannel.action?idSubChannel=${ctx.req.param('channel')}`, image: `${baseUrl}/assets_new/img/fbshare.jpg`, language: $('meta[property="og:locale"]').attr('content'), item: items, - }); -}; + }; +} diff --git a/lib/routes/cw/today.ts b/lib/routes/cw/today.ts index d19f7ba9e..218ca9428 100644 --- a/lib/routes/cw/today.ts +++ b/lib/routes/cw/today.ts @@ -1,19 +1,41 @@ +import { Route } from '@/types'; import { baseUrl, parsePage } from './utils'; import puppeteer from '@/utils/puppeteer'; -export default async (ctx) => { +export const route: Route = { + path: '/today', + categories: ['bbs'], + example: '/cw/today', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['cw.com.tw/today', 'cw.com.tw/'], + }, + name: '最新上線', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const browser = await puppeteer(); const { $, items } = await parsePage('today', browser, ctx); await browser.close(); - ctx.set('data', { + return { title: $('head title').text(), description: $('meta[name=description]').attr('content'), link: `${baseUrl}/today`, image: `${baseUrl}/assets_new/img/fbshare.jpg`, language: $('meta[property="og:locale"]').attr('content'), item: items, - }); -}; + }; +} diff --git a/lib/routes/cyzone/author.ts b/lib/routes/cyzone/author.ts index 872b903b9..c2190b03f 100644 --- a/lib/routes/cyzone/author.ts +++ b/lib/routes/cyzone/author.ts @@ -1,7 +1,29 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { rootUrl, apiRootUrl, processItems, getInfo } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/author/:id', + categories: ['traditional-media'], + example: '/cyzone/author/1225562', + parameters: { id: '作者 id,可在对应作者页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['cyzone.cn/author/:id', 'cyzone.cn/'], + }, + 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) : 5; @@ -12,8 +34,8 @@ export default async (ctx) => { author_id: id, }); - ctx.set('data', { + return { item: items, ...(await getInfo(currentUrl, cache.tryGet)), - }); -}; + }; +} diff --git a/lib/routes/cyzone/index.ts b/lib/routes/cyzone/index.ts index 72d2fdad4..3f49f147e 100644 --- a/lib/routes/cyzone/index.ts +++ b/lib/routes/cyzone/index.ts @@ -1,7 +1,19 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { rootUrl, apiRootUrl, processItems, getInfo } from './util'; -export default async (ctx) => { +export const route: Route = { + path: ['/channel/:id?', '/:id?'], + radar: { + source: ['cyzone.cn/channel/:id', 'cyzone.cn/'], + target: '/:id', + }, + name: 'Unknown', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const { id = 'news' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 5; @@ -19,8 +31,8 @@ export default async (ctx) => { } ); - ctx.set('data', { + return { item: items, ...(await getInfo(currentUrl, cache.tryGet)), - }); -}; + }; +} diff --git a/lib/routes/cyzone/label.ts b/lib/routes/cyzone/label.ts index 63991e6ef..ce7405693 100644 --- a/lib/routes/cyzone/label.ts +++ b/lib/routes/cyzone/label.ts @@ -1,7 +1,29 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { rootUrl, apiRootUrl, processItems, getInfo } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/label/:name', + categories: ['traditional-media'], + example: '/cyzone/label/创业邦周报', + parameters: { name: '标签名称,可在对应标签页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['cyzone.cn/label/:name', 'cyzone.cn/'], + }, + name: '标签', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const name = ctx.req.param('name'); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 5; @@ -12,8 +34,8 @@ export default async (ctx) => { tag: name, }); - ctx.set('data', { + return { item: items, ...(await getInfo(currentUrl, cache.tryGet)), - }); -}; + }; +} diff --git a/lib/routes/cztv/daily.ts b/lib/routes/cztv/daily.ts index e334e1aec..75f143cd7 100644 --- a/lib/routes/cztv/daily.ts +++ b/lib/routes/cztv/daily.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 renderDesc = (item) => art(path.join(__dirname, 'templates/daily.art'), item); -export default async (ctx) => { +export const route: Route = { + path: '/zjxwlb/daily', + categories: ['bbs'], + example: '/cztv/zjxwlb/daily', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['cztv.com/videos/zjxwlb', 'cztv.com/'], + }, + name: '浙江新闻联播 - 每日合集', + maintainers: ['yhkang'], + handler, +}; + +async function handler() { const url = 'http://www.cztv.com/videos/zjxwlb'; const { data: res } = await got(url); @@ -36,9 +58,9 @@ export default async (ctx) => { description: renderDesc({ list: list.slice(1) }), }; - ctx.set('data', { + return { title: '浙江新闻联播-每日合集', link: url, item: [out], - }); -}; + }; +} diff --git a/lib/routes/cztv/zjxwlb.ts b/lib/routes/cztv/zjxwlb.ts index c3d03cc90..97935484c 100644 --- a/lib/routes/cztv/zjxwlb.ts +++ b/lib/routes/cztv/zjxwlb.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 renderDesc = (item) => art(path.join(__dirname, 'templates/zjxwlb.art'), item); -export default async (ctx) => { +export const route: Route = { + path: '/zjxwlb', + categories: ['bbs'], + example: '/cztv/zjxwlb', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['cztv.com/videos/zjxwlb', 'cztv.com/'], + }, + name: '浙江新闻联播', + maintainers: ['yhkang'], + handler, +}; + +async function handler() { const url = 'http://www.cztv.com/videos/zjxwlb'; const { data: res } = await got(url); @@ -33,9 +55,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: '浙江新闻联播', link: url, item: list, - }); -}; + }; +} diff --git a/lib/routes/dahecube/index.ts b/lib/routes/dahecube/index.ts index c3ba3fe83..4467b51b2 100644 --- a/lib/routes/dahecube/index.ts +++ b/lib/routes/dahecube/index.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 utils from './utils'; const { TYPE, parseUrl } = utils; -export default async (ctx) => { +export const route: Route = { + path: '/:type?', + categories: ['traditional-media'], + example: '/dahecube', + 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') ?? 'recommend'; const params = JSON.stringify({ channelid: TYPE[type].id, @@ -50,13 +69,13 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '大河财立方', link: parseUrl(type), description: `大河财立方 ${TYPE[type].name}`, language: 'zh-cn', item: items, - }); + }; ctx.set('json', { title: '大河财立方', @@ -64,4 +83,4 @@ export default async (ctx) => { description: `大河财立方 ${TYPE[type].name}`, items, }); -}; +} diff --git a/lib/routes/daily/discussed.ts b/lib/routes/daily/discussed.ts index 97c9f361e..2a2333bd3 100644 --- a/lib/routes/daily/discussed.ts +++ b/lib/routes/daily/discussed.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getData, getList, getRedirectedLink } from './utils.js'; const variables = { @@ -56,12 +57,34 @@ const graphqlQuery = { variables, }; -export default async (ctx) => { +export const route: Route = { + path: '/discussed', + categories: ['new-media'], + example: '/daily/discussed', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['daily.dev/popular'], + target: '', + }, + name: 'Most Discussed', + maintainers: ['Rjnishant530'], + handler, +}; + +async function handler() { const baseUrl = 'https://app.daily.dev/discussed'; const data = await getData(graphqlQuery); const list = getList(data); const items = await getRedirectedLink(list); - ctx.set('data', { + return { title: 'Most Discussed', link: baseUrl, item: items, @@ -69,5 +92,5 @@ export default async (ctx) => { logo: 'https://app.daily.dev/favicon-32x32.png', icon: 'https://app.daily.dev/favicon-32x32.png', language: 'en-us', - }); -}; + }; +} diff --git a/lib/routes/daily/index.ts b/lib/routes/daily/index.ts index ed7f851ba..4b9b40758 100644 --- a/lib/routes/daily/index.ts +++ b/lib/routes/daily/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getData, getList, getRedirectedLink } from './utils.js'; const variables = { @@ -66,12 +67,23 @@ const graphqlQuery = { variables, }; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['daily.dev/popular'], + target: '', + }, + name: 'Unknown', + maintainers: ['Rjnishant530'], + handler, +}; + +async function handler() { const baseUrl = 'https://app.daily.dev/popular'; const data = await getData(graphqlQuery); const list = getList(data); const items = await getRedirectedLink(list); - ctx.set('data', { + return { title: 'Popular', link: baseUrl, item: items, @@ -79,5 +91,5 @@ export default async (ctx) => { logo: 'https://app.daily.dev/favicon-32x32.png', icon: 'https://app.daily.dev/favicon-32x32.png', language: 'en-us', - }); -}; + }; +} diff --git a/lib/routes/daily/upvoted.ts b/lib/routes/daily/upvoted.ts index 9d605df3b..10f74a846 100644 --- a/lib/routes/daily/upvoted.ts +++ b/lib/routes/daily/upvoted.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getData, getList, getRedirectedLink } from './utils.js'; const variables = { @@ -60,12 +61,34 @@ const graphqlQuery = { variables, }; -export default async (ctx) => { +export const route: Route = { + path: '/upvoted', + categories: ['new-media'], + example: '/daily/upvoted', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['daily.dev/popular'], + target: '', + }, + name: 'Most upvoted', + maintainers: ['Rjnishant530'], + handler, +}; + +async function handler() { const baseUrl = 'https://app.daily.dev/upvoted'; const data = await getData(graphqlQuery); const list = getList(data); const items = await getRedirectedLink(list); - ctx.set('data', { + return { title: 'Most Upvoted', link: baseUrl, item: items, @@ -73,5 +96,5 @@ export default async (ctx) => { logo: 'https://app.daily.dev/favicon-32x32.png', icon: 'https://app.daily.dev/favicon-32x32.png', language: 'en-us', - }); -}; + }; +} diff --git a/lib/routes/damai/activity.ts b/lib/routes/damai/activity.ts index 7e59bcedf..ad197205c 100644 --- a/lib/routes/damai/activity.ts +++ b/lib/routes/damai/activity.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: '/activity/:city/:category/:subcategory/:keyword?', + categories: ['game'], + example: '/damai/activity/上海/音乐会/全部/柴可夫斯基', + parameters: { city: '城市,如果不需要限制,请填入`全部`', category: '分类,如果不需要限制,请填入`全部`', subcategory: '子分类,如果不需要限制,请填入`全部`', keyword: '搜索关键字,置空为不限制' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '票务更新', + maintainers: ['hoilc'], + handler, +}; + +async function handler(ctx) { const city = ctx.req.param('city') === '全部' ? '' : ctx.req.param('city'); const category = ctx.req.param('category') === '全部' ? '' : ctx.req.param('category'); const subcategory = ctx.req.param('subcategory') === '全部' ? '' : ctx.req.param('subcategory'); @@ -32,7 +51,7 @@ export default async (ctx) => { const data = response.data; const list = data.pageData.resultData || []; - ctx.set('data', { + return { title: `大麦网票务 - ${city || '全国'} - ${category || '全部分类'}${subcategory ? ' - ' + subcategory : ''}${keyword ? ' - ' + keyword : ''}`, link: 'https://search.damai.cn/search.htm', item: list.map((item) => ({ @@ -43,5 +62,5 @@ export default async (ctx) => { }), link: `https://detail.damai.cn/item.htm?id=${item.projectid}`, })), - }); -}; + }; +} diff --git a/lib/routes/dapenti/subject.ts b/lib/routes/dapenti/subject.ts index 8dec686f9..aa509ee5a 100644 --- a/lib/routes/dapenti/subject.ts +++ b/lib/routes/dapenti/subject.ts @@ -1,5 +1,24 @@ +import { Route } from '@/types'; import utils from './utils'; -export default async (ctx) => { - ctx.set('data', await utils.parseFeed({ subjectid: ctx.req.param('id') })); +export const route: Route = { + path: '/subject/:id', + categories: ['anime'], + example: '/dapenti/subject/184', + parameters: { id: '主题 id' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '主题', + maintainers: ['xyqfer'], + handler, }; + +async function handler(ctx) { + ctx.set('data', await utils.parseFeed({ subjectid: ctx.req.param('id') })); +} diff --git a/lib/routes/dapenti/tugua.ts b/lib/routes/dapenti/tugua.ts index 16a76541f..25839bc7f 100644 --- a/lib/routes/dapenti/tugua.ts +++ b/lib/routes/dapenti/tugua.ts @@ -1,5 +1,24 @@ +import { Route } from '@/types'; import utils from './utils'; -export default async (ctx) => { - ctx.set('data', await utils.parseFeed({ subjectid: 70 })); +export const route: Route = { + path: '/tugua', + categories: ['anime'], + example: '/dapenti/tugua', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '图卦', + maintainers: ['tgly307'], + handler, }; + +async function handler(ctx) { + ctx.set('data', await utils.parseFeed({ subjectid: 70 })); +} diff --git a/lib/routes/darwinawards/index.ts b/lib/routes/darwinawards/index.ts index 6d8ad29b5..c454729af 100644 --- a/lib/routes/darwinawards/index.ts +++ b/lib/routes/darwinawards/index.ts @@ -1,8 +1,20 @@ +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: ['/all', '/'], + radar: { + source: ['darwinawards.com/darwin', 'darwinawards.com/'], + target: '', + }, + name: 'Unknown', + maintainers: ['zoenglinghou', 'nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://darwinawards.com'; const currentUrl = `${rootUrl}/darwin/`; @@ -49,9 +61,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/dayanzai/index.ts b/lib/routes/dayanzai/index.ts index ef9b4e2f9..82d7d2788 100644 --- a/lib/routes/dayanzai/index.ts +++ b/lib/routes/dayanzai/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,29 @@ import timezone from '@/utils/timezone'; const rootUrl = 'http://www.dayanzai.me/'; -export default async (ctx) => { +export const route: Route = { + path: '/:category/:fulltext?', + categories: ['programming'], + example: '/dayanzai/windows', + parameters: { category: '分类', fulltext: '是否获取全文,需要获取则传入参数`y`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['dayanzai.me/:category', 'dayanzai.me/:category/*'], + target: '/:category', + }, + name: '分类', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { category, fulltext } = ctx.req.param(); const currentUrl = rootUrl + category; const response = await got.get(currentUrl); @@ -48,10 +71,10 @@ export default async (ctx) => { ) : list; - ctx.set('data', { + return { title: `大眼仔旭 ${category}`, link: currentUrl, description: `大眼仔旭 ${category} RSS`, item: items, - }); -}; + }; +} diff --git a/lib/routes/dblp/publication.ts b/lib/routes/dblp/publication.ts index 9ea1ef15a..18e07ec8c 100644 --- a/lib/routes/dblp/publication.ts +++ b/lib/routes/dblp/publication.ts @@ -1,8 +1,30 @@ +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: '/:field', + categories: ['journal'], + example: '/dblp/knowledge%20tracing', + parameters: { field: 'Research field' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['dblp.org/:field'], + }, + name: 'Keyword Search', + maintainers: ['ytno1'], + handler, +}; + +async function handler(ctx) { // 在此处编写您的逻辑 const field = ctx.req.param('field'); @@ -51,7 +73,7 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { // 在此处输出您的 RSS // 源标题 title: `【dblp】${field}`, @@ -61,5 +83,5 @@ export default async (ctx) => { description: `DBLP ${field} RSS`, // 处理后的数据,即文章列表 item: list, - }); -}; + }; +} diff --git a/lib/routes/dcard/section.ts b/lib/routes/dcard/section.ts index 8fa9b7923..1d376800a 100644 --- a/lib/routes/dcard/section.ts +++ b/lib/routes/dcard/section.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { parseDate } from '@/utils/parse-date'; import utils from './utils'; import puppeteer from '@/utils/puppeteer'; -export default async (ctx) => { +export const route: Route = { + path: '/:section/:type?', + categories: ['blog'], + example: '/dcard/funny/popular', + parameters: { section: '板塊名稱,URL 中獲得', type: '排序,popular 熱門;latest 最新,默認為 latest' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '板塊帖子', + maintainers: ['HenryQW'], + handler, +}; + +async function handler(ctx) { const { type = 'latest', section = 'posts' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30; const browser = await puppeteer(); @@ -59,10 +78,10 @@ export default async (ctx) => { const result = await utils.ProcessFeed(items, cookies, browser, limit, cache); await browser.close(); - ctx.set('data', { + return { title, link, description: '不想錯過任何有趣的話題嗎?趕快加入我們吧!', item: result, - }); -}; + }; +} diff --git a/lib/routes/dcfever/news.ts b/lib/routes/dcfever/news.ts index cc6ce39d5..da3b51271 100644 --- a/lib/routes/dcfever/news.ts +++ b/lib/routes/dcfever/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 { baseUrl, parseItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/news/:type?', + categories: ['traditional-media'], + example: '/dcfever/news', + parameters: { type: '分類,預設為所有新聞' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '新聞中心', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); const response = await got(`${baseUrl}/news/index.php`, { @@ -25,7 +44,7 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => parseItem(item, cache.tryGet))); - ctx.set('data', { + return { title: `${$('.channel_nav') .contents() .filter((_, e) => e.nodeType === 3) @@ -33,5 +52,5 @@ export default async (ctx) => { link: response.url, image: 'https://cdn10.dcfever.com/images/android_192.png', item: items, - }); -}; + }; +} diff --git a/lib/routes/dcfever/reviews.ts b/lib/routes/dcfever/reviews.ts index afd6f8cd3..022fb0366 100644 --- a/lib/routes/dcfever/reviews.ts +++ b/lib/routes/dcfever/reviews.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, parseItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/reviews/:type?', + categories: ['traditional-media'], + example: '/dcfever/reviews/cameras', + parameters: { type: '分類,預設為 `cameras`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['dcfever.com/:type/reviews.php'], + target: '/reviews/:type', + }, + name: '測試報告', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { type = 'cameras' } = ctx.req.param(); const response = await got(`${baseUrl}/${type}/reviews.php`); @@ -21,10 +44,10 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => parseItem(item, cache.tryGet))); - ctx.set('data', { + return { title: $('head title').text(), link: response.url, image: 'https://cdn10.dcfever.com/images/android_192.png', item: items, - }); -}; + }; +} diff --git a/lib/routes/dcfever/trading-search.ts b/lib/routes/dcfever/trading-search.ts index 131dfb500..093580985 100644 --- a/lib/routes/dcfever/trading-search.ts +++ b/lib/routes/dcfever/trading-search.ts @@ -1,10 +1,29 @@ +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 { baseUrl, parseTradeItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/trading/search/:keyword/:mainCat?', + categories: ['traditional-media'], + example: '/dcfever/trading/search/Sony', + parameters: { keyword: '關鍵字', mainCat: '主要分類 ID,見上表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '二手市集 - 物品搜尋', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { keyword, mainCat } = ctx.req.param(); const response = await got(`${baseUrl}/trading/search.php`, { @@ -31,10 +50,10 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => parseTradeItem(item, cache.tryGet))); - ctx.set('data', { + return { title: $('head title').text(), link: response.url, image: 'https://cdn10.dcfever.com/images/android_192.png', item: items, - }); -}; + }; +} diff --git a/lib/routes/dcfever/trading.ts b/lib/routes/dcfever/trading.ts index 5babbea3b..b4487ce95 100644 --- a/lib/routes/dcfever/trading.ts +++ b/lib/routes/dcfever/trading.ts @@ -1,10 +1,29 @@ +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 { baseUrl, parseTradeItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/trading/:id', + categories: ['traditional-media'], + example: '/dcfever/trading/1', + 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, order = 'new' } = ctx.req.param(); const response = await got(`${baseUrl}/trading/listing.php`, { @@ -31,10 +50,10 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => parseTradeItem(item, cache.tryGet))); - ctx.set('data', { + return { title: $('head title').text(), link: response.url, image: 'https://cdn10.dcfever.com/images/android_192.png', item: items, - }); -}; + }; +} diff --git a/lib/routes/ddosi/category.ts b/lib/routes/ddosi/category.ts index b30ea1293..dfeec2800 100644 --- a/lib/routes/ddosi/category.ts +++ b/lib/routes/ddosi/category.ts @@ -1,9 +1,32 @@ +import { Route } from '@/types'; const envs = process.env; 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/:category?', + categories: ['programming'], + example: '/ddosi/category/黑客工具', + parameters: { category: 'N' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ddosi.org/category/:category/'], + target: '/category/:category', + }, + name: '分类', + maintainers: [], + handler, +}; + +async function handler(ctx) { const url = 'https://www.ddosi.org/category'; const userAgent = envs.UA || 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1'; const category = ctx.req.param('category'); @@ -34,9 +57,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `雨苁-${category}`, link: `${url}/${category}/`, item: items, - }); -}; + }; +} diff --git a/lib/routes/ddosi/index.ts b/lib/routes/ddosi/index.ts index 68bfd1fd2..96f765198 100644 --- a/lib/routes/ddosi/index.ts +++ b/lib/routes/ddosi/index.ts @@ -1,9 +1,21 @@ +import { Route } from '@/types'; const envs = process.env; 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: ['ddosi.org/'], + target: '', + }, + name: 'Unknown', + maintainers: ['XinRoom'], + handler, +}; + +async function handler() { const url = 'https://www.ddosi.org/'; const userAgent = envs.UA || 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1'; const response = await got({ @@ -33,9 +45,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `雨苁`, link: String(url), item: items, - }); -}; + }; +} diff --git a/lib/routes/deadline/posts.ts b/lib/routes/deadline/posts.ts index 6fb0d71e8..096627f1b 100644 --- a/lib/routes/deadline/posts.ts +++ b/lib/routes/deadline/posts.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: '/', + radar: { + source: ['deadline.com/'], + target: '', + }, + name: 'Unknown', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://deadline.com'; const response = await got(`${baseUrl}/wp-json/wp/v2/posts`, { searchParams: { @@ -45,12 +57,12 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: 'Deadline – Hollywood Entertainment Breaking News', description: 'Deadline.com is always the first to break up-to-the-minute entertainment, Hollywood and media news, with an unfiltered, no-holds-barred analysis of events.', link: baseUrl, language: 'en-US', image: `${baseUrl}/wp-content/themes/pmc-deadline-2019/assets/app/icons/apple-touch-icon.png`, item: items, - }); -}; + }; +} diff --git a/lib/routes/dedao/index.ts b/lib/routes/dedao/index.ts index 10b2cba66..d2b6150f6 100644 --- a/lib/routes/dedao/index.ts +++ b/lib/routes/dedao/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 { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'news'; const rootUrl = `https://www.igetget.com/${category === 'video' ? 'video' : 'news'}`; @@ -38,10 +46,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `得到${category === 'video' ? '' : '大事件'} - ${category === 'news' ? '新闻' : category === 'figure' ? '人物故事' : '视频'}`, link: rootUrl, item: items, description: data.description, - }); -}; + }; +} diff --git a/lib/routes/dedao/knowledge.ts b/lib/routes/dedao/knowledge.ts index 4bdf68665..e66d2776a 100644 --- a/lib/routes/dedao/knowledge.ts +++ b/lib/routes/dedao/knowledge.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: '/knowledge/:topic?/:type?', + categories: ['traditional-media'], + example: '/dedao/knowledge', + parameters: { topic: '话题 id,可在对应话题页 URL 中找到', type: '分享类型,`true` 指精选,`false` 指最新,默认为精选' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['dedao.cn/knowledge/topic/:topic', 'dedao.cn/knowledge', 'dedao.cn/'], + }, + name: '知识城邦', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const topic = ctx.req.param('topic') ?? ''; const type = /t|y/i.test(ctx.req.param('type') ?? 'true'); const count = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 100; @@ -58,10 +80,10 @@ export default async (ctx) => { }), })); - ctx.set('data', { + return { title: `得到 - 知识城邦${title === '' ? '' : ' - ' + title}`, link: currentUrl, item: items, description, - }); -}; + }; +} diff --git a/lib/routes/dedao/list.ts b/lib/routes/dedao/list.ts index 438fcf326..2fbfc1fee 100644 --- a/lib/routes/dedao/list.ts +++ b/lib/routes/dedao/list.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: '/list/:category?', + categories: ['traditional-media'], + example: '/dedao/list/年度日更', + parameters: { category: '分类名,默认为年度日更' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['igetget.com/'], + }, + name: '首页', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? '年度日更'; const rootUrl = 'https://www.igetget.com'; @@ -59,9 +81,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `得到 - ${category}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/dedao/user.ts b/lib/routes/dedao/user.ts index 3abe916c3..baebefe29 100644 --- a/lib/routes/dedao/user.ts +++ b/lib/routes/dedao/user.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 types = { 12: '视频', }; -export default async (ctx) => { +export const route: Route = { + path: '/user/:id/:type?', + categories: ['traditional-media'], + example: '/dedao/user/VkA5OqLX4RyGxmZRNBMlwBrDaJQ9og', + parameters: { id: '用户 id,可在对应用户主页 URL 中找到', type: '类型,见下表,默认为`0`,即动态' }, + 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 type = ctx.req.param('type') ? Number.parseInt(ctx.req.param('type')) : 0; const count = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 100; @@ -71,12 +90,12 @@ export default async (ctx) => { }), })); - ctx.set('data', { + return { title: `${author}的得到主页 - ${types[type]}`, link: currentUrl, item: items, image, description, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/deeplearning/thebatch.ts b/lib/routes/deeplearning/thebatch.ts index 21d4a467d..f3973c26d 100644 --- a/lib/routes/deeplearning/thebatch.ts +++ b/lib/routes/deeplearning/thebatch.ts @@ -1,7 +1,29 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/thebatch', + categories: ['design'], + example: '/deeplearning/thebatch', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.deeplearning.ai/thebatch', 'www.deeplearning.ai/'], + }, + name: 'TheBatch 周报', + maintainers: ['nczitzk', 'juvenn'], + handler, +}; + +async function handler() { const page = await got({ method: 'get', url: `https://www.deeplearning.ai/the-batch/`, @@ -21,7 +43,7 @@ export default async (ctx) => { pubDate: new Date(item.published_at).toUTCString(), })); - ctx.set('data', { + return { title: `The Batch - a new weekly newsletter from deeplearning.ai`, link: `https://www.deeplearning.ai/the-batch/`, item: await Promise.all( @@ -33,5 +55,5 @@ export default async (ctx) => { }) ) ), - }); -}; + }; +} diff --git a/lib/routes/deepmind/blog.ts b/lib/routes/deepmind/blog.ts index e2b85fac1..cd8005abd 100644 --- a/lib/routes/deepmind/blog.ts +++ b/lib/routes/deepmind/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 parser from '@/utils/rss-parser'; -export default async (ctx) => { +export const route: Route = { + path: '/blog', + categories: ['traditional-media'], + example: '/deepmind/blog', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['deepmind.com/blog', 'deepmind.com/'], + }, + name: 'Blog', + maintainers: ['nczitzk', 'TonyRL'], + handler, +}; + +async function handler() { const feed = await parser.parseURL('https://www.deepmind.com/blog/rss.xml'); const items = await Promise.all( @@ -22,12 +44,12 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: feed.title, description: feed.description, image: 'https://assets-global.website-files.com/621d30e84caf0be3291dbf1c/621d336835a91420c6a8dcf2_webclip.png', link: `${feed.link}/blog`, item: items, language: 'en', - }); -}; + }; +} diff --git a/lib/routes/deltaio/blog.ts b/lib/routes/deltaio/blog.ts index 3a2bcac48..80ced6320 100644 --- a/lib/routes/deltaio/blog.ts +++ b/lib/routes/deltaio/blog.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 { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/blog', + categories: ['programming'], + example: '/deltaio/blog', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['delta.io/blog'], + }, + name: 'Blogs', + maintainers: ['RengarLee'], + handler, +}; + +async function handler() { const baseUrl = 'https://delta.io'; const dataUrl = `${baseUrl}/page-data/blog/page-data.json`; @@ -26,9 +48,9 @@ export default async (ctx) => { itunes_item_image: `${baseUrl}${node.frontmatter.thumbnail.childImageSharp.gatsbyImageData.images.fallback.src}`, })); - ctx.set('data', { + return { title: 'delta.io blog', link: `${baseUrl}/blog`, item: items, - }); -}; + }; +} diff --git a/lib/routes/devolverdigital/blog.ts b/lib/routes/devolverdigital/blog.ts index 18f8687fd..0cca3455e 100644 --- a/lib/routes/devolverdigital/blog.ts +++ b/lib/routes/devolverdigital/blog.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: '/blog', + categories: ['programming'], + example: '/devolverdigital/blog', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['devolverdigital.com/blog'], + }, + name: 'Official Blogs', + maintainers: ['XXY233'], + handler, +}; + +async function handler() { const baseUrl = 'https://www.devolverdigital.com/blog'; const { data: response } = await got(baseUrl); @@ -42,10 +64,10 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: 'DevolverDigital Blog', language: 'en-us', link: 'https://www.devolverdigital.com/blog', item: items, - }); -}; + }; +} diff --git a/lib/routes/dgjyw/index.ts b/lib/routes/dgjyw/index.ts index f174804f7..757267147 100644 --- a/lib/routes/dgjyw/index.ts +++ b/lib/routes/dgjyw/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 params = getSubPath(ctx); const rootUrl = 'https://www.dgjyw.com'; @@ -61,9 +69,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/dhu/jiaowu/news.ts b/lib/routes/dhu/jiaowu/news.ts index 5793162a2..c46682ad1 100644 --- a/lib/routes/dhu/jiaowu/news.ts +++ b/lib/routes/dhu/jiaowu/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'; @@ -9,7 +10,25 @@ const map = { student: '/tzggwxszl/list.htm', teacher: '/tzggwjszl/list.htm', }; -export default async (ctx) => { +export const route: Route = { + path: '/jiaowu/news/:type?', + categories: ['forecast'], + example: '/dhu/jiaowu/news/student', + parameters: { type: '默认为 `student`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '教务处通知', + maintainers: ['KiraKiseki'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); const link = Object.hasOwn(map, type) ? `${base_url}${map[type]}` : `${base_url}/tzggwxszl/list.htm`; const response = await got({ @@ -21,7 +40,7 @@ export default async (ctx) => { }); const $ = load(response.data); - ctx.set('data', { + return { link: base_url, title: '东华大学教务处-' + $('.col_title').text(), item: $('.list_item') @@ -31,5 +50,5 @@ export default async (ctx) => { pubDate: timezone(parseDate($('.Article_PublishDate', elem).text()), +8), })) .get(), - }); -}; + }; +} diff --git a/lib/routes/dhu/news/xsxx.ts b/lib/routes/dhu/news/xsxx.ts index 28215a60c..9dfd44925 100644 --- a/lib/routes/dhu/news/xsxx.ts +++ b/lib/routes/dhu/news/xsxx.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 baseUrl = 'https://news.dhu.edu.cn/_wp3services/generalQuery?queryObj=articles&siteId=14&columnId=6410&pageIndex=1&rows=20'; -export default async (ctx) => { +export const route: Route = { + path: '/news/xsxx', + categories: ['forecast'], + example: '/dhu/news/xsxx', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['news.dhu.edu.cn/6410'], + }, + name: '学术信息', + maintainers: ['fox2049'], + handler, +}; + +async function handler() { const { data } = await got(baseUrl, { headers: { Referer: 'https://news.dhu.edu.cn/6410/', @@ -35,9 +57,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '学术信息', link: 'https://news.dhu.edu.cn/6410', item: items, - }); -}; + }; +} diff --git a/lib/routes/dhu/xxgk/news.ts b/lib/routes/dhu/xxgk/news.ts index 683a49c0c..48d59fa2f 100644 --- a/lib/routes/dhu/xxgk/news.ts +++ b/lib/routes/dhu/xxgk/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'; @@ -5,7 +6,25 @@ import timezone from '@/utils/timezone'; const base_url = 'https://xxgk.dhu.edu.cn/1737/list.htm'; -export default async (ctx) => { +export const route: Route = { + path: '/xxgk/news', + categories: ['forecast'], + example: '/dhu/xxgk/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '最新信息公开', + maintainers: ['KiraKiseki'], + handler, +}; + +async function handler() { const link = base_url; const response = await got({ method: 'get', @@ -16,7 +35,7 @@ export default async (ctx) => { }); const $ = load(response.data); - ctx.set('data', { + return { link: base_url, title: '东华大学信息公开网-最新公开信息', item: $('.cols') @@ -26,5 +45,5 @@ export default async (ctx) => { pubDate: timezone(parseDate($('.cols_meta', elem).text()), +8), })) .get(), - }); -}; + }; +} diff --git a/lib/routes/dhu/yjs/news.ts b/lib/routes/dhu/yjs/news.ts index 8d91d91b0..83595e1e8 100644 --- a/lib/routes/dhu/yjs/news.ts +++ b/lib/routes/dhu/yjs/news.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,25 @@ const map = { notice: '/7206/list.htm', class: '/xkks/list.htm', }; -export default async (ctx) => { +export const route: Route = { + path: '/yjs/news/:type?', + categories: ['forecast'], + example: '/dhu/yjs/news/class', + parameters: { type: '默认为 `class`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '研究生院通知', + maintainers: ['fox2049'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') || 'class'; const link = `${baseUrl}${map[type]}`; const { data: response } = await got(link); @@ -40,9 +59,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '东华大学研究生-' + $('.Column_Name').text(), link, item: items, - }); -}; + }; +} diff --git a/lib/routes/dhu/yjs/zs.ts b/lib/routes/dhu/yjs/zs.ts index 08d8ad8e8..ccb4d5f91 100644 --- a/lib/routes/dhu/yjs/zs.ts +++ b/lib/routes/dhu/yjs/zs.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -9,7 +10,25 @@ const map = { doctor: '/7126/list.htm', master: '/7128/list.htm', }; -export default async (ctx) => { +export const route: Route = { + path: '/yjs/zs/:type?', + categories: ['forecast'], + example: '/dhu/yjs/zs/master', + parameters: { type: '默认为 `master`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '研究生招生信息', + maintainers: ['fox2049'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') || 'master'; const link = `${baseUrl}${map[type]}`; const { data: response } = await got(link); @@ -39,9 +58,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '东华大学研究生-' + $('.col_title').text(), link, item: items, - }); -}; + }; +} diff --git a/lib/routes/diandong/news.ts b/lib/routes/diandong/news.ts index 87ef0cb6a..87a819c73 100644 --- a/lib/routes/diandong/news.ts +++ b/lib/routes/diandong/news.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,29 @@ const titleMap = { 23: '行业', }; -export default async (ctx) => { +export const route: Route = { + path: '/news/:cate?', + categories: ['traditional-media'], + example: '/diandong/news', + parameters: { cate: '分类,见下表,默认为推荐' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['diandong.com/news'], + target: '/news/:cate', + }, + name: '资讯', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const cate = ctx.req.param('cate') ?? 0; const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 25; const url = `${rootUrl}/content/list?page=1&size=${limit}&source_id=12&content_type=news&content_ids=&category_id=${cate}`; @@ -43,9 +66,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `电动邦 - ${titleMap[cate]}`, link: 'https://www.diandong.com/news', item: items, - }); -}; + }; +} diff --git a/lib/routes/diershoubing/news.ts b/lib/routes/diershoubing/news.ts index 2637b15c7..b82b9224a 100644 --- a/lib/routes/diershoubing/news.ts +++ b/lib/routes/diershoubing/news.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 { art } from '@/utils/render'; import * as path from 'node:path'; const renderDesc = (data) => art(path.join(__dirname, 'templates/news.art'), data); -export default async (ctx) => { +export const route: Route = { + path: '/news', + categories: ['reading'], + example: '/diershoubing/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['diershoubing.com/'], + }, + name: '新闻', + maintainers: ['wushijishan'], + handler, +}; + +async function handler(ctx) { const { data } = await got(`https://api.diershoubing.com:5001/feed/tag/?pn=0&rn=${ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 20}&tag_type=0&src=ios`); const items = data.feeds.map((item) => { @@ -44,10 +66,10 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `二柄APP`, link: `https://www.diershoubing.com`, description: `二柄APP新闻`, item: items, - }); -}; + }; +} diff --git a/lib/routes/discord/channel.ts b/lib/routes/discord/channel.ts index 5a0720f51..6bc845e7e 100644 --- a/lib/routes/discord/channel.ts +++ b/lib/routes/discord/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 { art } from '@/utils/render'; import * as path from 'node:path'; import { baseUrl, getChannel, getChannelMessages, getGuild } from './discord-api'; -export default async (ctx) => { +export const route: Route = { + path: '/channel/:channelId', + categories: ['new-media'], + example: '/discord/channel/950465850056536084', + parameters: { channelId: 'Channel ID' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['discord.com/channels/:guildId/:channelId/:messageID', 'discord.com/channels/:guildId/:channelId'], + }, + name: 'Channel Messages', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { if (!config.discord || !config.discord.authorization) { throw new Error('Discord RSS is disabled due to the lack of relevant config'); } @@ -32,11 +54,11 @@ export default async (ctx) => { link: `${baseUrl}/channels/${guildId}/${channelId}/${message.id}`, })); - ctx.set('data', { + return { title: `#${channelName} - ${guildName} - Discord`, description: channelTopic, link: `${baseUrl}/channels/${guildId}/${channelId}`, image: `https://cdn.discordapp.com/icons/${guildId}/${guidIcon}.webp`, item: messages, - }); -}; + }; +} diff --git a/lib/routes/discourse/notifications.ts b/lib/routes/discourse/notifications.ts index bcd985423..16f34efcb 100644 --- a/lib/routes/discourse/notifications.ts +++ b/lib/routes/discourse/notifications.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { getConfig } from './utils'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/:configId/notifications/:fulltext?', + categories: ['blog'], + example: '/discourse/0/notifications', + parameters: { configId: 'Environment variable configuration id, see above', fulltext: 'Fetch the content if the notification points to a post. This is disabled by default, set it to `1` to enable it.' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Notifications', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { link, key } = getConfig(ctx); const response = await got(`${link}/notifications.json`, { headers: { 'User-Api-Key': key } }).json(); @@ -32,9 +51,9 @@ export default async (ctx) => { } const { about } = await got(`${link}/about.json`, { headers: { 'User-Api-Key': key } }).json(); - ctx.set('data', { + return { title: `${about.title} - Notifications`, description: about.description, item: items, - }); -}; + }; +} diff --git a/lib/routes/discourse/posts.ts b/lib/routes/discourse/posts.ts index 652c67327..3d8fb9f80 100644 --- a/lib/routes/discourse/posts.ts +++ b/lib/routes/discourse/posts.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import { getConfig } from './utils'; import got from '@/utils/got'; import RSSParser from '@/utils/rss-parser'; -export default async (ctx) => { +export const route: Route = { + path: '/:configId/posts', + categories: ['blog'], + example: '/discourse/0/posts', + parameters: { configId: 'Environment variable configuration id, see above' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Latest posts', + maintainers: ['dzx-dzx'], + handler, +}; + +async function handler(ctx) { const { link, key } = getConfig(ctx); const feed = await RSSParser.parseString( @@ -21,5 +40,5 @@ export default async (ctx) => { ...e, })); - ctx.set('data', { item: feed.items, ...feed }); -}; + return { item: feed.items, ...feed }; +} diff --git a/lib/routes/discuz/discuz.ts b/lib/routes/discuz/discuz.ts index fa3f86169..864f09a72 100644 --- a/lib/routes/discuz/discuz.ts +++ b/lib/routes/discuz/discuz.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,14 @@ async function loadContent(itemLink, charset, header) { return { description }; } -export default async (ctx) => { +export const route: Route = { + path: ['/:ver{[7x]}/:cid{[0-9]{2}}/:link{.+}', '/:ver{[7x]}/:link{.+}', '/:link{.+}'], + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { let link = ctx.req.param('link'); const ver = ctx.req.param('ver') ? ctx.req.param('ver').toUpperCase() : undefined; const cid = ctx.req.param('cid'); @@ -144,10 +152,10 @@ export default async (ctx) => { throw new Error('不支持当前Discuz版本.'); } - ctx.set('data', { + return { title: $('head > title').text(), description: $('head > meta[name=description]').attr('content'), link, item: items, - }); -}; + }; +} diff --git a/lib/routes/disinfo/publications.ts b/lib/routes/disinfo/publications.ts index f284234c9..cc2692ef9 100644 --- a/lib/routes/disinfo/publications.ts +++ b/lib/routes/disinfo/publications.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: '/publications', + categories: ['traditional-media'], + example: '/disinfo/publications', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['disinfo.eu/'], + }, + name: 'Publications', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://www.disinfo.eu'; const currentUrl = `${rootUrl}/publications`; const response = await got({ @@ -49,9 +71,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/diskanalyzer/whats-new.ts b/lib/routes/diskanalyzer/whats-new.ts index aa19e4237..4368d29b1 100644 --- a/lib/routes/diskanalyzer/whats-new.ts +++ b/lib/routes/diskanalyzer/whats-new.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: '/whats-new', + categories: ['university'], + example: '/diskanalyzer/whats-new', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['diskanalyzer.com/whats-new', 'diskanalyzer.com/'], + }, + name: "What's New", + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://diskanalyzer.com'; const currentUrl = `${rootUrl}/whats-new`; @@ -41,9 +63,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/distill/index.ts b/lib/routes/distill/index.ts index f7f4ad8a4..cdcc0bc53 100644 --- a/lib/routes/distill/index.ts +++ b/lib/routes/distill/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: ['distill.pub/'], + target: '', + }, + name: 'Unknown', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://distill.pub'; const response = await got({ @@ -57,9 +69,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/dlnews/category.ts b/lib/routes/dlnews/category.ts index 4928bfc47..188c554ec 100644 --- a/lib/routes/dlnews/category.ts +++ b/lib/routes/dlnews/category.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -60,7 +61,18 @@ const extractArticle = (item) => return item; }); -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + radar: { + source: ['dlnews.com/articles/:category'], + target: '/:category', + }, + name: 'Unknown', + maintainers: ['Rjnishant530'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category'); const baseUrl = 'https://www.dlnews.com'; const apiPath = '/pf/api/v3/content/fetch/articles-api'; @@ -82,7 +94,7 @@ export default async (ctx) => { items.push(data); } - ctx.set('data', { + return { title: Object.hasOwn(topics, category) ? `${topics[category]} : DL News` : 'DL News', link: baseUrl, item: items, @@ -90,5 +102,5 @@ export default async (ctx) => { logo: 'https://www.dlnews.com/pf/resources/favicon.ico?d=284', icon: 'https://www.dlnews.com/pf/resources/favicon.ico?d=284', language: 'en-us', - }); -}; + }; +} diff --git a/lib/routes/dlsite/campaign.ts b/lib/routes/dlsite/campaign.ts index cb2aad17e..b6c6d43d9 100644 --- a/lib/routes/dlsite/campaign.ts +++ b/lib/routes/dlsite/campaign.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -118,7 +119,25 @@ const setUrl = (info) => { return paramsPath.slice(1); }; -export default async (ctx) => { +export const route: Route = { + path: '/campaign/:type/:free?', + categories: ['program-update'], + example: '/dlsite/campaign/home', + parameters: { type: 'Type, see table above', free: 'Free only, empty means false, other value means true' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Discounted Works', + maintainers: ['cssxsh'], + handler, +}; + +async function handler(ctx) { const info = infos[ctx.req.param('type')]; // 判断参数是否合理 if (info === undefined) { @@ -163,12 +182,12 @@ export default async (ctx) => { return signle; }); - ctx.set('data', { + return { title, link: `${host}/${link}`, description, language: 'ja-jp', allowEmpty: true, item, - }); -}; + }; +} diff --git a/lib/routes/dlsite/ci-en/article.ts b/lib/routes/dlsite/ci-en/article.ts index 4535da2c9..80a7031c0 100644 --- a/lib/routes/dlsite/ci-en/article.ts +++ b/lib/routes/dlsite/ci-en/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 timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/ci-en/:id/article', + categories: ['program-update'], + example: '/dlsite/ci-en/7400/article', + parameters: { id: 'Creator id, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ci-en.dlsite.com/creator/:id/article/843558', 'ci-en.dlsite.com/'], + }, + name: "Ci-en Creators' Article", + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? '7400'; const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 10; @@ -57,9 +79,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/dlsite/index.ts b/lib/routes/dlsite/index.ts index 74921189c..36584187b 100644 --- a/lib/routes/dlsite/index.ts +++ b/lib/routes/dlsite/index.ts @@ -1,5 +1,13 @@ +import { Route } from '@/types'; import { ProcessItems } from './utils'; -export default async (ctx) => { - ctx.set('data', await ProcessItems(ctx)); +export const route: Route = { + path: '*', + name: 'Unknown', + maintainers: [], + handler, }; + +async function handler(ctx) { + ctx.set('data', await ProcessItems(ctx)); +} diff --git a/lib/routes/dlsite/new.ts b/lib/routes/dlsite/new.ts index 479c08295..36a49fadd 100644 --- a/lib/routes/dlsite/new.ts +++ b/lib/routes/dlsite/new.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; @@ -49,7 +50,25 @@ const infos = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/new/:type', + categories: ['program-update'], + example: '/dlsite/new/home', + parameters: { type: 'Type, see table below' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Current Release', + maintainers: ['cssxsh'], + handler, +}; + +async function handler(ctx) { const info = infos[ctx.req.param('type')]; // 判断参数是否合理 if (info === undefined) { @@ -98,11 +117,11 @@ export default async (ctx) => { return signle; }); - ctx.set('data', { + return { title, link, description, language: 'ja-jp', item, - }); -}; + }; +} diff --git a/lib/routes/dmzj/news.ts b/lib/routes/dmzj/news.ts index ce4b2fb37..6dfc85bc6 100644 --- a/lib/routes/dmzj/news.ts +++ b/lib/routes/dmzj/news.ts @@ -1,12 +1,35 @@ +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: '/news/:category?', + categories: ['program-update'], + example: '/dmzj/news/donghuaqingbao', + parameters: { category: '类别' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['news.dmzj.com/'], + target: '/news', + }, + name: '新闻站', + maintainers: ['vzz64'], + handler, +}; + +async function handler(ctx) { const url = `https://news.dmzj.com/${ctx.req.param('category') || ''}`; const $ = load((await got(url)).data); - ctx.set('data', { + return { title: $('title').text(), link: url, item: $('.briefnews_con_li .li_img_de') @@ -22,5 +45,5 @@ export default async (ctx) => { .get(), })) .get(), - }); -}; + }; +} diff --git a/lib/routes/dn/news.ts b/lib/routes/dn/news.ts index 082e80258..fd37e1f90 100644 --- a/lib/routes/dn/news.ts +++ b/lib/routes/dn/news.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: '/:language/news/:category?', + categories: ['traditional-media'], + example: '/dn/en-us/news', + parameters: { language: 'Language, see below', category: 'Category, see below, The Latest 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 { language, category = '' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 10; @@ -79,7 +98,7 @@ export default async (ctx) => { const title = $('a.logo img').prop('alt'); const icon = $('link[rel="icon"]').prop('href'); - ctx.set('data', { + return { item: items, title: `${title} - ${$('div.group a.active').text()}`, link: currentUrl, @@ -90,5 +109,5 @@ export default async (ctx) => { logo: icon, subtitle: $('title').text(), author: title, - }); -}; + }; +} diff --git a/lib/routes/dnaindia/category.ts b/lib/routes/dnaindia/category.ts index 2ca19c630..99c51c1d6 100644 --- a/lib/routes/dnaindia/category.ts +++ b/lib/routes/dnaindia/category.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 logger from '@/utils/logger'; -export default async (ctx) => { +export const route: Route = { + path: ['/:category', '/topic/:topic'], + categories: ['bbs'], + example: '/dnaindia/headlines', + parameters: { category: 'Find it in the URL, or tables below' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['dnaindia.com/:category'], + }, + name: 'News', + maintainers: ['Rjnishant530'], + handler, +}; + +async function handler(ctx) { const { category, topic } = ctx.req.param(); const baseUrl = 'https://www.dnaindia.com'; let route; @@ -55,7 +77,7 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'DNA India', link: baseUrl, item: items, @@ -63,5 +85,5 @@ export default async (ctx) => { logo: 'https://cdn.dnaindia.com/sites/all/themes/dnaindia/favicon-1016.ico', icon: 'https://cdn.dnaindia.com/sites/all/themes/dnaindia/favicon-1016.ico', language: 'en-us', - }); -}; + }; +} diff --git a/lib/routes/dockerhub/build.ts b/lib/routes/dockerhub/build.ts index 2a9c6f8f0..3452b1f67 100644 --- a/lib/routes/dockerhub/build.ts +++ b/lib/routes/dockerhub/build.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { hash } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/build/:owner/:image/:tag?', + categories: ['university'], + example: '/dockerhub/build/wangqiru/ttrss', + parameters: { owner: 'Image owner', image: 'Image name', tag: 'Image tag,default to latest' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Image New Build', + maintainers: ['HenryQW'], + handler, +}; + +async function handler(ctx) { const { owner, image, tag = 'latest' } = ctx.req.param(); const namespace = `${owner}/${image}`; @@ -13,7 +32,7 @@ export default async (ctx) => { const item = data.data; - ctx.set('data', { + return { title: `${namespace}:${tag} build history`, description: metadata.data.description, link, @@ -27,5 +46,5 @@ export default async (ctx) => { guid: hash(item.images), }, ], - }); -}; + }; +} diff --git a/lib/routes/dockerhub/tag.ts b/lib/routes/dockerhub/tag.ts index 2aaf1d236..ad197e211 100644 --- a/lib/routes/dockerhub/tag.ts +++ b/lib/routes/dockerhub/tag.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { hash } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/tag/:owner/:image/:limits?', + categories: ['university'], + example: '/dockerhub/tag/library/mariadb', + parameters: { owner: 'Image owner', image: 'Image name', limits: 'Tag count, 10 by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Image New Tag', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { owner, image, limits } = ctx.req.param(); const namespace = `${owner}/${image}`; @@ -15,7 +34,7 @@ export default async (ctx) => { const tags = data.data.results; - ctx.set('data', { + return { title: `${namespace} tags`, description: metadata.data.description, link, @@ -29,5 +48,5 @@ export default async (ctx) => { // check for (1) different tag names and (2) different image hashes, considering varients of all arches guid: `${namespace}:${item.name}@${hash(item.images)}`, })), - }); -}; + }; +} diff --git a/lib/routes/docschina/weekly.ts b/lib/routes/docschina/weekly.ts index b81eb4273..78bbfed08 100644 --- a/lib/routes/docschina/weekly.ts +++ b/lib/routes/docschina/weekly.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: '/weekly/:category?', + categories: ['design'], + example: '/docschina/weekly', + parameters: { category: '周刊分类,见下表,默认为js' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Unknown', + maintainers: ['daijinru', 'hestudy'], + handler, +}; + +async function handler(ctx) { const { category = 'js' } = ctx.req.param(); const baseURL = 'https://docschina.org'; @@ -15,7 +34,7 @@ export default async (ctx) => { const dataEl = $('#__NEXT_DATA__'); const dataText = dataEl.text(); const data = JSON.parse(dataText); - ctx.set('data', { + return { title, link: baseURL + path, item: data?.props?.pageProps?.data?.map((item) => ({ @@ -25,5 +44,5 @@ export default async (ctx) => { author: item.editors?.join(','), itunes_item_image: item.imageUrl, })), - }); -}; + }; +} diff --git a/lib/routes/dol/announce.ts b/lib/routes/dol/announce.ts index b9db38169..f544bc6d1 100644 --- a/lib/routes/dol/announce.ts +++ b/lib/routes/dol/announce.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; -export default async (ctx) => { +export const route: Route = { + path: '/announce/:owner?/:province?/:office?', + categories: ['study'], + example: '/dol/announce', + parameters: { owner: 'Requester/former land owner', province: 'Province which the land is belongs to', office: 'DOL office name which the land is belongs to (สำนักงานที่ดิน(กรุงเทพมหานคร|จังหวัด*) [สาขา*])' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'e-LandsAnnouncement', + maintainers: ['itpcc'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://announce.dol.go.th'; const { owner, province, office } = ctx.req.param(); const queryParams = { @@ -89,4 +108,4 @@ export default async (ctx) => { }); ctx.set('data', result); -}; +} diff --git a/lib/routes/domp4/detail.ts b/lib/routes/domp4/detail.ts index 76d596e77..0a3ae6907 100644 --- a/lib/routes/domp4/detail.ts +++ b/lib/routes/domp4/detail.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { load } from 'cheerio'; import got from '@/utils/got'; @@ -69,7 +70,28 @@ function getMetaInfo($) { }; } -export default async (ctx) => { +export const route: Route = { + path: '/detail/:id', + categories: ['picture'], + example: '/domp4/detail/LBTANI22222I', + parameters: { id: '从剧集详情页 URL 处获取,如:`https://www.mp4kan.com/html/LBTANI22222I.html`,取 `.html` 前面部分' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: true, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['domp4.cc/detail/:id'], + }, + name: '剧集订阅', + maintainers: ['savokiss'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const { domain, second } = ctx.req.query(); let pureId = id; @@ -89,11 +111,11 @@ export default async (ctx) => { const list = getItemList($, detailUrl, second); const meta = getMetaInfo($); - ctx.set('data', { + return { link: detailUrl, title: meta.title || 'domp4电影 - 详情', image: meta.cover, description: meta.description, item: list, - }); -}; + }; +} diff --git a/lib/routes/domp4/latest.ts b/lib/routes/domp4/latest.ts index ecb04181b..03f905b70 100644 --- a/lib/routes/domp4/latest.ts +++ b/lib/routes/domp4/latest.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -16,7 +17,28 @@ function getItemList($, type) { return list; } -export default async (ctx) => { +export const route: Route = { + path: '/latest/:type?', + categories: ['picture'], + example: '/domp4/latest/vod', + parameters: { type: '`vod` 代表电影,`tv` 代表电视剧,默认 vod' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['domp4.cc/', 'domp4.cc/custom/update.html'], + }, + name: '最近更新', + maintainers: ['savokiss'], + handler, +}; + +async function handler(ctx) { const { type = 'vod' } = ctx.req.param(); const { domain } = ctx.req.query(); @@ -27,9 +49,9 @@ export default async (ctx) => { const $ = load(res.data); const list = getItemList($, type); - ctx.set('data', { + return { link: latestUrl, title: 'domp4电影', item: list, - }); -}; + }; +} diff --git a/lib/routes/dongqiudi/daily.ts b/lib/routes/dongqiudi/daily.ts index a05b65de4..90c4226fa 100644 --- a/lib/routes/dongqiudi/daily.ts +++ b/lib/routes/dongqiudi/daily.ts @@ -1,3 +1,25 @@ -export default (ctx) => { - ctx.redirect('/dongqiudi/special/48'); +import { Route } from '@/types'; +export const route: Route = { + path: '/daily', + categories: ['traditional-media'], + example: '/dongqiudi/daily', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.dongqiudi.com/special/48'], + }, + name: '早报', + maintainers: ['HenryQW'], + handler, }; + +function handler(ctx) { + ctx.redirect('/dongqiudi/special/48'); +} diff --git a/lib/routes/dongqiudi/player-news.ts b/lib/routes/dongqiudi/player-news.ts index ad6eade02..77779ccba 100644 --- a/lib/routes/dongqiudi/player-news.ts +++ b/lib/routes/dongqiudi/player-news.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/player_news/:id', + categories: ['traditional-media'], + example: '/dongqiudi/player_news/50000339', + parameters: { id: '球员 id, 可在[懂球帝数据](https://www.dongqiudi.com/data)中通过其队伍找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '球员新闻', + maintainers: ['HenryQW'], + handler, +}; + +async function handler(ctx) { const playerId = ctx.req.param('id'); await utils.ProcessFeed(ctx, 'player', playerId); -}; +} diff --git a/lib/routes/dongqiudi/result.ts b/lib/routes/dongqiudi/result.ts index 03f934ef4..a88161869 100644 --- a/lib/routes/dongqiudi/result.ts +++ b/lib/routes/dongqiudi/result.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { JSDOM } from 'jsdom'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/result/:team', + categories: ['traditional-media'], + example: '/dongqiudi/result/50001755', + parameters: { team: '球队 id, 可在[懂球帝数据](https://www.dongqiudi.com/data)中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '足球赛果', + maintainers: ['HenryQW'], + handler, +}; + +async function handler(ctx) { const team = ctx.req.param('team'); const link = `https://www.dongqiudi.com/team/${team}.html`; @@ -22,9 +41,9 @@ export default async (ctx) => { pubDate: parseDate(result.start_time), })); - ctx.set('data', { + return { title: `${teamName} 比赛结果`, link, item: out.slice(-10, out.length), - }); -}; + }; +} diff --git a/lib/routes/dongqiudi/special.ts b/lib/routes/dongqiudi/special.ts index 0207ef561..a7c0c24e5 100644 --- a/lib/routes/dongqiudi/special.ts +++ b/lib/routes/dongqiudi/special.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: '/special/:id', + categories: ['traditional-media'], + example: '/dongqiudi/special/41', + parameters: { id: '专题 id, 可自行通过 https://www.dongqiudi.com/special/+数字匹配' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.dongqiudi.com/special/:id'], + }, + name: '专题', + maintainers: ['dxmpalb'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const { data: response } = await got(`https://www.dongqiudi.com/api/old/columns/${id}`); @@ -26,10 +48,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `懂球帝专题-${response.title}`, description: response.description, link: `https://www.dongqiudi.com/special/${id}`, item: out.filter(Boolean), - }); -}; + }; +} diff --git a/lib/routes/dongqiudi/team-news.ts b/lib/routes/dongqiudi/team-news.ts index 3f8b0f8bc..fbc937d9d 100644 --- a/lib/routes/dongqiudi/team-news.ts +++ b/lib/routes/dongqiudi/team-news.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/team_news/:team', + categories: ['traditional-media'], + example: '/dongqiudi/team_news/50001755', + parameters: { team: '球队 id, 可在[懂球帝数据](https://www.dongqiudi.com/data)中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '球队新闻', + maintainers: ['HenryQW'], + handler, +}; + +async function handler(ctx) { const teamId = ctx.req.param('team'); await utils.ProcessFeed(ctx, 'team', teamId); -}; +} diff --git a/lib/routes/dongqiudi/top-news.ts b/lib/routes/dongqiudi/top-news.ts index 0e1cbf312..51111e926 100644 --- a/lib/routes/dongqiudi/top-news.ts +++ b/lib/routes/dongqiudi/top-news.ts @@ -1,9 +1,32 @@ +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: '/top_news/:id?', + categories: ['traditional-media'], + example: '/dongqiudi/top_news/1', + parameters: { id: '类别 id,不填默认头条新闻' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['m.dongqiudi.com/home/:id'], + target: '/top_news/:id', + }, + name: '新闻', + maintainers: ['HendricksZheng'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? 1; const { data } = await got(`https://api.dongqiudi.com/app/tabs/web/${id}.json`); const articles = data.articles; @@ -26,9 +49,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `懂球帝 - ${label}`, link: `https://www.dongqiudi.com/articlesList/${id}`, item: out.filter((e) => e !== undefined), - }); -}; + }; +} diff --git a/lib/routes/dongqiudi/utils.ts b/lib/routes/dongqiudi/utils.ts index 6e4e527fe..d41fb78bf 100644 --- a/lib/routes/dongqiudi/utils.ts +++ b/lib/routes/dongqiudi/utils.ts @@ -109,12 +109,12 @@ const ProcessFeed = async (ctx, type, id) => { ) ); - ctx.set('data', { + return { title: `${name} - 相关新闻`, link, image: type === 'team' ? typeInfo.team_logo : typeInfo.person_logo, item: out, - }); + }; }; const ProcessFeedType2 = (item, response) => { diff --git a/lib/routes/dorohedoro/news.ts b/lib/routes/dorohedoro/news.ts index d6f460223..53827924c 100644 --- a/lib/routes/dorohedoro/news.ts +++ b/lib/routes/dorohedoro/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: '/dorohedoro/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['dorohedoro.net/news', 'dorohedoro.net/'], + }, + name: 'News', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 25; const rootUrl = 'https://dorohedoro.net'; @@ -64,9 +86,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'アニメ『ドロヘドロ』', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/douban/book/rank.ts b/lib/routes/douban/book/rank.ts index b7c80a119..5435a29ec 100644 --- a/lib/routes/douban/book/rank.ts +++ b/lib/routes/douban/book/rank.ts @@ -1,6 +1,25 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/book/rank/:type?', + categories: ['new-media'], + example: '/douban/book/rank/fiction', + parameters: { type: '图书类型,默认合并列表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '热门图书排行', + maintainers: ['xyqfer', 'queensferryme'], + handler, +}; + +async function handler(ctx) { const { type = '' } = ctx.req.param(); const referer = `https://m.douban.com/book/${type}`; @@ -14,7 +33,7 @@ export default async (ctx) => { const items = type ? await _(type) : [...(await _('fiction')), ...(await _('nonfiction'))]; - ctx.set('data', { + return { title: `豆瓣热门图书-${type ? (type === 'fiction' ? '虚构类' : '非虚构类') : '全部'}`, link: referer, description: '每周一更新', @@ -30,5 +49,5 @@ export default async (ctx) => { link: url, }; }), - }); -}; + }; +} diff --git a/lib/routes/douban/channel/subject.ts b/lib/routes/douban/channel/subject.ts index 94222a659..d4801d60c 100644 --- a/lib/routes/douban/channel/subject.ts +++ b/lib/routes/douban/channel/subject.ts @@ -1,6 +1,25 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/channel/:id/subject/:nav', + categories: ['new-media'], + example: '/douban/channel/30168934/subject/0', + parameters: { id: '频道id', nav: '书影音分类' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '频道书影音', + maintainers: ['umm233'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const nav = ctx.req.param('nav'); const link = `https://www.douban.com/subject/${id}`; @@ -40,7 +59,7 @@ export default async (ctx) => { break; } - ctx.set('data', { + return { title: `豆瓣${channel_name}频道-${nav_name}推荐`, link, description: `豆瓣${channel_name}频道书影音下的${nav_name}推荐`, @@ -56,5 +75,5 @@ export default async (ctx) => { link: url, }; }), - }); -}; + }; +} diff --git a/lib/routes/douban/channel/topic.ts b/lib/routes/douban/channel/topic.ts index 6ba4d9ac4..5bd1e9dcb 100644 --- a/lib/routes/douban/channel/topic.ts +++ b/lib/routes/douban/channel/topic.ts @@ -1,6 +1,25 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/channel/:id/:nav?', + categories: ['new-media'], + example: '/douban/channel/30168934/hot', + parameters: { id: '频道id', nav: '专题分类,可选,默认为 default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '频道专题', + maintainers: ['umm233'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const nav = ctx.req.param('nav') || 'default'; const link = `https://www.douban.com/channel/${id}`; @@ -37,7 +56,7 @@ export default async (ctx) => { break; } - ctx.set('data', { + return { title: `豆瓣${channel_name}频道-${nav_name}动态`, link, description: `豆瓣${channel_name}频道专题下的${nav_name}动态`, @@ -59,5 +78,5 @@ export default async (ctx) => { }) .filter(Boolean), allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/douban/commercialpress/latest.ts b/lib/routes/douban/commercialpress/latest.ts index 95a7b23a1..c902bac37 100644 --- a/lib/routes/douban/commercialpress/latest.ts +++ b/lib/routes/douban/commercialpress/latest.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: '/commercialpress/latest', + categories: ['new-media'], + example: '/douban/commercialpress/latest', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '商务印书馆新书速递', + maintainers: ['xyqfer'], + handler, +}; + +async function handler() { const link = 'https://site.douban.com/commercialpress/room/827243/'; const { data: roomResponse } = await got({ method: 'get', @@ -47,9 +66,9 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title: `商务印书馆-${title}`, link, item: resultItem, - }); -}; + }; +} diff --git a/lib/routes/douban/event/hot.ts b/lib/routes/douban/event/hot.ts index 4bdfd27cf..8ff19ab80 100644 --- a/lib/routes/douban/event/hot.ts +++ b/lib/routes/douban/event/hot.ts @@ -1,6 +1,25 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/event/hot/:locationId', + categories: ['new-media'], + example: '/douban/event/hot/118172', + parameters: { locationId: '位置 id, [同城首页](https://www.douban.com/location)打开控制台执行 `window.__loc_id__` 获取' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '热门同城活动', + maintainers: ['xyqfer'], + handler, +}; + +async function handler(ctx) { const { locationId = 0 } = ctx.req.param(); const referer = 'https://m.douban.com/app_topic/event_hot'; @@ -12,7 +31,7 @@ export default async (ctx) => { }, }); - ctx.set('data', { + return { title: `豆瓣同城-热门活动-${locationId}`, link: referer, item: response.data.subject_collection_items.map(({ title, url, cover, subtype, info, price_range }) => { @@ -26,5 +45,5 @@ export default async (ctx) => { link: url, }; }), - }); -}; + }; +} diff --git a/lib/routes/douban/other/bookstore.ts b/lib/routes/douban/other/bookstore.ts index e0bec2604..332d72dca 100644 --- a/lib/routes/douban/other/bookstore.ts +++ b/lib/routes/douban/other/bookstore.ts @@ -1,6 +1,25 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/bookstore', + categories: ['new-media'], + example: '/douban/bookstore', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '豆瓣书店', + maintainers: ['xyqfer'], + handler, +}; + +async function handler() { const link = 'https://market.douban.com/book/'; const response = await got({ method: 'get', @@ -12,7 +31,7 @@ export default async (ctx) => { const data = response.data.data; - ctx.set('data', { + return { title: '豆瓣书店', link, description: '在豆瓣书店,遇见美好·書生活', @@ -26,5 +45,5 @@ export default async (ctx) => { 价格: ${price}元 `, })), - }); -}; + }; +} diff --git a/lib/routes/douban/other/celebrity.ts b/lib/routes/douban/other/celebrity.ts index bc14d98fc..45c910676 100644 --- a/lib/routes/douban/other/celebrity.ts +++ b/lib/routes/douban/other/celebrity.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: '/celebrity/:id/:sort?', + categories: ['new-media'], + example: '/douban/celebrity/1274261', + parameters: { id: '电影人 id', sort: '排序方式,缺省为 `time`(时间排序),可为 `vote` (评价排序)' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '豆瓣电影人', + maintainers: ['minimalistrojan'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const sort = ctx.req.param('sort') || 'time'; @@ -21,7 +40,7 @@ export default async (ctx) => { .replace(/的全部作品(\d+)/, ''); let itemPicUrl; - ctx.set('data', { + return { title: `豆瓣电影人 - ${person}`, link: `https://movie.douban.com/celebrity/${id}/movies?sortby=${sort}`, item: @@ -37,5 +56,5 @@ export default async (ctx) => { }; }) .get(), - }); -}; + }; +} diff --git a/lib/routes/douban/other/classification.ts b/lib/routes/douban/other/classification.ts index 31880a921..81d1fe0c2 100644 --- a/lib/routes/douban/other/classification.ts +++ b/lib/routes/douban/other/classification.ts @@ -1,6 +1,25 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/movie/classification/:sort?/:score?/:tags?', + categories: ['new-media'], + example: '/douban/movie/classification/R/7.5/Netflix,2020', + parameters: { sort: '排序方式,默认为U', score: '最低评分,默认不限制', tags: '分类标签,多个标签之间用英文逗号分隔,常见的标签到豆瓣电影的分类页面查看,支持自定义标签' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '豆瓣电影分类', + maintainers: ['zzwab'], + handler, +}; + +async function handler(ctx) { const sort = ctx.req.param('sort') || 'U'; const score = Number.parseFloat(ctx.req.param('score')) || 0; const tags = ctx.req.param('tags') || ''; @@ -12,7 +31,7 @@ export default async (ctx) => { const movies = response.data.data; - ctx.set('data', { + return { title: `豆瓣电影分类${score ? `超过 ${score} 分的` : ''}影视`, link: `https://movie.douban.com/tag/#/?sort=U&range=0,10&tags=`, item: movies @@ -33,5 +52,5 @@ export default async (ctx) => { }) .filter(Boolean), allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/douban/other/discussion.ts b/lib/routes/douban/other/discussion.ts index b36fddf46..0e485a12a 100644 --- a/lib/routes/douban/other/discussion.ts +++ b/lib/routes/douban/other/discussion.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; /* * @Author: nightmare-mio wanglongwei2009@qq.com @@ -9,7 +10,28 @@ 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/discussion', + categories: ['new-media'], + example: '/douban/36328704/discussion', + parameters: { id: '书本id;默认论坛文章使用"按回应时间排序",仅第一页文章' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['book.douban.com/:id/discussion'], + }, + name: '豆瓣读书论坛', + maintainers: ['nightmare-mio'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const link = 'https://book.douban.com/subject'; const { data: response } = await got(`${link}/${id}/discussion/`); @@ -51,9 +73,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title, link: `${link}/${id}/discussion`, item: items, - }); -}; + }; +} diff --git a/lib/routes/douban/other/doulist.ts b/lib/routes/douban/other/doulist.ts index 3d3ac3b80..42f9df4c0 100644 --- a/lib/routes/douban/other/doulist.ts +++ b/lib/routes/douban/other/doulist.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: '/doulist/:id', + categories: ['new-media'], + example: '/douban/doulist/37716774', + parameters: { id: '豆列id' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Unknown', + maintainers: ['LogicJake', 'honue'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const url = `https://www.douban.com/doulist/${id}`; @@ -56,10 +75,10 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title, link: url, description, item: out, - }); -}; + }; +} diff --git a/lib/routes/douban/other/explore-column.ts b/lib/routes/douban/other/explore-column.ts index c5c71bdf7..9debe9943 100644 --- a/lib/routes/douban/other/explore-column.ts +++ b/lib/routes/douban/other/explore-column.ts @@ -1,9 +1,17 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import * as url from 'node:url'; const host = 'https://www.douban.com/explore/column/'; -export default async (ctx) => { +export const route: Route = { + path: '/explore/column/:id', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const link = url.resolve(host, id); const response = await got.get(link); @@ -48,9 +56,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `${title}-豆瓣发现`, link, item: out, - }); -}; + }; +} diff --git a/lib/routes/douban/other/explore.ts b/lib/routes/douban/other/explore.ts index 7bf54fdd0..8a24e6063 100644 --- a/lib/routes/douban/other/explore.ts +++ b/lib/routes/douban/other/explore.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: '/explore', + categories: ['new-media'], + example: '/douban/explore', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '浏览发现', + maintainers: ['clarkzsd'], + handler, +}; + +async function handler() { const response = await got({ method: 'get', url: 'https://www.douban.com/explore', @@ -17,7 +36,7 @@ export default async (ctx) => { const $ = load(data); const list = $('div.item'); - ctx.set('data', { + return { title: '豆瓣-浏览发现', link: 'https://www.douban.com/explore', item: @@ -49,5 +68,5 @@ export default async (ctx) => { }; }) .get(), - }); -}; + }; +} diff --git a/lib/routes/douban/other/group.ts b/lib/routes/douban/other/group.ts index 1da37f54e..08eb4da8d 100644 --- a/lib/routes/douban/other/group.ts +++ b/lib/routes/douban/other/group.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: '/group/:groupid/:type?', + categories: ['new-media'], + example: '/douban/group/648102', + parameters: { groupid: '豆瓣小组的 id', type: '缺省 最新,essence 最热,elite 精华' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.douban.com/group/:groupid'], + target: '/group/:groupid', + }, + name: '豆瓣小组', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { const groupid = ctx.req.param('groupid'); const type = ctx.req.param('type'); @@ -41,9 +64,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `豆瓣小组-${$('h1').text().trim()}`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/douban/other/jobs.ts b/lib/routes/douban/other/jobs.ts index 7937c3664..d88c3a66c 100644 --- a/lib/routes/douban/other/jobs.ts +++ b/lib/routes/douban/other/jobs.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -9,7 +10,25 @@ const titleMap = { intern: '实习生招聘', }; -export default async (ctx) => { +export const route: Route = { + path: '/jobs/:type', + categories: ['new-media'], + example: '/douban/jobs/campus', + 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'); const url = `${rootUrl}/jobs/${type}`; @@ -25,9 +44,9 @@ export default async (ctx) => { })) .get(); - ctx.set('data', { + return { title: `豆瓣${titleMap[type]}`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/douban/other/later.ts b/lib/routes/douban/other/later.ts index af2abad59..621452d98 100644 --- a/lib/routes/douban/other/later.ts +++ b/lib/routes/douban/other/later.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: '/movie/later', + categories: ['new-media'], + example: '/douban/movie/later', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '即将上映的电影', + maintainers: ['DIYgod'], + handler, +}; + +async function handler() { const response = await got({ method: 'get', url: 'https://movie.douban.com/cinema/later/beijing/', @@ -24,9 +43,9 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title: '即将上映的电影', link: 'https://movie.douban.com/cinema/later/', item, - }); -}; + }; +} diff --git a/lib/routes/douban/other/latest-book.ts b/lib/routes/douban/other/latest-book.ts index b656dae54..42fe610a0 100644 --- a/lib/routes/douban/other/latest-book.ts +++ b/lib/routes/douban/other/latest-book.ts @@ -1,13 +1,32 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; const url = 'https://book.douban.com/latest'; -export default async (ctx) => { +export const route: Route = { + path: '/book/latest', + categories: ['new-media'], + example: '/douban/book/latest', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Unknown', + maintainers: ['fengkx'], + handler, +}; + +async function handler() { const res = await got.get(url); const $ = load(res.data); const list = $('#content').find('li').get(); - ctx.set('data', { + return { title: '豆瓣新书速递', link: url, item: list.map((item, index) => ({ @@ -15,5 +34,5 @@ export default async (ctx) => { link: $(item).find('a').first().attr('href'), description: $(item).html(), })), - }); -}; + }; +} diff --git a/lib/routes/douban/other/latest-music.ts b/lib/routes/douban/other/latest-music.ts index ec9ec8d2d..98b31a3ef 100644 --- a/lib/routes/douban/other/latest-music.ts +++ b/lib/routes/douban/other/latest-music.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: '/music/latest/:area?', + categories: ['new-media'], + example: '/douban/music/latest/chinese', + parameters: { area: '区域类型,默认全部' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '最新增加的音乐', + maintainers: ['fengkx', 'xyqfer'], + handler, +}; + +async function handler(ctx) { const { area = '' } = ctx.req.param(); const title = '豆瓣最新增加的音乐'; let data; @@ -71,4 +90,4 @@ export default async (ctx) => { } ctx.set('data', data); -}; +} diff --git a/lib/routes/douban/other/list.ts b/lib/routes/douban/other/list.ts index 69d3b84b7..be5c6a8e4 100644 --- a/lib/routes/douban/other/list.ts +++ b/lib/routes/douban/other/list.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 * as path from 'node:path'; import { art } from '@/utils/render'; import { fallback, queryToInteger } from '@/utils/readable-social'; -export default async (ctx) => { +export const route: Route = { + path: '/list/:type?/:routeParams?', + categories: ['new-media'], + example: '/douban/list/subject_real_time_hotest', + parameters: { type: '榜单类型,见下表。默认为实时热门书影音', routeParams: '额外参数;请参阅以下说明和表格' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.douban.com/subject_collection/:type'], + target: '/list/:type', + }, + name: '豆瓣榜单与集合', + maintainers: ['5upernova-heng', 'honue'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') || 'subject_real_time_hotest'; const routeParams = Object.fromEntries(new URLSearchParams(ctx.req.param('routeParams'))); const playable = fallback(undefined, queryToInteger(routeParams.playable), 0); @@ -57,10 +80,10 @@ export default async (ctx) => { start += count; } - ctx.set('data', { + return { title: `豆瓣 - ${title}`, link: `https://m.douban.com/subject_collection/${type}`, item: items, description, - }); -}; + }; +} diff --git a/lib/routes/douban/other/playing.ts b/lib/routes/douban/other/playing.ts index 2951b6b9d..3f7ec81d5 100644 --- a/lib/routes/douban/other/playing.ts +++ b/lib/routes/douban/other/playing.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: ['/movie/playing', '/movie/playing/:score'], + categories: ['new-media'], + example: '/douban/movie/playing', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '正在上映的电影', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { const score = Number.parseFloat(ctx.req.param('score')) || 0; const response = await got({ method: 'get', @@ -9,7 +28,7 @@ export default async (ctx) => { }); const $ = load(response.data); - ctx.set('data', { + return { title: `正在上映的${score ? `超过 ${score} 分的` : ''}电影`, link: `https://movie.douban.com/cinema/nowplaying/`, item: $('.list-item') @@ -29,5 +48,5 @@ export default async (ctx) => { }) .filter(Boolean), allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/douban/other/recommended.ts b/lib/routes/douban/other/recommended.ts index 755d3e280..70ffa5fde 100644 --- a/lib/routes/douban/other/recommended.ts +++ b/lib/routes/douban/other/recommended.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 * as path from 'node:path'; import { art } from '@/utils/render'; import { fallback, queryToInteger } from '@/utils/readable-social'; -export default async (ctx) => { +export const route: Route = { + path: '/recommended/:type?/:routeParams?', + categories: ['new-media'], + example: '/douban/recommended/tv', + parameters: { type: '片单类型剧集/电影,tv或movie,默认为tv', routeParams: '额外参数;请参阅以下说明和表格' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '豆瓣每月推荐片单', + maintainers: ['honue'], + handler, +}; + +async function handler(ctx) { const subjectType = ctx.req.param('type') || 'tv'; const apiKey = '0ac44ae016490db2204ce0a042db2916'; let url = `https://frodo.douban.com/api/v2/skynet/new_playlists?apikey=${apiKey}&subject_type=${subjectType}`; @@ -64,10 +83,10 @@ export default async (ctx) => { description, }; }); - ctx.set('data', { + return { title: `豆瓣 - ${response.data.subject_collection.name}`, link: `https://m.douban.com/subject_collection/${subjectCollectionId}`, item: items, description, - }); -}; + }; +} diff --git a/lib/routes/douban/other/replied.ts b/lib/routes/douban/other/replied.ts index 435a4f9cb..423248db1 100644 --- a/lib/routes/douban/other/replied.ts +++ b/lib/routes/douban/other/replied.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: '/replied/:uid', + categories: ['new-media'], + example: '/douban/replied/xiaoyaxiaoya', + parameters: { uid: '用户id,可在用户日记页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '最新回应过的日记', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const currentUrl = `https://www.douban.com/people/${ctx.req.param('uid')}/notes`; const response = await got({ method: 'get', @@ -79,9 +98,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text() + ' - 最新回应过', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/douban/other/replies.ts b/lib/routes/douban/other/replies.ts index c3ba61df5..7b7d4c646 100644 --- a/lib/routes/douban/other/replies.ts +++ b/lib/routes/douban/other/replies.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: '/replies/:uid', + categories: ['new-media'], + example: '/douban/replies/xiaoyaxiaoya', + parameters: { uid: '用户id,可在用户日记页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '日记最新回应', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const currentUrl = `https://www.douban.com/people/${ctx.req.param('uid')}/notes`; const response = await got({ method: 'get', @@ -54,9 +73,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text() + ' - 最新回应', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/douban/other/topic.ts b/lib/routes/douban/other/topic.ts index f244c7188..bb295ab83 100644 --- a/lib/routes/douban/other/topic.ts +++ b/lib/routes/douban/other/topic.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/topic/:id/:sort?', + categories: ['new-media'], + example: '/douban/topic/48823', + parameters: { id: '话题id', sort: '排序方式,hot或new,默认为new' }, + 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 sort = ctx.req.param('sort') || 'new'; @@ -72,10 +91,10 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `${title}-豆瓣话题`, description, link, item: out, - }); -}; + }; +} diff --git a/lib/routes/douban/other/ustop.ts b/lib/routes/douban/other/ustop.ts index 14249af0b..e98cec613 100644 --- a/lib/routes/douban/other/ustop.ts +++ b/lib/routes/douban/other/ustop.ts @@ -1,13 +1,32 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/movie/ustop', + categories: ['new-media'], + example: '/douban/movie/ustop', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '北美票房榜', + maintainers: ['DIYgod'], + handler, +}; + +async function handler() { const response = await got({ method: 'get', url: 'https://api.douban.com/v2/movie/us_box?apikey=0df993c66c0c636e29ecbb5344252a4a', }); const movieList = response.data.subjects; - ctx.set('data', { + return { title: '豆瓣电影北美票房榜', link: 'https://movie.douban.com/chart', item: movieList.map((item) => { @@ -18,5 +37,5 @@ export default async (ctx) => { link: item.alt, }; }), - }); -}; + }; +} diff --git a/lib/routes/douban/other/weekly-best.ts b/lib/routes/douban/other/weekly-best.ts index eec9cdc5e..296206000 100644 --- a/lib/routes/douban/other/weekly-best.ts +++ b/lib/routes/douban/other/weekly-best.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: '/movie/weekly/:type?', + categories: ['new-media'], + example: '/douban/movie/weekly', + parameters: { type: '分类,可在榜单页 URL 中找到,默认为一周口碑电影榜' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '一周口碑榜', + maintainers: ['numm233', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') || 'movie_weekly_best'; const link = 'https://m.douban.com/movie'; @@ -28,7 +47,7 @@ export default async (ctx) => { const data = itemResponse.data.subject_collection_items; - ctx.set('data', { + return { title: infoResponse.data.title, link: `https://m.douban.com/subject_collection/${type}`, description: infoResponse.data.description, @@ -52,5 +71,5 @@ export default async (ctx) => { link: url, }; }), - }); -}; + }; +} diff --git a/lib/routes/douban/people/status.ts b/lib/routes/douban/people/status.ts index 851d58086..c1fd4dbbf 100644 --- a/lib/routes/douban/people/status.ts +++ b/lib/routes/douban/people/status.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import querystring from 'querystring'; import got from '@/utils/got'; @@ -443,7 +444,14 @@ async function getFullTextItems(items) { ); } -export default async (ctx) => { +export const route: Route = { + path: '/people/:userid/status/:routeParams?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const userid = ctx.req.param('userid'); const url = `https://m.douban.com/rexxar/api/v2/status/user_timeline/${userid}`; const items = await cache.tryGet( @@ -460,7 +468,7 @@ export default async (ctx) => { await getFullTextItems(items); } - ctx.set('data', { + return { title: `豆瓣广播 - ${items ? items[0].status.author.name : userid}`, link: `https://m.douban.com/people/${userid}/statuses`, item: @@ -476,5 +484,5 @@ export default async (ctx) => { description: r.description, }; }), - }); -}; + }; +} diff --git a/lib/routes/douban/people/wish.ts b/lib/routes/douban/people/wish.ts index ac1d194ce..1fad23c03 100644 --- a/lib/routes/douban/people/wish.ts +++ b/lib/routes/douban/people/wish.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import querystring from 'querystring'; import { load } from 'cheerio'; import got from '@/utils/got'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/people/:userid/wish/:routeParams?', + categories: ['new-media'], + example: '/douban/people/exherb/wish', + parameters: { userid: '用户id', routeParams: '额外参数;见下' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '用户想看', + maintainers: ['exherb'], + handler, +}; + +async function handler(ctx) { const userid = ctx.req.param('userid'); const routeParams = querystring.parse(ctx.req.param('routeParams')); @@ -64,9 +83,9 @@ export default async (ctx) => { } const items = (await Promise.all(tasks)).flat(); - ctx.set('data', { + return { title: `豆瓣想看 - ${userName || userid}`, link: `https://movie.douban.com/people/${userid}/wish`, item: items, - }); -}; + }; +} diff --git a/lib/routes/douyin/hashtag.ts b/lib/routes/douyin/hashtag.ts index 83d88f649..33da5b872 100644 --- a/lib/routes/douyin/hashtag.ts +++ b/lib/routes/douyin/hashtag.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; @@ -6,7 +7,29 @@ import { fallback, queryToBoolean } from '@/utils/readable-social'; import { templates, resolveUrl, proxyVideo, getOriginAvatar } from './utils'; import puppeteer from '@/utils/puppeteer'; -export default async (ctx) => { +export const route: Route = { + path: '/hashtag/:cid/:routeParams?', + categories: ['new-media'], + example: '/douyin/hashtag/1592824105719812', + parameters: { cid: '标签 ID,可在标签页面 URL 中找到', routeParams: '额外参数,query string 格式,请参阅上面的表格' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['douyin.com/hashtag/:cid'], + target: '/hashtag/:cid', + }, + name: '标签', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const cid = ctx.req.param('cid'); if (isNaN(cid)) { throw new TypeError('Invalid tag ID. Tag ID should be a number.'); @@ -86,11 +109,11 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: tagName, description: `${tagInfo.viewCount} 次播放`, image: userAvatar, link: tagUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/douyin/live.ts b/lib/routes/douyin/live.ts index a805a20a9..cd3efdd0e 100644 --- a/lib/routes/douyin/live.ts +++ b/lib/routes/douyin/live.ts @@ -1,10 +1,32 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { config } from '@/config'; import { getOriginAvatar } from './utils'; import logger from '@/utils/logger'; import puppeteer from '@/utils/puppeteer'; -export default async (ctx) => { +export const route: Route = { + path: '/live/:rid', + categories: ['multimedia'], + example: '/douyin/live/685317364746', + parameters: { rid: '直播间 id, 可在主播直播间页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['live.douyin.com/:rid'], + }, + name: '直播间开播', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const rid = ctx.req.param('rid'); if (isNaN(rid)) { throw new TypeError('Invalid room ID. Room ID should be a number.'); @@ -70,12 +92,12 @@ export default async (ctx) => { } } - ctx.set('data', { + return { title: `${nickname}的抖音直播间 - 抖音直播`, description: `欢迎来到${nickname}的抖音直播间,${nickname}与大家一起记录美好生活 - 抖音直播`, image: getOriginAvatar(userAvatar), link: pageUrl, item: items, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/douyin/user.ts b/lib/routes/douyin/user.ts index 8ac60f9cd..9cee3fa72 100644 --- a/lib/routes/douyin/user.ts +++ b/lib/routes/douyin/user.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; @@ -5,7 +6,29 @@ import { config } from '@/config'; import { fallback, queryToBoolean } from '@/utils/readable-social'; import { templates, resolveUrl, proxyVideo, getOriginAvatar, universalGet } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/user/:uid/:routeParams?', + categories: ['new-media'], + example: '/douyin/user/MS4wLjABAAAARcAHmmF9mAG3JEixq_CdP72APhBlGlLVbN-1eBcPqao', + parameters: { uid: 'uid,可在用户页面 URL 中找到', routeParams: '额外参数,query string 格式,请参阅上面的表格' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['douyin.com/user/:uid'], + target: '/user/:uid', + }, + name: '博主', + maintainers: ['Max-Tortoise', 'Rongronggg9'], + handler, +}; + +async function handler(ctx) { const uid = ctx.req.param('uid'); if (!uid.startsWith('MS4wLjABAAAA')) { throw new Error('Invalid UID. UID should start with MS4wLjABAAAA.'); @@ -68,11 +91,11 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: userNickName, description: userDescription, image: userAvatar, link: pageUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/douyu/group.ts b/lib/routes/douyu/group.ts index 59b74acd2..b1f2b8483 100644 --- a/lib/routes/douyu/group.ts +++ b/lib/routes/douyu/group.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: '/group/:id/:sort?', + categories: ['blog'], + example: '/douyu/group/1011', + parameters: { id: '鱼吧 id,可在鱼吧页 URL 中找到', sort: '排序方式,见下表,默认为发布时间排序' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['yuba.douyu.com/group/:id', 'yuba.douyu.com/group/newself/:id', 'yuba.douyu.com/group/newall/:id', 'yuba.douyu.com/'], + target: '/group/:id', + }, + name: '鱼吧帖子', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const sort = ctx.req.param('sort') ?? '2'; @@ -39,10 +62,10 @@ export default async (ctx) => { }), })); - ctx.set('data', { + return { title: `斗鱼鱼吧 - ${detailResponse.data.data.group_name}`, link: currentUrl, item: items, description: detailResponse.data.data.describe, - }); -}; + }; +} diff --git a/lib/routes/douyu/post.ts b/lib/routes/douyu/post.ts index 4fcddbcd7..0b6ad1c42 100644 --- a/lib/routes/douyu/post.ts +++ b/lib/routes/douyu/post.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: '/post/:id', + categories: ['blog'], + example: '/douyu/post/631737151576473201', + parameters: { id: '帖子 id,可在帖子页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['yuba.douyu.com/p/:id', 'yuba.douyu.com/'], + }, + name: '鱼吧跟帖', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const rootUrl = 'https://yuba.douyu.com'; @@ -52,10 +74,10 @@ export default async (ctx) => { }), })); - ctx.set('data', { + return { title: `斗鱼鱼吧 - ${data.title}`, link: currentUrl, item: items, description: data.content, - }); -}; + }; +} diff --git a/lib/routes/douyu/room.ts b/lib/routes/douyu/room.ts index 0ddcb5e48..ad6f37826 100644 --- a/lib/routes/douyu/room.ts +++ b/lib/routes/douyu/room.ts @@ -1,6 +1,28 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/room/:id', + categories: ['multimedia'], + example: '/douyu/room/24422', + parameters: { id: '直播间 id, 可在主播直播间页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.douyu.com/:id', 'www.douyu.com/'], + }, + name: '直播间开播', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const response = await got({ @@ -25,10 +47,10 @@ export default async (ctx) => { ]; } - ctx.set('data', { + return { title: `${data.owner_name}的斗鱼直播间`, link: `https://www.douyu.com/${id}`, item, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/dribbble/keyword.ts b/lib/routes/dribbble/keyword.ts index cf6041e0f..afd1eefbe 100644 --- a/lib/routes/dribbble/keyword.ts +++ b/lib/routes/dribbble/keyword.ts @@ -1,10 +1,29 @@ +import { Route } from '@/types'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/keyword/:keyword', + categories: ['live'], + example: '/dribbble/keyword/player', + parameters: { keyword: 'desired keyword' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Keyword', + maintainers: ['DIYgod', 'loganrockmore'], + handler, +}; + +async function handler(ctx) { const keyword = ctx.req.param('keyword'); const url = `https://dribbble.com/search/shots/recent?q=${keyword}`; const title = `Dribbble - keyword ${keyword}`; ctx.set('data', await utils.getData(ctx, url, title)); -}; +} diff --git a/lib/routes/dribbble/popular.ts b/lib/routes/dribbble/popular.ts index 2f3d193f1..92af8ea88 100644 --- a/lib/routes/dribbble/popular.ts +++ b/lib/routes/dribbble/popular.ts @@ -1,10 +1,33 @@ +import { Route } from '@/types'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/popular/:timeframe?', + categories: ['live'], + example: '/dribbble/popular', + parameters: { timeframe: 'support the following values: week, month, year and ever' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['dribbble.com/'], + target: '/popular', + }, + name: 'Popular', + maintainers: ['DIYgod', 'loganrockmore'], + handler, +}; + +async function handler(ctx) { const timeframe = ctx.req.param('timeframe'); const url = `https://dribbble.com/shots/popular${timeframe ? `?timeframe=${timeframe}` : ''}`; const title = 'Dribbble - Popular Shots'; ctx.set('data', await utils.getData(ctx, url, title)); -}; +} diff --git a/lib/routes/dribbble/user.ts b/lib/routes/dribbble/user.ts index 9af04e9dd..979d1ec36 100644 --- a/lib/routes/dribbble/user.ts +++ b/lib/routes/dribbble/user.ts @@ -1,10 +1,32 @@ +import { Route } from '@/types'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/user/:name', + categories: ['live'], + example: '/dribbble/user/google', + parameters: { name: "username, available in user's homepage URL" }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['dribbble.com/:name'], + }, + name: 'User (or team)', + maintainers: ['DIYgod', 'loganrockmore'], + handler, +}; + +async function handler(ctx) { const name = ctx.req.param('name'); const url = `https://dribbble.com/${name}`; const title = `Dribbble - user ${name}`; ctx.set('data', await utils.getData(ctx, url, title)); -}; +} diff --git a/lib/routes/dtcj/datahero.ts b/lib/routes/dtcj/datahero.ts index 497989e3e..a49688e11 100644 --- a/lib/routes/dtcj/datahero.ts +++ b/lib/routes/dtcj/datahero.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,25 @@ const titles = { 10: '阿里云天池', }; -export default async (ctx) => { +export const route: Route = { + path: '/datahero/:category?', + categories: ['other'], + example: '/dtcj/datahero', + 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 = 'https://dtcj.com'; @@ -43,9 +62,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${category ? titles[category] : '全部'} - 数据侠专栏 | DT 财经`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/dtcj/datainsight.ts b/lib/routes/dtcj/datainsight.ts index 9737fcbbd..d7789e7f6 100644 --- a/lib/routes/dtcj/datainsight.ts +++ b/lib/routes/dtcj/datainsight.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: '/datainsight/:id?', + categories: ['other'], + example: '/dtcj/datainsight', + parameters: { id: '分类,见下表,默认为全部' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['dtcj.com/insighttopic/:id'], + target: '/datainsight/:id', + }, + name: '数据洞察', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? ''; const rootUrl = 'https://dtcj.com'; @@ -45,9 +68,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/duozhuayu/search.ts b/lib/routes/duozhuayu/search.ts index 47c5b1300..54cee57cb 100644 --- a/lib/routes/duozhuayu/search.ts +++ b/lib/routes/duozhuayu/search.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 { art } from '@/utils/render'; import * as path from 'node:path'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/search/:wd', + categories: ['game'], + example: '/duozhuayu/search/JavaScript', + parameters: { wd: '搜索关键词' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['duozhuayu.com/search/book/:wd'], + }, + name: '搜索结果', + maintainers: ['fengkx'], + handler, +}; + +async function handler(ctx) { const wd = ctx.req.param('wd'); const baseUrl = 'https://www.duozhuayu.com'; const type = 'book'; @@ -61,10 +83,10 @@ export default async (ctx) => { description: art(path.join(__dirname, 'templates/book.art'), { item }), })); - ctx.set('data', { + return { title: `多抓鱼搜索-${wd}`, link, description: `多抓鱼搜索-${wd}`, item, - }); -}; + }; +} diff --git a/lib/routes/dushu/fuzhou/index.ts b/lib/routes/dushu/fuzhou/index.ts index 1b7ba69fd..1fde2fe35 100644 --- a/lib/routes/dushu/fuzhou/index.ts +++ b/lib/routes/dushu/fuzhou/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 transformTime = (item) => { item.endTime = `${endTime.getFullYear()}-${endTime.getMonth() + 1}-${endTime.getDate()} ${endTime.getHours()}:${endTime.getMinutes()}`; }; -export default async (ctx) => { +export const route: Route = { + path: '/fuzhou', + categories: ['traditional-media'], + example: '/dushu/fuzhou', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.dushu365.com*'], + }, + name: '樊登福州运营中心', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler() { const response = await got .post(host, { json: { @@ -31,7 +53,7 @@ export default async (ctx) => { const data = response.data.activityListVOS; data.map((element) => transformTime(element)); - ctx.set('data', { + return { title: '樊登福州运营中心', link, item: data.map((item) => ({ @@ -41,5 +63,5 @@ export default async (ctx) => { item, }), })), - }); -}; + }; +} diff --git a/lib/routes/dut/index.ts b/lib/routes/dut/index.ts index c92ae0649..a671c8290 100644 --- a/lib/routes/dut/index.ts +++ b/lib/routes/dut/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,14 @@ import defaults from './defaults'; import shortcuts from './shortcuts'; import { isValidHost } from '@/utils/valid-host'; -export default async (ctx) => { +export const route: Route = { + path: ['/*/*', '/:0?'], + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const site = ctx.params[0] ?? 'news'; if (!isValidHost(site)) { throw new Error('Invalid site'); @@ -86,10 +94,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/dx2025/index.ts b/lib/routes/dx2025/index.ts index 425d38f57..387378108 100644 --- a/lib/routes/dx2025/index.ts +++ b/lib/routes/dx2025/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: '/:type?/:category?', + categories: ['traditional-media'], + example: '/dx2025', + parameters: { type: '内容类别,见下表,默认为空', category: '行业分类,见下表,默认为空' }, + 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') || ''; @@ -44,9 +63,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/dxy/profile/thread.ts b/lib/routes/dxy/profile/thread.ts index f4bde617c..d266aaedc 100644 --- a/lib/routes/dxy/profile/thread.ts +++ b/lib/routes/dxy/profile/thread.ts @@ -1,10 +1,32 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { webBaseUrl, generateNonce, sign, getPost } from '../utils'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/bbs/profile/thread/:userId', + categories: ['blog'], + example: '/dxy/bbs/profile/thread/8335054', + parameters: { userId: '个人 ID,可在 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['dxy.cn/bbs/newweb/pc/profile/:userId/threads', 'dxy.cn/bbs/newweb/pc/profile/:userId'], + }, + name: '个人帖子', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const userId = ctx.req.param('userId'); const { limit = '30' } = ctx.req.query(); @@ -73,11 +95,11 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => getPost(item, cache.tryGet))); - ctx.set('data', { + return { title: `${userInfo.nickname} 的个人主页 - 丁香园论坛 - 专业医生社区,医学、药学、生命科学、科研学术交流`, description: `${userInfo.identificationTitle} ${userInfo.signature}`, link: `${webBaseUrl}/bbs/newweb/pc/profile/${userId}/threads`, image: userInfo.avatar, item: items, - }); -}; + }; +} diff --git a/lib/routes/dxy/special.ts b/lib/routes/dxy/special.ts index 6dba35ac0..68256eada 100644 --- a/lib/routes/dxy/special.ts +++ b/lib/routes/dxy/special.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 { phoneBaseUrl, webBaseUrl, generateNonce, sign, getPost } from './utils'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/bbs/special/:specialId', + categories: ['blog'], + example: '/dxy/bbs/special/72', + parameters: { specialId: '专题 ID,可在对应专题页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '专题', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const specialId = ctx.req.param('specialId'); const { limit = '10' } = ctx.req.query(); @@ -70,11 +89,11 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => getPost(item, cache.tryGet))); - ctx.set('data', { + return { title: specialDetail.name, description: `${specialDetail.content} ${specialDetail.postCount} 內容 ${specialDetail.followCount} 关注`, link: `${phoneBaseUrl}/bbs/special?specialId=${specialId}`, image: specialDetail.specialAvatar, item: items, - }); -}; + }; +} diff --git a/lib/routes/e-hentai/index.ts b/lib/routes/e-hentai/index.ts index 55a5eecd4..15547dae4 100644 --- a/lib/routes/e-hentai/index.ts +++ b/lib/routes/e-hentai/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 * 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: '/:what?/:id?/:needTorrents?/:needImages?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? ''; const what = ctx.req.param('what') ?? ''; const needTorrents = /t|y/i.test(ctx.req.param('needTorrents') ?? 'true'); @@ -111,9 +119,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `${id || what || 'Front Page'} - E-Hentai Galleries`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/eagle/blog.ts b/lib/routes/eagle/blog.ts index 5fbd2c2a3..154894f0d 100644 --- a/lib/routes/eagle/blog.ts +++ b/lib/routes/eagle/blog.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 { isValidHost } from '@/utils/valid-host'; const cateList = new Set(['all', 'design-resources', 'learn-design', 'inside-eagle']); -export default async (ctx) => { +export const route: Route = { + path: '/blog/:cate?/:language?', + categories: ['live'], + example: '/eagle/blog/en', + parameters: { cate: 'Category, get by URL, `all` by default', language: 'Language, `cn`, `tw`, `en`, `en` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['cn.eagle.cool/blog'], + target: '/blog', + }, + name: 'Blog', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { let cate = ctx.req.param('cate') ?? 'all'; let language = ctx.req.param('language') ?? 'cn'; if (!isValidHost(cate) || !isValidHost(language)) { @@ -44,9 +67,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `eagle - ${title}`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/eagle/changelog.ts b/lib/routes/eagle/changelog.ts index 88a096cb0..d65fd3a86 100644 --- a/lib/routes/eagle/changelog.ts +++ b/lib/routes/eagle/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/:language?', + categories: ['university'], + example: '/eagle/changelog/en', + parameters: { language: 'Language, see list, default to be `cn`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Changelog', + maintainers: ['tigercubden'], + handler, +}; + +async function handler(ctx) { let language = ctx.req.param('language'); let changelog = ''; @@ -33,7 +52,7 @@ export default async (ctx) => { const list = $('.version'); - ctx.set('data', { + return { // 源标题 title: `Eagle ${changelog}`, // 源链接 @@ -73,5 +92,5 @@ export default async (ctx) => { }; }) .get(), - }); -}; + }; +} diff --git a/lib/routes/earthquake/ceic.ts b/lib/routes/earthquake/ceic.ts index f9c7f8a92..502bb0c59 100644 --- a/lib/routes/earthquake/ceic.ts +++ b/lib/routes/earthquake/ceic.ts @@ -1,8 +1,31 @@ +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: '/ceic/:type?', + categories: ['travel'], + example: '/earthquake/ceic/1', + parameters: { type: '类型,见下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.cea.gov.cn/cea/xwzx/zqsd/index.html', 'www.cea.gov.cn/'], + target: '', + }, + name: '中国地震台', + maintainers: ['SettingDust'], + handler, +}; + +async function handler(ctx) { let type = Number(ctx.req.param('type')); type = type ?? 1; const baseUrl = 'http://www.ceic.ac.cn'; @@ -45,7 +68,7 @@ export default async (ctx) => { json = json.slice(0, 20); } - ctx.set('data', { + return { title: typeName, link: `${baseUrl}/speedsearch`, allowEmpty: true, @@ -64,5 +87,5 @@ export default async (ctx) => { guid: NEW_DID, }; }), - }); -}; + }; +} diff --git a/lib/routes/earthquake/index.ts b/lib/routes/earthquake/index.ts index d87ae47d2..4c6e383d8 100644 --- a/lib/routes/earthquake/index.ts +++ b/lib/routes/earthquake/index.ts @@ -1,8 +1,31 @@ +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: '/:region?', + categories: ['travel'], + example: '/earthquake', + parameters: { region: '区域,0全部,1国内(默认),2国外' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.cea.gov.cn/cea/xwzx/zqsd/index.html', 'www.cea.gov.cn/'], + target: '', + }, + name: '中国地震局', + maintainers: ['LogicJake'], + handler, +}; + +async function handler(ctx) { const region = ctx.req.param('region') ?? 1; const api = 'https://www.cea.gov.cn/eportal/ui?struts.portlet.mode=view&struts.portlet.action=/portlet/expressEarthquake!queryExpressEarthquakeList.action&pageId=363409&moduleId=a852ba487b534470a84a30f00e7d6670'; const link = 'https://www.cea.gov.cn/cea/xwzx/zqsd/index.html'; @@ -32,9 +55,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: '中国地震局震情速递', link, item: out, - }); -}; + }; +} diff --git a/lib/routes/eastday/24.ts b/lib/routes/eastday/24.ts index 16aebafd0..e87a37540 100644 --- a/lib/routes/eastday/24.ts +++ b/lib/routes/eastday/24.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,29 @@ const categories = { 教育: 'jiaoyu', }; -export default async (ctx) => { +export const route: Route = { + path: '/24/:category?', + categories: ['bbs'], + example: '/eastday/24', + parameters: { category: '分类,见下表,默认为社会' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['mini.eastday.com/'], + target: '/24', + }, + name: '24 小时热闻', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? '社会'; const rootUrl = 'https://mini.eastday.com'; @@ -82,9 +105,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `24小时${category}热闻 - 东方资讯`, link: `${rootUrl}/#${categories[category]}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/eastday/portrait.ts b/lib/routes/eastday/portrait.ts index 9d8a254ac..a9819c284 100644 --- a/lib/routes/eastday/portrait.ts +++ b/lib/routes/eastday/portrait.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: '/portrait', + categories: ['bbs'], + example: '/eastday/portrait', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.eastday.com/'], + }, + name: '原创', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://apin.eastday.com'; const currentUrl = `${rootUrl}/api/news/Portrait`; @@ -40,9 +62,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '原创 - 东方网', link: 'https://www.eastday.com/eastday/shouye/07index/yc/index.html', item: items, - }); -}; + }; +} diff --git a/lib/routes/eastday/sh.ts b/lib/routes/eastday/sh.ts index 06ed1359f..767073450 100644 --- a/lib/routes/eastday/sh.ts +++ b/lib/routes/eastday/sh.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 logger from '@/utils/logger'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -export default async (ctx) => { +export const route: Route = { + path: '/sh', + categories: ['bbs'], + example: '/eastday/sh', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['sh.eastday.com/'], + }, + name: '上海新闻', + maintainers: ['saury'], + handler, +}; + +async function handler() { const domain = 'http://wap.eastday.com'; const response = await got({ @@ -42,9 +64,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `东方网-上海`, link: `${domain}/wap/sh.html`, item: result, - }); -}; + }; +} diff --git a/lib/routes/eastmoney/report/index.ts b/lib/routes/eastmoney/report/index.ts index 60b7a33b4..b42a39ed2 100644 --- a/lib/routes/eastmoney/report/index.ts +++ b/lib/routes/eastmoney/report/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: '/report/:category', + categories: ['other'], + example: '/eastmoney/report/strategyreport', + parameters: { category: '研报类型' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['data.eastmoney.com/report/:category'], + }, + name: '研究报告', + maintainers: ['syzq'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://data.eastmoney.com'; const { category = 'strategyreport' } = ctx.req.param(); @@ -52,9 +74,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `东方财富网-${reportType[category]}`, link: baseUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/eastmoney/search/index.ts b/lib/routes/eastmoney/search/index.ts index cb933d30e..320857ebf 100644 --- a/lib/routes/eastmoney/search/index.ts +++ b/lib/routes/eastmoney/search/index.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import { parseDate } from '@/utils/parse-date'; import got from '@/utils/got'; import timezone from '@/utils/timezone'; -export default async (ctx) => { +export const route: Route = { + path: '/search/:keyword', + categories: ['other'], + example: '/eastmoney/search/web3', + parameters: { keyword: '关键词,可以设置为自己需要检索的关键词' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '搜索', + maintainers: ['drgnchan'], + handler, +}; + +async function handler(ctx) { const keyword = ctx.req.param('keyword'); const link = `https://so.eastmoney.com/News/s?KeyWord=${keyword}`; const body = { @@ -47,9 +66,9 @@ export default async (ctx) => { link: item.url, author: item.mediaName, })); - ctx.set('data', { + return { title: `东方财富网 - 搜索'${keyword}'`, link, item: items, - }); -}; + }; +} diff --git a/lib/routes/eastmoney/ttjj/user.ts b/lib/routes/eastmoney/ttjj/user.ts index 56df98895..f1ec85c7d 100644 --- a/lib/routes/eastmoney/ttjj/user.ts +++ b/lib/routes/eastmoney/ttjj/user.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 timezone from '@/utils/timezone'; -export default async (ctx) => { +export const route: Route = { + path: '/ttjj/user/:uid', + categories: ['other'], + example: '/eastmoney/ttjj/user/6551094298949188', + parameters: { uid: '用户id, 可以通过天天基金App分享用户主页到浏览器,在相应的URL中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '天天基金用户动态', + maintainers: ['zidekuls'], + handler, +}; + +async function handler(ctx) { const uid = ctx.req.param('uid'); const urlPrefix = 'https://jijinbaapi.eastmoney.com/gubaapi/v3/read'; @@ -57,10 +76,10 @@ export default async (ctx) => { }) ) ); - ctx.set('data', { + return { title: `天天基金-${username}的主页`, link: `https://fundbarmob.eastmoney.com/index.html?goPage=personDetailView&userid=${uid}`, description: `${username} 的动态`, item: result, - }); -}; + }; +} diff --git a/lib/routes/ecnu/contest.ts b/lib/routes/ecnu/contest.ts index 47043b74b..085b4b732 100644 --- a/lib/routes/ecnu/contest.ts +++ b/lib/routes/ecnu/contest.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 { load } from 'cheerio'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/acm/contest/:category?', + categories: ['forecast'], + example: '/ecnu/acm/contest/public', + parameters: { category: 'category is optional, default is all, use `public` for public only contests' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['acm.ecnu.edu.cn/contest/', 'acm.ecnu.edu.cn/'], + target: '/acm/contest/', + }, + name: 'ACM Online-Judge contests list', + maintainers: ['a180285'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? ''; const publicOnly = category === 'public'; const rootUrl = 'https://acm.ecnu.edu.cn'; @@ -36,9 +59,9 @@ export default async (ctx) => { }) .toArray(); - ctx.set('data', { + return { title: `ECNU ACM ${publicOnly ? '公开' : ''}比赛`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/ecnu/yjs.ts b/lib/routes/ecnu/yjs.ts index f266025a8..3094e226a 100644 --- a/lib/routes/ecnu/yjs.ts +++ b/lib/routes/ecnu/yjs.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: '/yjs', + categories: ['forecast'], + example: '/ecnu/yjs', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['yz.kaoyan.com/ecnu/tiaoji', 'yz.kaoyan.com/'], + }, + name: '研究生院', + maintainers: ['shengmaosu'], + handler, +}; + +async function handler() { const link = 'https://yz.kaoyan.com/ecnu/tiaoji/'; const response = await got(link); const $ = load(response.data); @@ -29,10 +51,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '华东师范大学研究生院', link, description: '华东师范大学研究生调剂信息', item: items, - }); -}; + }; +} diff --git a/lib/routes/economist/espresso.ts b/lib/routes/economist/espresso.ts index fce23df05..83663f5c2 100644 --- a/lib/routes/economist/espresso.ts +++ b/lib/routes/economist/espresso.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 { config } from '@/config'; const link = 'https://www.economist.com/the-world-in-brief'; -export default async (ctx) => { +export const route: Route = { + path: '/espresso', + categories: ['bbs'], + example: '/economist/espresso', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['economist.com/the-world-in-brief', 'economist.com/espresso'], + }, + name: 'Espresso', + maintainers: ['TonyRL'], + handler, +}; + +async function handler() { const $ = await cache.tryGet( link, async () => { @@ -71,11 +93,11 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('head title').text(), link, description: $('meta[property="og:description"]').attr('content'), language: 'en-gb', item: [...gobbets, ...articles], - }); -}; + }; +} diff --git a/lib/routes/economist/full.ts b/lib/routes/economist/full.ts index 070314ef7..af382a415 100644 --- a/lib/routes/economist/full.ts +++ b/lib/routes/economist/full.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'; @@ -18,7 +19,28 @@ const getArticleDetail = (link) => return { article, categories }; }); -export default async (ctx) => { +export const route: Route = { + path: '/:endpoint', + categories: ['bbs'], + example: '/economist/latest', + parameters: { endpoint: 'Category name, can be found on the [official page](https://www.economist.com/rss). For example, https://www.economist.com/china/rss.xml to china' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['economist.com/:endpoint'], + }, + name: 'Category', + maintainers: ['ImSingee'], + handler, +}; + +async function handler(ctx) { const endpoint = ctx.req.param('endpoint'); const feed = await parser.parseURL(`https://www.economist.com/${endpoint}/rss.xml`); @@ -38,10 +60,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/economist/global-business-review.ts b/lib/routes/economist/global-business-review.ts index 304f0031c..5afa0d44b 100644 --- a/lib/routes/economist/global-business-review.ts +++ b/lib/routes/economist/global-business-review.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; @@ -65,7 +66,29 @@ const getArticleDetail = (article_id, language) => { }); }; -export default async (ctx) => { +export const route: Route = { + path: '/global-business-review/:language?', + categories: ['bbs'], + example: '/economist/global-business-review/cn-en', + parameters: { language: 'Language, `en`, `cn`, `tw` are supported, support multiple options, default to cn-en' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['businessreview.global/'], + target: '/global-business-review', + }, + name: 'Global Business Review', + maintainers: ['prnake'], + handler, +}; + +async function handler(ctx) { const language = (ctx.req.param('language') && ctx.req .param('language') @@ -88,10 +111,10 @@ export default async (ctx) => { })) ); - ctx.set('data', { + return { title: TITLE[main_language], link: 'https://www.businessreview.global', description: DESCRIPTION[main_language], item: items, - }); -}; + }; +} diff --git a/lib/routes/ecust/e/news.ts b/lib/routes/ecust/e/news.ts index b8d2fa357..5f5640d63 100644 --- a/lib/routes/ecust/e/news.ts +++ b/lib/routes/ecust/e/news.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import timezone from '@/utils/timezone'; @@ -5,7 +6,28 @@ import { parseDate } from '@/utils/parse-date'; const baseUrl = 'https://e.ecust.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/jxjy/news', + categories: ['forecast'], + example: '/ecust/jxjy/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['e.ecust.edu.cn/engine2/m/38F638B77773ADD3', 'e.ecust.edu.cn/'], + }, + name: '继续教育学院 - 学院公告', + maintainers: ['jialinghui'], + handler, +}; + +async function handler() { const { data: response } = await got.post(`${baseUrl}/engine2/general/1301/type/more-datas`, { form: { engineInstanceId: 1_732_458, @@ -36,10 +58,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '华东理工继续教育学院', description: '学院公告', link: `${baseUrl}/engine2/m/38F638B77773ADD3`, item: items, - }); -}; + }; +} diff --git a/lib/routes/ecust/gschool/yjs.ts b/lib/routes/ecust/gschool/yjs.ts index a9db29acc..94871805a 100644 --- a/lib/routes/ecust/gschool/yjs.ts +++ b/lib/routes/ecust/gschool/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: '/ecust/yjs', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gschool.ecust.edu.cn/12753/list.htm', 'gschool.ecust.edu.cn/'], + }, + name: '研究生院通知公告', + maintainers: ['shengmaosu'], + handler, +}; + +async function handler() { const baseUrl = 'https://gschool.ecust.edu.cn'; const link = `${baseUrl}/12753/list.htm`; const response = await got(link); const $ = load(response.data); const list = $('#wp_news_w6 li'); - ctx.set('data', { + return { title: '华东理工大学研究生院', link, description: '华东理工大学研究生院通知公告', @@ -23,5 +45,5 @@ export default async (ctx) => { pubDate: parseDate(item.find('.news_meta').text()), }; }), - }); -}; + }; +} diff --git a/lib/routes/ecust/jwc/notice.ts b/lib/routes/ecust/jwc/notice.ts index 757f5f51a..b1b3b174c 100644 --- a/lib/routes/ecust/jwc/notice.ts +++ b/lib/routes/ecust/jwc/notice.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 get_from_link = async (link) => { }); return articleList; }; -export default async (ctx) => { +export const route: Route = { + path: '/jwc/:category?', + categories: ['forecast'], + example: '/ecust/jwc/mto', + parameters: { category: '订阅板块,默认为全部订阅' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '本科教务处信息网', + maintainers: ['lxl66566'], + handler, +}; + +async function handler(ctx) { const { category = 'all' } = ctx.req.param(); const categoryItem = categoryMap[category] || null; // all -> null const pageUrl = categoryItem ? [`${baseUrl}${categoryItem.link}/list.htm`] : Object.values(categoryMap).map((item) => `${baseUrl}${item.link}/list.htm`); @@ -59,9 +78,9 @@ export default async (ctx) => { }) ) ); - ctx.set('data', { + return { title: `华理教务处 - ${categoryItem ? categoryItem.name : '全部'}`, link: categoryItem ? pageUrl[0] : baseUrl, item: result, - }); -}; + }; +} diff --git a/lib/routes/egsea/flash.ts b/lib/routes/egsea/flash.ts index c65ea24a4..3253a46a2 100644 --- a/lib/routes/egsea/flash.ts +++ b/lib/routes/egsea/flash.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: '/flash', + categories: ['traditional-media'], + example: '/egsea/flash', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['egsea.com/news/flash'], + }, + name: '快讯', + maintainers: ['hillerliao'], + handler, +}; + +async function handler() { const response = await got({ method: 'get', url: 'https://www.egsea.com/news/flash-list?per-page=30', @@ -22,9 +44,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: '快讯 - e 公司', link: 'https://www.egsea.com/news/flash', item: out, - }); -}; + }; +} diff --git a/lib/routes/ehentai/favorites.ts b/lib/routes/ehentai/favorites.ts index e264da1b1..2d0e9040d 100644 --- a/lib/routes/ehentai/favorites.ts +++ b/lib/routes/ehentai/favorites.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import EhAPI from './ehapi'; -export default async (ctx) => { +export const route: Route = { + path: '/favorites/:favcat?/:order?/:page?/:routeParams?', + categories: ['anime'], + example: '/ehentai/favorites/0/posted/1', + parameters: { favcat: 'Favorites folder number', order: '`posted`(Sort by gallery release time) , `favorited`(Sort by time added to favorites)', page: 'Page number', routeParams: 'Additional parameters, see the table above' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: true, + supportPodcast: false, + supportScihub: false, + }, + name: 'Favorites', + maintainers: ['yindaheng98', 'syrinka'], + handler, +}; + +async function handler(ctx) { if (!EhAPI.has_cookie) { throw new Error('Ehentai favorites RSS is disabled due to the lack of relevant config'); } @@ -27,4 +46,4 @@ export default async (ctx) => { item: items, } ); -}; +} diff --git a/lib/routes/ehentai/search.ts b/lib/routes/ehentai/search.ts index ce44e7444..02dc4ac62 100644 --- a/lib/routes/ehentai/search.ts +++ b/lib/routes/ehentai/search.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import EhAPI from './ehapi'; -export default async (ctx) => { +export const route: Route = { + path: '/search/:params?/:page?/:routeParams?', + categories: ['anime'], + example: '/ehentai/search/f_search=artist%3Amana%24/1', + parameters: { params: 'Search parameters. You can copy the content after `https://e-hentai.org/?`', page: 'Page number', routeParams: 'Additional parameters, see the table above' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: true, + supportPodcast: false, + supportScihub: false, + }, + name: 'Search', + maintainers: ['yindaheng98', 'syrinka'], + handler, +}; + +async function handler(ctx) { const page = ctx.req.param('page'); let params = ctx.req.param('params'); const routeParams = new URLSearchParams(ctx.req.param('routeParams')); @@ -35,4 +54,4 @@ export default async (ctx) => { item: items, } ); -}; +} diff --git a/lib/routes/ehentai/tag.ts b/lib/routes/ehentai/tag.ts index ecbd50adc..68199f639 100644 --- a/lib/routes/ehentai/tag.ts +++ b/lib/routes/ehentai/tag.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import EhAPI from './ehapi'; -export default async (ctx) => { +export const route: Route = { + path: '/tag/:tag/:page?/:routeParams?', + categories: ['anime'], + example: '/ehentai/tag/language:chinese/1', + parameters: { tag: 'Tag', page: 'Page number', routeParams: 'Additional parameters, see the table above' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: true, + supportPodcast: false, + supportScihub: false, + }, + name: 'Tag', + maintainers: ['yindaheng98', 'syrinka'], + handler, +}; + +async function handler(ctx) { const page = ctx.req.param('page'); const tag = ctx.req.param('tag'); const routeParams = new URLSearchParams(ctx.req.param('routeParams')); @@ -23,4 +42,4 @@ export default async (ctx) => { item: items, } ); -}; +} diff --git a/lib/routes/ekantipur/issue.ts b/lib/routes/ekantipur/issue.ts index 0bff6e780..ca6aca056 100644 --- a/lib/routes/ekantipur/issue.ts +++ b/lib/routes/ekantipur/issue.ts @@ -1,9 +1,32 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; // Require necessary modules import got from '@/utils/got'; // a customised got import { load } from 'cheerio'; // an HTML parser with a jQuery-like API -export default async (ctx) => { +export const route: Route = { + path: '/:channel?', + categories: ['bbs'], + example: '/ekantipur/news', + parameters: { channel: 'Find it in the ekantipur.com menu or pick from the list below:' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ekantipur.com/:channel'], + target: '/:channel', + }, + name: 'Full Article RSS', + maintainers: ['maniche04'], + handler, +}; + +async function handler(ctx) { // Your logic here // Defining base URL const baseUrl = 'https://ekantipur.com'; @@ -54,12 +77,12 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { // channel title title: `Ekantipur - ${channel}`, // channel link link: `${baseUrl}/${channel}`, // each feed item item: items, - }); -}; + }; +} diff --git a/lib/routes/elasticsearch-cn/index.ts b/lib/routes/elasticsearch-cn/index.ts index 461dfc162..85eaf9789 100644 --- a/lib/routes/elasticsearch-cn/index.ts +++ b/lib/routes/elasticsearch-cn/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, parseRelativeDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:params?', + categories: ['blog'], + example: '/elasticsearch-cn', + parameters: { params: '分类,可在对应分类页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['elasticsearch.cn/:params', 'elasticsearch.cn/'], + target: '/:params', + }, + name: '发现', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const params = ctx.req.param('params') ?? ''; const rootUrl = 'https://elasticsearch.cn'; @@ -50,9 +73,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/eleduck/jobs.ts b/lib/routes/eleduck/jobs.ts index a89fde1f2..6cdf0b0ae 100644 --- a/lib/routes/eleduck/jobs.ts +++ b/lib/routes/eleduck/jobs.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: '/jobs', + categories: ['blog'], + example: '/eleduck/jobs', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['eleduck.com/categories/5', 'eleduck.com/'], + }, + name: '工作机会', + maintainers: ['sfyumi'], + handler, +}; + +async function handler() { const { data: response } = await got('https://svc.eleduck.com/api/v1/posts', { searchParams: { category: 5, @@ -31,9 +53,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '招聘 | 电鸭社区', link: 'https://eleduck.com/category/5', item: out, - }); -}; + }; +} diff --git a/lib/routes/eleduck/posts.ts b/lib/routes/eleduck/posts.ts index 4fded878c..310ae32c6 100644 --- a/lib/routes/eleduck/posts.ts +++ b/lib/routes/eleduck/posts.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; @@ -15,7 +16,25 @@ const getCateName = async (cid = 0) => { return cates[cid] || '全部'; }; -export default async (ctx) => { +export const route: Route = { + path: '/posts/:id?', + categories: ['blog'], + example: '/eleduck/posts/4', + parameters: { id: '分类id,可以论坛的URL找到,默认为全部' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '分类文章', + maintainers: ['running-grass'], + handler, +}; + +async function handler(ctx) { const cid = ctx.req.param('id') || 0; const response = await got(`https://svc.eleduck.com/api/v1/posts?category=${cid}&sort=-published_at&page=1`); @@ -27,7 +46,7 @@ export default async (ctx) => { const cname = await getCateName(cid); - ctx.set('data', { + return { title: `电鸭社区的文章--${cname}`, link: `https://eleduck.com/categories/${cid}`, description: `电鸭社区的文章,栏目为${cname}`, @@ -38,5 +57,5 @@ export default async (ctx) => { link: `https://eleduck.com/${item.category.id === 22 ? 'tposts' : 'posts'}/${item.id}`, author: item.user.nickname, })), - }); -}; + }; +} diff --git a/lib/routes/elsevier/issue.ts b/lib/routes/elsevier/issue.ts index 802192e91..1f9a88315 100644 --- a/lib/routes/elsevier/issue.ts +++ b/lib/routes/elsevier/issue.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 { art } from '@/utils/render'; import { CookieJar } from 'tough-cookie'; const cookieJar = new CookieJar(); -export default async (ctx) => { +export const route: Route = { + path: ['/:journal/vol/:issue', '/:journal/:issue'], + radar: { + source: ['www.sciencedirect.com/journal/:journal/*'], + target: '/:journal', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const journal = ctx.req.param('journal'); const issue = 'Volume ' + ctx.req.param('issue').replace('-', ' Issue '); const host = 'https://www.sciencedirect.com'; @@ -58,9 +70,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${jrnlName} - ${issue}`, link: issueUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/elsevier/journal.ts b/lib/routes/elsevier/journal.ts index b7a9b9353..e5f8ddd5b 100644 --- a/lib/routes/elsevier/journal.ts +++ b/lib/routes/elsevier/journal.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 { art } from '@/utils/render'; import { CookieJar } from 'tough-cookie'; const cookieJar = new CookieJar(); -export default async (ctx) => { +export const route: Route = { + path: ['/:journal/latest', '/:journal'], + radar: { + source: ['www.sciencedirect.com/journal/:journal/*'], + target: '/:journal', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const journal = ctx.req.param('journal'); const host = 'https://www.sciencedirect.com'; const jrnlUrl = `${host}/journal/${journal}`; @@ -68,9 +80,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: jrnlName, link: jrnlUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/embassy/index.ts b/lib/routes/embassy/index.ts index 280e36ecb..8c504f845 100644 --- a/lib/routes/embassy/index.ts +++ b/lib/routes/embassy/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'; import supportedList from './supported-list'; -export default async (ctx) => { +export const route: Route = { + path: '/:country/:city?', + name: 'Unknown', + maintainers: ['HenryQW'], + handler, +}; + +async function handler(ctx) { const country = ctx.req.param('country'); const city = ctx.req.param('city') ?? undefined; @@ -52,10 +60,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: desc, description: desc, link, item: out, - }); -}; + }; +} diff --git a/lib/routes/epicgames/index.ts b/lib/routes/epicgames/index.ts index 91a6e2c5a..913520425 100644 --- a/lib/routes/epicgames/index.ts +++ b/lib/routes/epicgames/index.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 { art } from '@/utils/render'; import * as path from 'node:path'; import dayjs from 'dayjs'; -export default async (ctx) => { +export const route: Route = { + path: '/freegames/:locale?/:country?', + categories: ['reading'], + example: '/epicgames/freegames', + parameters: { locale: 'Locale, en_US by default', country: 'Country, US by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['store.epicgames.com/:locale/free-games'], + target: '/freegames/:locale', + }, + name: 'Free games', + maintainers: ['DIYgod', 'NeverBehave', 'Zyx-A', 'junfengP', 'nczitzk', 'KotaHv'], + handler, +}; + +async function handler(ctx) { const locale = ctx.req.param('locale') ?? 'en-US'; const country = ctx.req.param('country') ?? 'US'; @@ -80,9 +103,9 @@ export default async (ctx) => { pubDate: parseDate(item.promotions.promotionalOffers[0].promotionalOffers[0].startDate), }; }); - ctx.set('data', { + return { title: 'Epic Games Store - Free Games', link: currentUrl, item: await Promise.all(items), - }); -}; + }; +} diff --git a/lib/routes/eprice/rss.ts b/lib/routes/eprice/rss.ts index 7cbf2e660..7a66ff1d1 100644 --- a/lib/routes/eprice/rss.ts +++ b/lib/routes/eprice/rss.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'; const allowRegion = new Set(['tw', 'hk']); -export default async (ctx) => { +export const route: Route = { + path: '/:region?', + categories: ['traditional-media'], + example: '/eprice/tw', + parameters: { region: '地区,预设为 tw' }, + 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') ?? 'tw'; if (!allowRegion.has(region)) { throw new Error('Invalid region'); @@ -83,14 +102,14 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: feed.title, link: feed.link, description: feed.description, item: items, image: feed.image.url, language: feed.language, - }); + }; ctx.set('json', { title: feed.title, @@ -100,4 +119,4 @@ export default async (ctx) => { image: feed.image.url, language: feed.language, }); -}; +} diff --git a/lib/routes/eventernote/actors.ts b/lib/routes/eventernote/actors.ts index d130b8c74..f9d5bfb20 100644 --- a/lib/routes/eventernote/actors.ts +++ b/lib/routes/eventernote/actors.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -11,7 +12,28 @@ const timeStringRegexes = [ const maxPages = 5; const pageCount = 10; -export default async (ctx) => { +export const route: Route = { + path: '/actors/:name/:id', + categories: ['program-update'], + example: '/eventernote/actors/三森すずこ/2634', + parameters: { name: '声优姓名', id: '声优 ID' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.eventernote.com/actors/:name/:id/events'], + }, + name: '声优活动及演唱会', + maintainers: ['KTachibanaM'], + handler, +}; + +async function handler(ctx) { const { name, id } = ctx.req.param(); const title = `${name}のイベント・ライブ情報一覧`; @@ -90,12 +112,12 @@ export default async (ctx) => { page += 1; } - ctx.set('data', { + return { title, link, description: title, language: 'ja', allowEmpty: true, item: events, - }); -}; + }; +} diff --git a/lib/routes/f-droid/apprelease.ts b/lib/routes/f-droid/apprelease.ts index 65a4c36f9..84053fd3c 100644 --- a/lib/routes/f-droid/apprelease.ts +++ b/lib/routes/f-droid/apprelease.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import * as cheerio from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/apprelease/:app', + categories: ['university'], + example: '/f-droid/apprelease/com.termux', + parameters: { app: "App's package name" }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['f-droid.org/en/packages/:app/'], + }, + name: 'App Update', + maintainers: ['garywill'], + handler, +}; + +async function handler(ctx) { const { app } = ctx.req.param(); const { data: response } = await got(`https://f-droid.org/en/packages/${app}/`); const $ = cheerio.load(response); @@ -24,10 +46,10 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `${appName} releases on F-Droid`, discription: $('.package-summary').text(), link: `https://f-droid.org/en/packages/${app}/`, item: items, - }); -}; + }; +} diff --git a/lib/routes/famitsu/category.ts b/lib/routes/famitsu/category.ts index df93baf04..ecd785d68 100644 --- a/lib/routes/famitsu/category.ts +++ b/lib/routes/famitsu/category.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://www.famitsu.com'; -export default async (ctx) => { +export const route: Route = { + path: '/category/:category?', + categories: ['reading'], + example: '/famitsu/category/new-article', + parameters: { category: 'Category, see table below, `new-article` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Category', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { category = 'new-article' } = ctx.req.param(); const url = `${baseUrl}/search/?category=${category}`; const { data } = await got(url); @@ -52,12 +71,12 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), description: $('head meta[name="description"]').attr('content'), image: 'https://www.famitsu.com/img/1812/favicons/apple-touch-icon.png', link: url, item: items, language: 'ja', - }); -}; + }; +} diff --git a/lib/routes/fansly/post.ts b/lib/routes/fansly/post.ts index f5013ed06..6ba33d9e1 100644 --- a/lib/routes/fansly/post.ts +++ b/lib/routes/fansly/post.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { parseDate } from '@/utils/parse-date'; import { getAccountByUsername, getTimelineByAccountId, parseDescription, baseUrl } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/user/:username', + categories: ['new-media'], + example: '/fansly/user/AeriGoMoo', + parameters: { username: 'User ID' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['fansly.com/:username/posts', 'fansly.com/:username/media'], + }, + name: 'User Timeline', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const username = ctx.req.param('username'); const account = await getAccountByUsername(username, cache.tryGet); @@ -16,7 +38,7 @@ export default async (ctx) => { author: `${account.displayName ?? account.username} (@${account.username})`, })); - ctx.set('data', { + return { title: `${account.displayName ?? account.username} (@${account.username}) - Fansly`, link: `${baseUrl}/${account.username}`, description: account.about.replaceAll('\n', ' '), @@ -26,5 +48,5 @@ export default async (ctx) => { language: 'en', allowEmpty: true, item: items, - }); -}; + }; +} diff --git a/lib/routes/fansly/tag.ts b/lib/routes/fansly/tag.ts index 01ebe481e..d18ad5f01 100644 --- a/lib/routes/fansly/tag.ts +++ b/lib/routes/fansly/tag.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { parseDate } from '@/utils/parse-date'; import { getTagId, getTagSuggestion, findAccountById, parseDescription, baseUrl, icon } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/tag/:tag', + categories: ['new-media'], + example: '/fansly/tag/free', + parameters: { tag: 'Hashtag' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['fansly.com/explore/tag/:tag'], + }, + name: 'Hashtag', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const tag = ctx.req.param('tag'); const tagId = await getTagId(tag, cache.tryGet); @@ -19,7 +41,7 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `#${tag} - Fansly`, link: `${baseUrl}/explore/tag/${tag}`, image: icon, @@ -27,5 +49,5 @@ export default async (ctx) => { logo: icon, language: 'en', item: items, - }); -}; + }; +} diff --git a/lib/routes/fantia/search.ts b/lib/routes/fantia/search.ts index 267cd0a6a..35b0f6795 100644 --- a/lib/routes/fantia/search.ts +++ b/lib/routes/fantia/search.ts @@ -1,8 +1,34 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/search/:type?/:caty?/:period?/:order?/:rating?/:keyword?', + categories: ['anime'], + example: '/fantia/search/posts/all/daily', + parameters: { + type: 'Type, see the table below, `posts` by default', + caty: 'Category, see the table below, can also be found in search page URL, `すべてのクリエイター` by default', + period: 'Ranking period, see the table below, empty by default', + order: 'Sorting, see the table below, `更新の新しい順` by default', + rating: 'Rating, see the table below, `すべて` by default', + keyword: 'Keyword, empty by default', + }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Search', + maintainers: [], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') || 'posts'; const caty = ctx.req.param('caty') || ''; const order = ctx.req.param('order') || 'updater'; @@ -62,9 +88,9 @@ export default async (ctx) => { break; } - ctx.set('data', { + return { title: `Fantia - Search ${type}`, link: apiUrl.replace('api/v1/search/', ''), item: items, - }); -}; + }; +} diff --git a/lib/routes/fantia/user.ts b/lib/routes/fantia/user.ts index 6f893457d..bfee47577 100644 --- a/lib/routes/fantia/user.ts +++ b/lib/routes/fantia/user.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 { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/user/:id', + categories: ['anime'], + example: '/fantia/user/3498', + parameters: { id: 'User id, can be found in user profile URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['fantia.jp/fanclubs/:id'], + }, + name: 'User Posts', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const rootUrl = 'https://fantia.jp'; const userUrl = `${rootUrl}/api/v1/fanclubs/${ctx.req.param('id')}`; @@ -53,9 +75,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `Fantia - ${response.data.fanclub.fanclub_name_with_creator_name}`, link: `${rootUrl}/fanclubs/${ctx.req.param('id')}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/farmatters/index.ts b/lib/routes/farmatters/index.ts index bbfc137b4..4489ba358 100644 --- a/lib/routes/farmatters/index.ts +++ b/lib/routes/farmatters/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -18,7 +19,29 @@ const ids = { 3: 1, }; -export default async (ctx) => { +export const route: Route = { + path: ['/exclusive/:locale?', '/news/:locale?', '/:locale?', '/:type/:id/:locale?'], + categories: ['traditional-media'], + example: '/farmatters/exclusive', + parameters: { locale: 'Locale, `zh-CN` or `en-US`, `zh-CN` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['farmatters.com/exclusive'], + target: '/exclusive', + }, + name: 'Exclusive', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const { type, id, locale = 'zh-CN' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 50; @@ -74,7 +97,7 @@ export default async (ctx) => { const subtitle = `${$('h4').first().text()}${type === 'wiki' ? ` - ${$('div.css-6f6728 div.MuiBox-root').eq(ids[id]).text()}` : ''}`; const icon = new URL('favicon.ico', rootUrl).href; - ctx.set('data', { + return { item: items, title: `${$('title').text().split(/-/)[0].trim()} - ${subtitle}`, link: currentUrl, @@ -85,5 +108,5 @@ export default async (ctx) => { logo: icon, subtitle, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/fastbull/express-news.ts b/lib/routes/fastbull/express-news.ts index 53712ace9..cd3b60f6c 100644 --- a/lib/routes/fastbull/express-news.ts +++ b/lib/routes/fastbull/express-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: '/express-news', + categories: ['other'], + example: '/fastbull/express-news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['fastbull.cn/express-news', 'fastbull.cn/'], + }, + name: '快讯', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://www.fastbull.cn'; const currentUrl = `${rootUrl}/express-news`; @@ -25,9 +47,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: '实时财经快讯 - FastBull', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/fastbull/news.ts b/lib/routes/fastbull/news.ts index d6cd79ffa..8639cef05 100644 --- a/lib/routes/fastbull/news.ts +++ b/lib/routes/fastbull/news.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: ['/news', '/'], + categories: ['other'], + example: '/fastbull/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['fastbull.cn/news', 'fastbull.cn/'], + }, + name: '新闻', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://www.fastbull.cn'; const currentUrl = `${rootUrl}/news`; @@ -53,9 +75,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '财经头条、财经新闻、最新资讯 - FastBull', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/fda/cdrh.ts b/lib/routes/fda/cdrh.ts index 247e4533d..448be451e 100644 --- a/lib/routes/fda/cdrh.ts +++ b/lib/routes/fda/cdrh.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: '/cdrh/:titleOnly?', + radar: { + source: ['fda.gov/medical-devices/news-events-medical-devices/cdrhnew-news-and-updates', 'fda.gov/'], + target: '/cdrh/:titleOnly', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const titleOnly = !!(ctx.req.param('titleOnly') ?? ''); const rootUrl = 'https://www.fda.gov'; const currentUrl = `${rootUrl}/medical-devices/news-events-medical-devices/cdrhnew-news-and-updates`; @@ -55,9 +67,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/feng/forum.ts b/lib/routes/feng/forum.ts index 68945509e..0a0682000 100644 --- a/lib/routes/feng/forum.ts +++ b/lib/routes/feng/forum.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 { baseUrl, getForumMeta, getThreads, getThread } from './utils'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/forum/:id/:type?', + categories: ['blog'], + example: '/feng/forum/1', + parameters: { id: '版块 ID,可在版块 URL 找到', type: '排序,见下表,默认为 `all`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['feng.com/forum/photo/:id', 'feng.com/forum/:id'], + target: '/forum/:id', + }, + name: '社区', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const topicId = Number(ctx.req.param('id')); const { type = 'all' } = ctx.req.param(); @@ -36,10 +59,10 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `${topicMeta.topicName} - 社区 - 威锋 - 千万果粉大本营`, description: topicMeta.topicDescription, link: `${baseUrl}/forum/${topicId}`, item: posts, - }); -}; + }; +} diff --git a/lib/routes/ff14/ff14-global.ts b/lib/routes/ff14/ff14-global.ts index 48e8f305a..75e72f076 100644 --- a/lib/routes/ff14/ff14-global.ts +++ b/lib/routes/ff14/ff14-global.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 { isValidHost } from '@/utils/valid-host'; -export default async (ctx) => { +export const route: Route = { + path: ['/global/:lang/:type?', '/ff14_global/:lang/:type?'], + categories: ['reading'], + example: '/ff14/global/na/all', + parameters: { lang: 'Region', type: 'Category, `all` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'FINAL FANTASY XIV (The Lodestone)', + maintainers: ['chengyuhui'], + handler, +}; + +async function handler(ctx) { const lang = ctx.req.param('lang'); const type = ctx.req.param('type') ?? 'all'; @@ -30,7 +49,7 @@ export default async (ctx) => { data = response.data; } - ctx.set('data', { + return { title: `FFXIV Lodestone updates (${type})`, link: `https://${lang}.finalfantasyxiv.com/lodestone/news/`, item: data.map(({ id, url, title, time, description, image }) => ({ @@ -43,5 +62,5 @@ export default async (ctx) => { pubDate: parseDate(time), guid: id, })), - }); -}; + }; +} diff --git a/lib/routes/ff14/ff14-zh.ts b/lib/routes/ff14/ff14-zh.ts index fe3c7d131..f210e9391 100644 --- a/lib/routes/ff14/ff14-zh.ts +++ b/lib/routes/ff14/ff14-zh.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: ['/zh/:type?', '/ff14_zh/:type?'], + categories: ['reading'], + example: '/ff14/zh/news', + parameters: { type: '分类名,预设为 `all`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ff.web.sdo.com/web8/index.html'], + target: '/zh', + }, + name: '最终幻想 14 国服', + maintainers: ['Kiotlin', 'ZeroClad', '15x15G'], + handler, +}; + +async function handler(ctx) { const referer = 'https://ff.sdo.com/web8/index.html'; const type = ctx.req.param('type') ?? 'all'; @@ -29,7 +52,7 @@ export default async (ctx) => { const data = response.data.Data; - ctx.set('data', { + return { title: '最终幻想14(国服)新闻中心', link: referer + '#/newstab/newslist', description: '《最终幻想14》是史克威尔艾尼克斯出品的全球经典游戏品牌FINAL FANTASY系列的最新作品,IGN获得9.2高分!全球累计用户突破1600万!', @@ -42,5 +65,5 @@ export default async (ctx) => { }), pubDate: timezone(parseDate(PublishDate), +8), })), - }); -}; + }; +} diff --git a/lib/routes/fffdm/manhua/manhua.ts b/lib/routes/fffdm/manhua/manhua.ts index 19a3829ea..358f6d936 100644 --- a/lib/routes/fffdm/manhua/manhua.ts +++ b/lib/routes/fffdm/manhua/manhua.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -20,7 +21,29 @@ const get_pic = async (url) => { }; }; -export default async (ctx) => { +export const route: Route = { + path: '/manhua/:id/:cdn?', + categories: ['program-update'], + example: '/fffdm/manhua/93', + parameters: { id: '漫画ID。默认获取全部,建议使用通用参数limit获取指定数量', cdn: 'cdn加速器。默认5,当前可选1-5' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.fffdm.com/manhua/:id', 'www.fffdm.com/:id'], + target: '/manhua/:id', + }, + name: '在线漫画', + maintainers: ['zytomorrow'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const count = ctx.req.query('limit') || 99999; const cdnNum = ctx.req.param('cdn') || 5; @@ -45,10 +68,10 @@ export default async (ctx) => { }); }) ); - ctx.set('data', { + return { title: '风之动漫 - ' + chapter_detail[0].comicTitle, link: `${host}/${id}`, description: '风之动漫', item: chapter_detail, - }); -}; + }; +} diff --git a/lib/routes/finology/bullets.ts b/lib/routes/finology/bullets.ts index 17f363c21..56ebf5ac4 100644 --- a/lib/routes/finology/bullets.ts +++ b/lib/routes/finology/bullets.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: '/bullets', + categories: ['other'], + example: '/finology/bullets', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['insider.finology.in/bullets'], + }, + name: 'Bullets', + maintainers: ['Rjnishant530'], + handler, +}; + +async function handler() { const baseUrl = 'https://insider.finology.in/bullets'; const { data: response } = await got(baseUrl); @@ -23,7 +45,7 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: 'Finology Insider Bullets', link: baseUrl, item: listItems, @@ -31,5 +53,5 @@ export default async (ctx) => { logo: 'https://assets.finology.in/insider/images/favicon/apple-touch-icon.png', icon: 'https://assets.finology.in/insider/images/favicon/favicon-32x32.png', language: 'en-us', - }); -}; + }; +} diff --git a/lib/routes/finology/most-viewed.ts b/lib/routes/finology/most-viewed.ts index caf5aa85b..58b55368b 100644 --- a/lib/routes/finology/most-viewed.ts +++ b/lib/routes/finology/most-viewed.ts @@ -1,7 +1,30 @@ +import { Route } from '@/types'; import logger from '@/utils/logger'; import { getItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/most-viewed/:time', + categories: ['other'], + example: '/finology/most-viewed/monthly', + parameters: { time: 'N' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['insider.finology.in/:category'], + target: '/:category', + }, + name: 'Most Viewed', + maintainers: ['Rjnishant530'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://insider.finology.in/most-viewed'; let selector; let title; @@ -21,7 +44,7 @@ export default async (ctx) => { selector, }; const listItems = await getItems(ctx, baseUrl, extra); - ctx.set('data', { + return { title: `Most Viewed ${title} - Finology Insider`, link: baseUrl, item: listItems, @@ -29,5 +52,5 @@ export default async (ctx) => { logo: 'https://assets.finology.in/insider/images/favicon/apple-touch-icon.png', icon: 'https://assets.finology.in/insider/images/favicon/favicon-32x32.png', language: 'en-us', - }); -}; + }; +} diff --git a/lib/routes/finology/tag.ts b/lib/routes/finology/tag.ts index 7bc2c72ef..9cdc6730f 100644 --- a/lib/routes/finology/tag.ts +++ b/lib/routes/finology/tag.ts @@ -1,7 +1,29 @@ +import { Route } from '@/types'; import logger from '@/utils/logger'; import { getItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: ['/:category', '/tag/:topic'], + categories: ['other'], + example: '/finology/success-stories', + parameters: { category: 'N' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['insider.finology.in/:category'], + }, + name: 'Category', + maintainers: ['Rjnishant530'], + handler, +}; + +async function handler(ctx) { const { topic, category } = ctx.req.param(); const baseUrl = 'https://insider.finology.in'; let route; @@ -21,7 +43,7 @@ export default async (ctx) => { selector: `div.w100.pb${number}.bg-color.flex.flex-col.align-center div.w23.br0625.shadow.position-r.bg-white.m-w100.card.t-w45`, }; const listItems = await getItems(ctx, `${baseUrl}${route}`, extra); - ctx.set('data', { + return { title: `${extra.topicName} - Finology Insider`, link: `${baseUrl}${route}`, item: listItems, @@ -29,5 +51,5 @@ export default async (ctx) => { logo: 'https://assets.finology.in/insider/images/favicon/apple-touch-icon.png', icon: 'https://assets.finology.in/insider/images/favicon/favicon-32x32.png', language: 'en-us', - }); -}; + }; +} diff --git a/lib/routes/finviz/news.ts b/lib/routes/finviz/news.ts index 19ad70183..0dd0c6dc8 100644 --- a/lib/routes/finviz/news.ts +++ b/lib/routes/finviz/news.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; @@ -8,7 +9,28 @@ const categories = { blogs: 1, }; -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + categories: ['other'], + example: '/finviz', + parameters: { category: 'Category, see below, News by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['finviz.com/news.ashx', 'finviz.com/'], + }, + name: 'News', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const { category = 'News' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 200; @@ -55,7 +77,7 @@ export default async (ctx) => { const icon = $('link[rel="icon"]').prop('href'); - ctx.set('data', { + return { item: items, title: `finviz - ${category}`, link: currentUrl, @@ -65,5 +87,5 @@ export default async (ctx) => { icon, logo: icon, subtitle: $('title').text(), - }); -}; + }; +} diff --git a/lib/routes/finviz/quote.ts b/lib/routes/finviz/quote.ts index 7d18552d8..074c9e688 100644 --- a/lib/routes/finviz/quote.ts +++ b/lib/routes/finviz/quote.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: '/news/:ticker', + categories: ['other'], + example: '/finviz/news/AAPL', + parameters: { ticker: 'The stock ticker' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'US Stock News', + maintainers: ['HenryQW'], + handler, +}; + +async function handler(ctx) { const link = `https://finviz.com/quote.ashx?t=${ctx.req.param('ticker')}`; const response = await got(link); @@ -31,10 +50,10 @@ export default async (ctx) => { const name = $('.fullview-title b').text(); - ctx.set('data', { + return { title: `${ctx.req.param('ticker')} ${name} News by Finviz`, link, description: `A collection of ${name} news aggregated by Finviz.`, item, - }); -}; + }; +} diff --git a/lib/routes/firecore/index.ts b/lib/routes/firecore/index.ts index 47ecfe863..19508b304 100644 --- a/lib/routes/firecore/index.ts +++ b/lib/routes/firecore/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: '/:os', + categories: ['university'], + example: '/firecore/ios', + parameters: { os: '`ios`,`tvos`,`macos`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Release Notes', + maintainers: ['NathanDai'], + handler, +}; + +async function handler(ctx) { const host = 'https://firecore.com/releases'; const { data } = await got(host); const $ = load(data); @@ -29,9 +48,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `Infuse Release Notes (${ctx.req.param('os')})`, link: 'https://firecore.com/releases', item: items, - }); -}; + }; +} diff --git a/lib/routes/firefox/addons.ts b/lib/routes/firefox/addons.ts index 497e0fa15..31ad25705 100644 --- a/lib/routes/firefox/addons.ts +++ b/lib/routes/firefox/addons.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: '/addons/:id', + categories: ['university'], + example: '/firefox/addons/rsshub-radar', + parameters: { id: 'Add-ons id, can be found in add-ons url' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['addons.mozilla.org/:lang/firefox/addon/:id/versions', 'addons.mozilla.org/:lang/firefox/addon/:id'], + }, + name: 'Add-ons Update', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const response = await got({ @@ -12,7 +34,7 @@ export default async (ctx) => { const info = data.addons.byID[data.addons.bySlug[id]]; const versionIds = data.versions.bySlug[id].versionIds; - ctx.set('data', { + return { title: `${info.name} 附加组件更新 - Firefox`, description: info.summary || info.description, link: `https://addons.mozilla.org/zh-CN/firefox/addon/${id}/versions/`, @@ -31,5 +53,5 @@ export default async (ctx) => { category: info.categories, }; }), - }); -}; + }; +} diff --git a/lib/routes/firefox/breaches.ts b/lib/routes/firefox/breaches.ts index 6c174c7d9..6a40be35d 100644 --- a/lib/routes/firefox/breaches.ts +++ b/lib/routes/firefox/breaches.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: '/breaches', + categories: ['other'], + example: '/firefox/breaches', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['monitor.firefox.com/', 'monitor.firefox.com/breaches'], + }, + name: 'Firefox Monitor', + maintainers: ['TonyRL'], + handler, +}; + +async function handler() { const baseUrl = 'https://monitor.firefox.com'; const response = await got(`${baseUrl}/breaches`); @@ -28,11 +50,11 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('title').text(), description: $('head meta[name=description]').attr('content').trim(), link: response.url, item: items, image: $('head meta[property=og:image]').attr('content'), - }); -}; + }; +} diff --git a/lib/routes/firefox/release.ts b/lib/routes/firefox/release.ts index f8ecc5411..fdc05936a 100644 --- a/lib/routes/firefox/release.ts +++ b/lib/routes/firefox/release.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; @@ -10,7 +11,14 @@ const platformSlugs = { ios: 'ios/notes', }; -export default async (ctx) => { +export const route: Route = { + path: '/release/:platform?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { platform = 'desktop' } = ctx.req.param(); const devicePlatform = platform.replace('-', '/'); @@ -20,7 +28,7 @@ export default async (ctx) => { const version = $('.c-release-version').text(); const pubDate = parseDate($('.c-release-date').text(), 'MMMM D, YYYY'); - ctx.set('data', { + return { title: `Firefox ${platform} release notes`, link, item: [ @@ -32,5 +40,5 @@ export default async (ctx) => { pubDate, }, ], - }); -}; + }; +} diff --git a/lib/routes/fisher-spb/news.ts b/lib/routes/fisher-spb/news.ts index d97e4bfe8..815bcdb84 100644 --- a/lib/routes/fisher-spb/news.ts +++ b/lib/routes/fisher-spb/news.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 { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/news', + categories: ['other'], + example: '/fisher-spb/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['fisher.spb.ru/news'], + }, + name: 'News', + maintainers: ['denis-ya'], + handler, +}; + +async function handler() { const renderVideo = (link) => art(path.join(__dirname, './templates/video.art'), { link }); const renderImage = (href) => art(path.join(__dirname, './templates/image.art'), { href }); @@ -43,9 +65,9 @@ export default async (ctx) => { })) .get(); - ctx.set('data', { + return { title: $('head > title').text().trim(), link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/fishshell/index.ts b/lib/routes/fishshell/index.ts index 9fab39c5d..bf40413fa 100644 --- a/lib/routes/fishshell/index.ts +++ b/lib/routes/fishshell/index.ts @@ -1,14 +1,26 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { load } from 'cheerio'; import got from '@/utils/got'; import { config } from '@/config'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['fishshell.com/'], + target: '', + }, + name: 'Unknown', + maintainers: ['x2cf'], + handler, +}; + +async function handler() { const link = 'https://fishshell.com/docs/current/relnotes.html'; const data = await cache.tryGet(link, async () => (await got(link)).data, config.cache.contentExpire, false); const $ = load(data); - ctx.set('data', { + return { link, title: 'Release notes — fish-shell', language: 'en', @@ -24,5 +36,5 @@ export default async (ctx) => { }; }) .get(), - }); -}; + }; +} diff --git a/lib/routes/fjksbm/index.ts b/lib/routes/fjksbm/index.ts index ae56e418f..b3be6eb36 100644 --- a/lib/routes/fjksbm/index.ts +++ b/lib/routes/fjksbm/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'; import timezone from '@/utils/timezone'; -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + categories: ['journal'], + example: '/fjksbm', + parameters: { category: '分类,见下表,默认为网络报名进行中' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['fjksbm.com/portal/:category?', 'fjksbm.com/portal'], + }, + name: '分类', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? '0'; const id = Number.parseInt(category); @@ -44,11 +66,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('.panel-heading') .eq(isNumber ? id : 1) .text()} - 福建考试报名网`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/flyert/creditcard.ts b/lib/routes/flyert/creditcard.ts index db18888c9..0a783dac2 100644 --- a/lib/routes/flyert/creditcard.ts +++ b/lib/routes/flyert/creditcard.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 iconv from 'iconv-lite'; const gbk2utf8 = (s) => iconv.decode(s, 'gbk'); const host = 'https://www.flyert.com'; -export default async (ctx) => { +export const route: Route = { + path: '/creditcard/:bank', + categories: ['shopping'], + example: '/flyert/creditcard/zhongxin', + parameters: { bank: '信用卡板块各银行的拼音简称' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['flyert.com/'], + }, + name: '信用卡', + maintainers: ['nicolaszf'], + handler, +}; + +async function handler(ctx) { const bank = ctx.req.param('bank'); const target = `${host}/forum-${bank}-1.html`; let bankname = ''; @@ -81,10 +103,10 @@ export default async (ctx) => { const result = await util.ProcessFeed(list, cache); - ctx.set('data', { + return { title: `飞客茶馆信用卡 - ${bankname}`, link: 'https://www.flyert.com/', description: `飞客茶馆信用卡 - ${bankname}`, item: result, - }); -}; + }; +} diff --git a/lib/routes/flyert/preferential.ts b/lib/routes/flyert/preferential.ts index eb0bf845f..cffc2a516 100644 --- a/lib/routes/flyert/preferential.ts +++ b/lib/routes/flyert/preferential.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -9,7 +10,28 @@ const gbk2utf8 = (s) => iconv.decode(s, 'gbk'); const host = 'https://www.flyert.com'; const target = `${host}/forum.php?mod=forumdisplay&sum=all&fid=all&catid=322`; -export default async (ctx) => { +export const route: Route = { + path: '/preferential', + categories: ['shopping'], + example: '/flyert/preferential', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['flyert.com/'], + }, + name: '优惠信息', + maintainers: ['howel52'], + handler, +}; + +async function handler() { const response = await got(target, { responseType: 'buffer', }); @@ -40,10 +62,10 @@ export default async (ctx) => { }) ) ); - ctx.set('data', { + return { title: '飞客茶馆优惠', link: 'https://www.flyert.com/', description: '飞客茶馆优惠', item: items, - }); -}; + }; +} diff --git a/lib/routes/focustaiwan/index.ts b/lib/routes/focustaiwan/index.ts index 9d17a139c..8f085917a 100644 --- a/lib/routes/focustaiwan/index.ts +++ b/lib/routes/focustaiwan/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 { art } from '@/utils/render'; 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: '/focustaiwan', + parameters: { category: '分类,见下表,默认为 news' }, + 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 rootUrl = 'https://focustaiwan.tw'; @@ -66,11 +85,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: response.data.ResultData.MetaData.Title, link: response.data.ResultData.MetaData.CanonicalUrl, item: items, itunes_author: 'Focus Taiwan', image: 'https://imgcdn.cna.com.tw/Eng/website/img/default.png', - }); -}; + }; +} diff --git a/lib/routes/followin/index.ts b/lib/routes/followin/index.ts index ab7f60735..c5aa3166d 100644 --- a/lib/routes/followin/index.ts +++ b/lib/routes/followin/index.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { apiUrl, favicon, getBParam, getBuildId, getGToken, parseList, parseItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/:categoryId?/:lang?', + categories: ['other'], + example: '/followin', + parameters: { categoryId: 'Category ID, see table below, `1` by default', lang: 'Language, see table below, `en` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Home', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { categoryId = '1', lang = 'en' } = ctx.req.param(); const { limit = 20 } = ctx.req.query(); const gToken = await getGToken(cache.tryGet); @@ -27,10 +46,10 @@ export default async (ctx) => { const list = parseList(response.data.list, lang, buildId); const items = await Promise.all(list.map((item) => parseItem(item, cache.tryGet))); - ctx.set('data', { + return { title: 'Followin', link: 'https://followin.io', image: favicon, item: items, - }); -}; + }; +} diff --git a/lib/routes/followin/kol.ts b/lib/routes/followin/kol.ts index 16b490376..5b060fe4b 100644 --- a/lib/routes/followin/kol.ts +++ b/lib/routes/followin/kol.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { baseUrl, getBuildId, parseList, parseItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/kol/:kolId/:lang?', + categories: ['other'], + example: '/followin/kol/4075592991', + parameters: { kolId: 'KOL ID, can be found in URL', lang: 'Language, see table above, `en` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['followin.io/:lang/kol/:kolId', 'followin.io/kol/:kolId'], + }, + name: 'KOL', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { kolId, lang = 'en' } = ctx.req.param(); const { limit = 10 } = ctx.req.query(); @@ -15,12 +37,12 @@ export default async (ctx) => { const list = parseList(queries.find((q) => q.queryKey[0] === '/feed/list/user').state.data.pages[0].list.slice(0, limit), lang, buildId); const items = await Promise.all(list.map((item) => parseItem(item, cache.tryGet))); - ctx.set('data', { + return { title: `${profile.nickname} - Followin`, description: profile.bio, link: `${baseUrl}/${lang}/kol/${kolId}`, image: profile.avatar, language: lang, item: items, - }); -}; + }; +} diff --git a/lib/routes/followin/news.ts b/lib/routes/followin/news.ts index a4130a6e2..f01170de0 100644 --- a/lib/routes/followin/news.ts +++ b/lib/routes/followin/news.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { baseUrl, favicon, getBuildId, parseList, parseItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/news/:lang?', + categories: ['other'], + example: '/followin/news', + parameters: { lang: 'Language, see table above, `en` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['followin.io/:lang?/news', 'followin.io/news'], + }, + name: 'News', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { lang = 'en' } = ctx.req.param(); const { limit = 20 } = ctx.req.query(); @@ -12,11 +34,11 @@ export default async (ctx) => { const list = parseList(response.pageProps.dehydratedState.queries.find((q) => q.queryKey[0] === '/feed/list/recommended/news').state.data.pages[0].list.slice(0, limit), lang, buildId); const items = await Promise.all(list.map((item) => parseItem(item, cache.tryGet))); - ctx.set('data', { + return { title: `${lang === 'en' ? 'News' : lang === 'vi' ? 'Bản tin' : '快讯'} - Followin`, link: `${baseUrl}/${lang}/news`, image: favicon, language: lang, item: items, - }); -}; + }; +} diff --git a/lib/routes/followin/tag.ts b/lib/routes/followin/tag.ts index 0d9f33f1c..69f604108 100644 --- a/lib/routes/followin/tag.ts +++ b/lib/routes/followin/tag.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { apiUrl, baseUrl, getBParam, getBuildId, getGToken, parseList, parseItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/tag/:tagId/:lang?', + categories: ['other'], + example: '/followin/tag/177008', + parameters: { tagId: 'Tag ID, can be found in URL', lang: 'Language, see table above, `en` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['followin.io/:lang/tag/:tagId', 'followin.io/tag/:tagId'], + }, + name: 'Tag', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { tagId, lang = 'en' } = ctx.req.param(); const { limit = 20 } = ctx.req.query(); @@ -34,12 +56,12 @@ export default async (ctx) => { const list = parseList(tagResponse.data.list.slice(0, limit), lang, buildId); const items = await Promise.all(list.map((item) => parseItem(item, cache.tryGet))); - ctx.set('data', { + return { title: `${tagInfo.name} - Followin`, description: tagInfo.description, link: `${baseUrl}/${lang}/tag/${tagId}`, image: tagInfo.logo, language: lang, item: items, - }); -}; + }; +} diff --git a/lib/routes/followin/topic.ts b/lib/routes/followin/topic.ts index 34e1444d1..826ba1c4e 100644 --- a/lib/routes/followin/topic.ts +++ b/lib/routes/followin/topic.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { baseUrl, getBuildId, parseList, parseItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/topic/:topicId/:lang?', + categories: ['other'], + example: '/followin/topic/40', + parameters: { topicId: 'Topic ID, can be found in URL', lang: 'Language, see table above, `en` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['followin.io/:lang/topic/:topicId', 'followin.io/topic/:topicId'], + }, + name: 'Topic', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { topicId, lang = 'en' } = ctx.req.param(); const { limit = 20 } = ctx.req.query(); @@ -15,12 +37,12 @@ export default async (ctx) => { const list = parseList(queries.find((q) => q.queryKey[0] === '/feed/list/topic').state.data.pages[0].list.slice(0, limit), lang, buildId); const items = await Promise.all(list.map((item) => parseItem(item, cache.tryGet))); - ctx.set('data', { + return { title: `${topicInfo.title} - Followin`, description: topicInfo.desc, link: `${baseUrl}/${lang}/topic/${topicId}`, image: topicInfo.logo, language: lang, item: items, - }); -}; + }; +} diff --git a/lib/routes/foresightnews/article.ts b/lib/routes/foresightnews/article.ts index 4f166d3da..a6e9c5046 100644 --- a/lib/routes/foresightnews/article.ts +++ b/lib/routes/foresightnews/article.ts @@ -1,13 +1,35 @@ +import { Route } from '@/types'; import { rootUrl, apiRootUrl, processItems, icon, image } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/article', + categories: ['traditional-media'], + example: '/foresightnews/article', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['foresightnews.pro/'], + }, + name: '文章', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 50; const apiUrl = new URL('v1/articles', apiRootUrl).href; const { items } = await processItems(apiUrl, limit); - ctx.set('data', { + return { item: items, title: 'Foresight News - 文章', link: rootUrl, @@ -18,5 +40,5 @@ export default async (ctx) => { logo: icon, subtitle: '文章', author: 'Foresight News', - }); -}; + }; +} diff --git a/lib/routes/foresightnews/column.ts b/lib/routes/foresightnews/column.ts index b68d37a76..800b31911 100644 --- a/lib/routes/foresightnews/column.ts +++ b/lib/routes/foresightnews/column.ts @@ -1,6 +1,28 @@ +import { Route } from '@/types'; import { rootUrl, apiRootUrl, processItems, icon, image } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/column/:id', + categories: ['traditional-media'], + example: '/foresightnews/column/1', + parameters: { id: '专栏 id, 可在对应专栏页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['foresightnews.pro/column/detail/:id', 'foresightnews.pro/'], + }, + 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) : 50; @@ -13,7 +35,7 @@ export default async (ctx) => { const column = info.column; - ctx.set('data', { + return { item: items, title: `Foresight News - ${column}`, link: currentUrl, @@ -24,5 +46,5 @@ export default async (ctx) => { logo: icon, subtitle: column, author: 'Foresight News', - }); -}; + }; +} diff --git a/lib/routes/foresightnews/index.ts b/lib/routes/foresightnews/index.ts index 32e1b12ac..6f05f5177 100644 --- a/lib/routes/foresightnews/index.ts +++ b/lib/routes/foresightnews/index.ts @@ -1,13 +1,25 @@ +import { Route } from '@/types'; import { rootUrl, apiRootUrl, processItems, icon, image } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['foresightnews.pro/'], + target: '', + }, + name: 'Unknown', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 50; const apiUrl = new URL(`v2/feed`, apiRootUrl).href; const { items } = await processItems(apiUrl, limit); - ctx.set('data', { + return { item: items, title: 'Foresight News - 精选资讯', link: rootUrl, @@ -18,5 +30,5 @@ export default async (ctx) => { logo: icon, subtitle: '精选资讯', author: 'Foresight News', - }); -}; + }; +} diff --git a/lib/routes/foresightnews/news.ts b/lib/routes/foresightnews/news.ts index 8883dff37..2fb5c689c 100644 --- a/lib/routes/foresightnews/news.ts +++ b/lib/routes/foresightnews/news.ts @@ -1,6 +1,28 @@ +import { Route } from '@/types'; import { rootUrl, apiRootUrl, processItems, icon, image } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/news', + categories: ['traditional-media'], + example: '/foresightnews/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['foresightnews.pro/news', 'foresightnews.pro/'], + }, + name: '快讯', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 50; const apiUrl = new URL('v1/news', apiRootUrl).href; @@ -8,7 +30,7 @@ export default async (ctx) => { const { items } = await processItems(apiUrl, limit); - ctx.set('data', { + return { item: items, title: 'Foresight News - 快讯', link: currentUrl, @@ -19,5 +41,5 @@ export default async (ctx) => { logo: icon, subtitle: '快讯', author: 'Foresight News', - }); -}; + }; +} diff --git a/lib/routes/foreverblog/feeds.ts b/lib/routes/foreverblog/feeds.ts index c263617e5..0b8d0f128 100644 --- a/lib/routes/foreverblog/feeds.ts +++ b/lib/routes/foreverblog/feeds.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: '/feeds', + categories: ['programming'], + example: '/foreverblog/feeds', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.foreverblog.cn/feeds.html'], + }, + name: '专题展示 - 文章', + maintainers: ['7Wate', 'a180285'], + handler, +}; + +async function handler() { const currentUrl = 'https://www.foreverblog.cn/feeds.html'; const response = await got(currentUrl); @@ -28,9 +50,9 @@ export default async (ctx) => { }) .toArray(); - ctx.set('data', { + return { title: '十年之约——专题展示', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/fortnite/news.ts b/lib/routes/fortnite/news.ts index faafda4bb..8aff2c6df 100644 --- a/lib/routes/fortnite/news.ts +++ b/lib/routes/fortnite/news.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { parseDate } from '@/utils/parse-date'; import logger from '@/utils/logger'; import puppeteer from '@/utils/puppeteer'; -export default async (ctx) => { +export const route: Route = { + path: '/news/:options?', + categories: ['reading'], + example: '/fortnite/news', + parameters: { options: 'Params' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'News', + maintainers: ['lyqluis'], + handler, +}; + +async function handler(ctx) { const options = ctx.req .param('options') ?.split('&') @@ -55,9 +74,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'Fortnite News', link, item: items, - }); -}; + }; +} diff --git a/lib/routes/fortunechina/index.ts b/lib/routes/fortunechina/index.ts index 9fb54e900..88c0cf196 100644 --- a/lib/routes/fortunechina/index.ts +++ b/lib/routes/fortunechina/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, parseRelativeDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + categories: ['traditional-media'], + example: '/fortunechina', + parameters: { category: '分类,见下表,默认为首页' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['fortunechina.com/:category', 'fortunechina.com/'], + }, + name: '分类', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? ''; const rootUrl = 'https://www.fortunechina.com'; @@ -63,9 +85,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: category ? $('title').text() : '财富中文网', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/fosshub/index.ts b/lib/routes/fosshub/index.ts index eaed17a2b..6bcb7a80c 100644 --- a/lib/routes/fosshub/index.ts +++ b/lib/routes/fosshub/index.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 { 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: ['university'], + example: '/fosshub/qBittorrent', + parameters: { id: 'Software id, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Software Update', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? ''; const rootUrl = 'https://www.fosshub.com'; @@ -44,9 +63,9 @@ export default async (ctx) => { }, ]; - ctx.set('data', { + return { title: `${$('#fh-ssd__hl').text()} - FossHub`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/free/rss.ts b/lib/routes/free/rss.ts index 5b5785a8e..eda497ee3 100644 --- a/lib/routes/free/rss.ts +++ b/lib/routes/free/rss.ts @@ -1,11 +1,23 @@ +import { Route } from '@/types'; import { parseDate } from '@/utils/parse-date'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['free.com.tw/'], + target: '', + }, + name: 'Unknown', + maintainers: ['cnkmmk'], + handler, +}; + +async function handler() { const url = 'https://free.com.tw/'; const response = await got(`${url}/wp-json/wp/v2/posts`); const list = response.data; - ctx.set('data', { + return { title: '免費資源網路社群', link: url, description: '免費資源網路社群 - 全部文章', @@ -15,5 +27,5 @@ export default async (ctx) => { pubDate: parseDate(item.date_gmt), description: item.content.rendered, })), - }); -}; + }; +} diff --git a/lib/routes/freebuf/index.ts b/lib/routes/freebuf/index.ts index d95c4db2b..18aa7135b 100644 --- a/lib/routes/freebuf/index.ts +++ b/lib/routes/freebuf/index.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: '/articles/:type', + categories: ['programming'], + example: '/freebuf/articles/web', + parameters: { type: '文章类别' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['freebuf.com/articles/:type/*.html', 'freebuf.com/articles/:type'], + }, + name: '文章', + maintainers: ['trganda'], + handler, +}; + +async function handler(ctx) { const { type = 'web' } = ctx.req.param(); const fapi = 'https://www.freebuf.com/fapi/frontend/category/list'; @@ -33,9 +55,9 @@ export default async (ctx) => { author: item.nickname, })); - ctx.set('data', { + return { title: `Freebuf ${type}`, link: rssLink, item: items, - }); -}; + }; +} diff --git a/lib/routes/freecomputerbooks/index.ts b/lib/routes/freecomputerbooks/index.ts index 39f56c3ff..a768f2089 100644 --- a/lib/routes/freecomputerbooks/index.ts +++ b/lib/routes/freecomputerbooks/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -15,7 +16,18 @@ async function cheerioLoad(url) { return load((await got(url)).data); } -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + radar: { + source: ['freecomputerbooks.com/', 'freecomputerbooks.com/index.html'], + target: '', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const categoryId = ctx.req.param('category')?.trim(); const requestURL = categoryId ? new URL(`${categoryId}.html`, baseURL).href : baseURL; const $ = await cheerioLoad(requestURL); @@ -25,7 +37,7 @@ export default async (ctx) => { // Needing more robust processing if some day more such elements show up. const categoryTitle = $('.maintitlebar').text(); - ctx.set('data', { + return { title: 'Free Computer Books - ' + categoryTitle, link: requestURL, description: $('title').text(), @@ -37,8 +49,8 @@ export default async (ctx) => { .toArray() .map((elem) => buildPostItem($(elem), categoryTitle, cache)) ), - }); -}; + }; +} function buildPostItem(listItem, categoryTitle, cache) { const $ = load(''); // the only use below doesn't care about the content diff --git a/lib/routes/freewechat/profile.ts b/lib/routes/freewechat/profile.ts index 415a562a3..1737b7d11 100644 --- a/lib/routes/freewechat/profile.ts +++ b/lib/routes/freewechat/profile.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'; import { fixArticleContent } from '@/utils/wechat-mp'; const baseUrl = 'https://freewechat.com'; -export default async (ctx) => { +export const route: Route = { + path: '/profile/:id', + categories: ['traditional-media'], + example: '/freewechat/profile/MzI5NTUxNzk3OA==', + parameters: { id: '公众号 ID,可在URL中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['freewechat.com/profile/:id'], + }, + name: 'Unknown', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const url = `${baseUrl}/profile/${id}`; const { data: response } = await got(url); @@ -55,10 +77,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), link: url, image: 'https://freewechat.com/favicon.ico', item: items, - }); -}; + }; +} diff --git a/lib/routes/ft/channel.ts b/lib/routes/ft/channel.ts index f17655b82..8b6b584c8 100644 --- a/lib/routes/ft/channel.ts +++ b/lib/routes/ft/channel.ts @@ -1,6 +1,25 @@ +import { Route } from '@/types'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/:language/:channel?', + categories: ['bbs'], + example: '/ft/chinese/hotstoryby7day', + parameters: { language: '语言,简体`chinese`,繁体`traditional`', channel: '频道,缺省为每日更新' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'FT 中文网', + maintainers: ['HenryQW', 'xyqfer'], + handler, +}; + +async function handler(ctx) { ctx.set( 'data', await utils.getData({ @@ -9,4 +28,4 @@ export default async (ctx) => { ctx, }) ); -}; +} diff --git a/lib/routes/ft/myft.ts b/lib/routes/ft/myft.ts index 782805328..69f1156e9 100644 --- a/lib/routes/ft/myft.ts +++ b/lib/routes/ft/myft.ts @@ -1,9 +1,28 @@ +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: '/myft/:key', + categories: ['bbs'], + example: '/ft/myft/rss-key', + parameters: { key: 'the last part of myFT personal RSS address' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'myFT personal RSS', + maintainers: ['HenryQW'], + handler, +}; + +async function handler(ctx) { const ProcessFeed = (content) => { // clean up the article content.find('div.o-share, aside, div.o-ads').remove(); @@ -44,10 +63,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `FT.com - myFT`, link, description: `FT.com - myFT`, item: items, - }); -}; + }; +} diff --git a/lib/routes/fuliba/latest.ts b/lib/routes/fuliba/latest.ts index 8c971b2eb..0fc5662d4 100644 --- a/lib/routes/fuliba/latest.ts +++ b/lib/routes/fuliba/latest.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: '/latest', + categories: ['traditional-media'], + example: '/fuliba/latest', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['fuliba2023.net/'], + }, + name: '最新', + maintainers: ['shinemoon'], + handler, +}; + +async function handler(ctx) { const { data: response } = await got(`https://fuliba2023.net/wp-json/wp/v2/posts`, { searchParams: { per_page: ctx.req.query('limit') ?? 100, @@ -17,9 +39,9 @@ export default async (ctx) => { author: item._embedded.author[0].name, })); - ctx.set('data', { + return { title: '福利吧', link: `https://fuliba2023.net`, item: items, - }); -}; + }; +} diff --git a/lib/routes/furstar/archive.ts b/lib/routes/furstar/archive.ts index bb2667061..3c6fd7546 100644 --- a/lib/routes/furstar/archive.ts +++ b/lib/routes/furstar/archive.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: '/archive/:lang?', + categories: ['game'], + example: '/furstar/archive/cn', + parameters: { lang: '语言, 留空为jp, 支持cn, en' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['furstar.jp/:lang/archive.php', 'furstar.jp/archive.php'], + target: '/archive/:lang', + }, + name: '已经出售的角色列表', + maintainers: ['NeverBehave'], + handler, +}; + +async function handler(ctx) { const base = utils.langBase(ctx.req.param('lang')); const url = `${base}/archive.php`; const res = await got.get(url, { @@ -12,7 +35,7 @@ export default async (ctx) => { }); const info = utils.fetchAllCharacters(res.data, base); - ctx.set('data', { + return { title: 'Furstar 已出售角色', link: 'https://furstar.jp', description: 'Furstar 已经出售或预订的角色列表', @@ -24,5 +47,5 @@ export default async (ctx) => { pubDate: parseDate(new Date().toISOString()), // No Time for now link: e.detailPage, })), - }); -}; + }; +} diff --git a/lib/routes/furstar/artists.ts b/lib/routes/furstar/artists.ts index 7da98254c..58cbcd4b9 100644 --- a/lib/routes/furstar/artists.ts +++ b/lib/routes/furstar/artists.ts @@ -1,9 +1,32 @@ +import { Route } from '@/types'; import utils from './utils'; import { load } from 'cheerio'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/artists/:lang?', + categories: ['game'], + example: '/furstar/artists/cn', + parameters: { lang: '语言, 留空为jp, 支持cn, en' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['furstar.jp/'], + target: '/artists', + }, + name: '画师列表', + maintainers: ['NeverBehave'], + handler, +}; + +async function handler(ctx) { const base = utils.langBase(ctx.req.param('lang')); const res = await got.get(base, { https: { @@ -16,7 +39,7 @@ export default async (ctx) => { .get(); artists.shift(); // the first one is "show all" - ctx.set('data', { + return { title: 'furstar 所有画家', link: 'https://furstar.jp', description: 'Furstar 所有画家列表', @@ -28,5 +51,5 @@ export default async (ctx) => { pubDate: parseDate(new Date().toISOString()), // No Time for now link: `${base}/${e.link}`, })), - }); -}; + }; +} diff --git a/lib/routes/furstar/index.ts b/lib/routes/furstar/index.ts index e1a1ae53e..e7c8887e6 100644 --- a/lib/routes/furstar/index.ts +++ b/lib/routes/furstar/index.ts @@ -1,9 +1,32 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import utils from './utils'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/characters/:lang?', + categories: ['game'], + example: '/furstar/characters/cn', + parameters: { lang: '语言, 留空为jp, 支持cn, en' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['furstar.jp/:lang', 'furstar.jp/'], + target: '/characters/:lang', + }, + name: '最新售卖角色列表', + maintainers: ['NeverBehave'], + handler, +}; + +async function handler(ctx) { const base = utils.langBase(ctx.req.param('lang')); const res = await got.get(base, { https: { @@ -18,7 +41,7 @@ export default async (ctx) => { info, }); - ctx.set('data', { + return { title: 'Furstar 最新角色', link: 'https://furstar.jp', description: 'Furstar 最近更新的角色列表', @@ -30,5 +53,5 @@ export default async (ctx) => { pubDate: parseDate(new Date().toISOString()), // No Time for now link: e.detailPage, })), - }); -}; + }; +} diff --git a/lib/routes/futunn/main.ts b/lib/routes/futunn/main.ts index d891106e0..bff65237f 100644 --- a/lib/routes/futunn/main.ts +++ b/lib/routes/futunn/main.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: ['/highlights', '/main', '/'], + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 50; const rootUrl = 'https://news.futunn.com'; @@ -63,9 +71,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'Futubull - Headlines', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/fx-markets/channel.ts b/lib/routes/fx-markets/channel.ts index 8c7170299..2e270ce29 100644 --- a/lib/routes/fx-markets/channel.ts +++ b/lib/routes/fx-markets/channel.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: '/:channel', + categories: ['other'], + example: '/fx-markets/trading', + parameters: { channel: 'channel, can be found in the navi bar links at the home page' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Channel', + maintainers: [], + handler, +}; + +async function handler(ctx) { const channel = ctx.req.param('channel'); const link = `https://www.fx-markets.com/${channel}`; const html = (await got(link)).data; @@ -49,9 +68,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title, link, item: result, - }); -}; + }; +} diff --git a/lib/routes/fx678/kx.ts b/lib/routes/fx678/kx.ts index d0686b64f..9adb93862 100644 --- a/lib/routes/fx678/kx.ts +++ b/lib/routes/fx678/kx.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: '/kx', + categories: ['other'], + example: '/fx678/kx', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['fx678.com/kx'], + }, + name: '7x24 小时快讯', + maintainers: ['occupy5', 'dousha'], + handler, +}; + +async function handler() { const link = 'https://www.fx678.com/kx/'; const res = await got.get(link); const $ = load(res.data); @@ -37,9 +59,9 @@ export default async (ctx) => { }) ) ); - ctx.set('data', { + return { title: '7x24小时快讯', link, item: out, - }); -}; + }; +} diff --git a/lib/routes/fxiaoke/crm.ts b/lib/routes/fxiaoke/crm.ts index bd3d99c68..652257c96 100644 --- a/lib/routes/fxiaoke/crm.ts +++ b/lib/routes/fxiaoke/crm.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,25 @@ const titleMap = new Map([ ['customers', `签约喜报 - ${baseTitle}`], ]); -export default async (ctx) => { +export const route: Route = { + path: '/crm/:type', + categories: ['programming'], + example: '/fxiaoke/crm/news', + parameters: { type: '文章类型, 见下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '文章', + maintainers: ['akynazh'], + handler, +}; + +async function handler(ctx) { const t = ctx.req.param('type'); const title = titleMap.get(t); const url = `${baseUrl}/${t}/`; @@ -55,10 +74,10 @@ export default async (ctx) => { }) ) ); - ctx.set('data', { + return { title, link: url, description: desc, item: items, - }); -}; + }; +} diff --git a/lib/routes/fzmtr/announcements.ts b/lib/routes/fzmtr/announcements.ts index bbf28d3ce..701af8c24 100644 --- a/lib/routes/fzmtr/announcements.ts +++ b/lib/routes/fzmtr/announcements.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: '/announcements', + categories: ['shopping'], + example: '/fzmtr/announcements', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '通知公告', + maintainers: ['HankChow'], + handler, +}; + +async function handler() { const domain = 'www.fzmtr.com'; const announcementsUrl = `http://${domain}/html/fzdt/tzgg/index.html`; const response = await got(announcementsUrl); @@ -24,7 +43,7 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title: '福州地铁通知公告', url: announcementsUrl, description: '福州地铁通知公告', @@ -34,5 +53,5 @@ export default async (ctx) => { link: item.link, author: item.author, })), - }); -}; + }; +} diff --git a/lib/routes/gameapps/index.ts b/lib/routes/gameapps/index.ts index a705e20ab..4892c8bb8 100644 --- a/lib/routes/gameapps/index.ts +++ b/lib/routes/gameapps/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: ['gameapps.hk/'], + target: '', + }, + name: 'Unknown', + maintainers: ['TonyRL'], + handler, +}; + +async function handler() { const baseUrl = 'https://www.gameapps.hk'; const feed = await parser.parseURL(`${baseUrl}/rss`); @@ -68,12 +80,12 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: feed.title, link: feed.link, description: feed.description, image: `${baseUrl}/static/favicon/apple-touch-icon.png`, item: items, language: feed.language, - }); -}; + }; +} diff --git a/lib/routes/gamebase/news.ts b/lib/routes/gamebase/news.ts index 3b97aedd1..ce7bf7474 100644 --- a/lib/routes/gamebase/news.ts +++ b/lib/routes/gamebase/news.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -9,7 +10,25 @@ const types = { r18list: 'newsPornList', }; -export default async (ctx) => { +export const route: Route = { + path: '/news/:type?/:category?', + categories: ['reading'], + example: '/gamebase/news', + parameters: { type: '类型,见下表,默认为 newslist', category: '分类,可在对应分类页 URL 中找到,默认为 `all` 即全部' }, + 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') ?? 'newslist'; const category = ctx.req.param('category') ?? 'all'; const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 20; @@ -67,9 +86,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gamegene/news.ts b/lib/routes/gamegene/news.ts index ff2171233..9fef1ccee 100644 --- a/lib/routes/gamegene/news.ts +++ b/lib/routes/gamegene/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: '/gamegene/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['news.gamegene.cn/news'], + }, + name: '资讯', + maintainers: ['lone1y-51'], + handler, +}; + +async function handler() { const url = 'https://gamegene.cn/news'; const { data: response } = await got({ method: 'get', @@ -42,10 +64,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { // 在此处输出您的 RSS item: items, link: url, title: '游戏基因 GameGene', - }); -}; + }; +} diff --git a/lib/routes/gamer/ani/anime.ts b/lib/routes/gamer/ani/anime.ts index 91e401189..d9d3ca257 100644 --- a/lib/routes/gamer/ani/anime.ts +++ b/lib/routes/gamer/ani/anime.ts @@ -1,6 +1,25 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/ani/anime/:sn', + categories: ['program-update'], + example: '/gamer/ani/anime/36868', + parameters: { sn: '動畫 sn,在 URL 可以找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '動畫瘋 - 動畫', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { sn } = ctx.req.param(); const { data: response } = await got('https://api.gamer.com.tw/mobile_app/anime/v3/video.php', { @@ -24,10 +43,10 @@ export default async (ctx) => { })) .toReversed(); - ctx.set('data', { + return { title, link: `https://ani.gamer.com.tw/animeRef.php?sn=${anime.anime_sn}`, description: anime.content?.trim(), item: items, - }); -}; + }; +} diff --git a/lib/routes/gamer/ani/new-anime.ts b/lib/routes/gamer/ani/new-anime.ts index fbe451ca1..be5958e36 100644 --- a/lib/routes/gamer/ani/new-anime.ts +++ b/lib/routes/gamer/ani/new-anime.ts @@ -1,8 +1,31 @@ +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: '/ani/new_anime', + categories: ['program-update'], + example: '/gamer/ani/new_anime', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ani.gamer.com.tw/'], + target: '/new_anime', + }, + name: '動畫瘋 - 最後更新', + maintainers: [], + handler, +}; + +async function handler() { const rootUrl = 'https://ani.gamer.com.tw'; const { data: response } = await got('https://api.gamer.com.tw/mobile_app/anime/v3/index.php'); @@ -13,9 +36,9 @@ export default async (ctx) => { pubDate: timezone(parseDate(`${item.upTime} ${item.upTimeHours}`, 'MM/DD HH:mm'), +8), })); - ctx.set('data', { + return { title: '動畫瘋最後更新', link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gamer/gnn-index.ts b/lib/routes/gamer/gnn-index.ts index fafa2fa59..396fb2024 100644 --- a/lib/routes/gamer/gnn-index.ts +++ b/lib/routes/gamer/gnn-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: '/gnn/:category?', + categories: ['program-update'], + example: '/gamer/gnn/1', + parameters: { category: '版块' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'GNN 新聞', + maintainers: ['Arracc'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category'); let url = ''; let categoryName = ''; @@ -119,9 +138,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: '巴哈姆特-GNN新聞' + categoryName, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/gamer/hot.ts b/lib/routes/gamer/hot.ts index 8649b6052..81814fa3f 100644 --- a/lib/routes/gamer/hot.ts +++ b/lib/routes/gamer/hot.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: '/hot/:bsn', + categories: ['program-update'], + example: '/gamer/hot/47157', + parameters: { bsn: '板块 id,在 URL 可以找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '熱門推薦', + maintainers: ['nczitzk', 'TonyRL'], + handler, +}; + +async function handler(ctx) { const rootUrl = `https://forum.gamer.com.tw/A.php?bsn=${ctx.req.param('bsn')}`; const response = await got({ url: rootUrl, @@ -46,15 +65,15 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: rootUrl, item: items, - }); + }; ctx.set('json', { title: $('title').text(), link: rootUrl, item: items, }); -}; +} diff --git a/lib/routes/gamersecret/index.ts b/lib/routes/gamersecret/index.ts index c6dae62f1..b46dbadd6 100644 --- a/lib/routes/gamersecret/index.ts +++ b/lib/routes/gamersecret/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: '/:type?/:category?', + categories: ['reading'], + example: '/gamersecret', + parameters: { type: 'Type, see below, Latest News by default', category: 'Category, see below' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gamersecret.com/:type', 'gamersecret.com/:type/:category', 'gamersecret.com/'], + }, + name: 'Category', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') ?? 'latest-news'; const category = ctx.req.param('category') ?? ''; @@ -53,9 +75,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gamme/category.ts b/lib/routes/gamme/category.ts index 6f2f6e6d2..fcca76ae4 100644 --- a/lib/routes/gamme/category.ts +++ b/lib/routes/gamme/category.ts @@ -1,10 +1,18 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import parser from '@/utils/rss-parser'; import { isValidHost } from '@/utils/valid-host'; -export default async (ctx) => { +export const route: Route = { + path: '/:domain/:category?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { domain = 'news', category } = ctx.req.param(); if (!isValidHost(domain)) { throw new Error('Invalid domain'); @@ -41,11 +49,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: feed.title, link: feed.link, image: domain === 'news' ? `${baseUrl}/blogico.ico` : `${baseUrl}/favicon.ico`, description: feed.description, item: items, - }); -}; + }; +} diff --git a/lib/routes/gamme/tag.ts b/lib/routes/gamme/tag.ts index bceca6457..106faa805 100644 --- a/lib/routes/gamme/tag.ts +++ b/lib/routes/gamme/tag.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'; import { isValidHost } from '@/utils/valid-host'; -export default async (ctx) => { +export const route: Route = { + path: '/:domain/tag/:tag', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { domain = 'news', tag } = ctx.req.param(); if (!isValidHost(domain)) { throw new Error('Invalid domain'); @@ -52,11 +60,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${tag} | ${domain === 'news' ? '宅宅新聞' : '西斯新聞'}`, description: $('meta[name=description]').attr('content'), link: pageUrl, image: domain === 'news' ? `${baseUrl}/blogico.ico` : `${baseUrl}/favicon.ico`, item: items, - }); -}; + }; +} diff --git a/lib/routes/gcores/category.ts b/lib/routes/gcores/category.ts index 63f29ab3b..03230f608 100644 --- a/lib/routes/gcores/category.ts +++ b/lib/routes/gcores/category.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: '/category/:category', + categories: ['traditional-media'], + example: '/gcores/category/news', + parameters: { category: '分类名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gcores.com/:category'], + }, + name: '分类', + maintainers: ['MoguCloud', 'StevenRCE0'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category'); const url = `https://www.gcores.com/${category}`; const res = await got({ @@ -105,12 +127,12 @@ export default async (ctx) => { }); }) ); - ctx.set('data', { + return { title: feedTitle, link: url, item: out, - }); -}; + }; +} function convertEntityToContent(entity) { const { type, data } = entity; diff --git a/lib/routes/gcores/collection.ts b/lib/routes/gcores/collection.ts index 3ab27f475..b8b410df0 100644 --- a/lib/routes/gcores/collection.ts +++ b/lib/routes/gcores/collection.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; /* refer to: ./tag.js (author: @StevenREC0) */ import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/collections/:collection', + categories: ['traditional-media'], + example: '/gcores/collections/64', + parameters: { collection: '专题id,可在专题页面的 URL 中找到,如 游戏开发设计心得分享 -- 64' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gcores.com/collections/:collection'], + }, + name: '专题文章', + maintainers: ['kudryavka1013'], + handler, +}; + +async function handler(ctx) { // get params const collection = ctx.req.param('collection'); @@ -129,9 +151,9 @@ export default async (ctx) => { }); // return data - ctx.set('data', { + return { title: feedTitle, link: feedUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gcores/radio.ts b/lib/routes/gcores/radio.ts index f71c06cc9..378c05807 100644 --- a/lib/routes/gcores/radio.ts +++ b/lib/routes/gcores/radio.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 got from '@/utils/got'; import md5 from '@/utils/md5'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/radios/:category?', + categories: ['traditional-media'], + example: '/gcores/radios/45', + parameters: { category: '分类名,默认为全部,可在分类页面的 URL 中找到,如 Gadio News -- 45' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: true, + supportScihub: false, + }, + radar: { + source: ['gcores.com/categories/:category'], + target: '/radios/:category', + }, + name: '播客', + maintainers: ['eternasuno'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') || 'all'; const limit = Number.parseInt(ctx.req.query('limit')) || 12; @@ -57,7 +80,7 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title, link, description, @@ -65,8 +88,8 @@ export default async (ctx) => { itunes_author: '机核 GCORES', image: `https://www.gcores.com/${image}`, item, - }); -}; + }; +} const get = async (url) => { const response = await got({ diff --git a/lib/routes/gcores/tag.ts b/lib/routes/gcores/tag.ts index 17730a1b8..ca0afac5c 100644 --- a/lib/routes/gcores/tag.ts +++ b/lib/routes/gcores/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/:category?', + categories: ['traditional-media'], + example: '/gcores/tag/42/articles', + parameters: { tag: '标签名,可在选定标签分类页面的 URL 中找到,如视觉动物——42', category: '分类名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gcores.com/categories/:tag', 'gcores.com/'], + target: '/tag/:tag', + }, + name: '标签', + maintainers: ['StevenRCE0'], + handler, +}; + +async function handler(ctx) { const tag = ctx.req.param('tag'); const category = ctx.req.param('category'); const url = `https://www.gcores.com/categories/${tag + (category ? `?tab=${category}` : '')}`; @@ -96,12 +119,12 @@ export default async (ctx) => { }); }) ); - ctx.set('data', { + return { title: feedTitle, link: url, item: out, - }); -}; + }; +} function convertEntityToContent(entity) { const { type, data } = entity; diff --git a/lib/routes/gdsrx/index.ts b/lib/routes/gdsrx/index.ts index caccbe9ff..4a261976f 100644 --- a/lib/routes/gdsrx/index.ts +++ b/lib/routes/gdsrx/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: '/:id?', + categories: ['other'], + example: '/gdsrx', + parameters: { id: '栏目 id,可在对应栏目页 URL 中找到,见下表,默认为法规文库' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '栏目', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { id = '10' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 15; @@ -58,7 +77,7 @@ export default async (ctx) => { .map((c) => $(c).text()) .pop(); - ctx.set('data', { + return { item: items, title: `${author} - ${subtitle}`, link: currentUrl, @@ -69,5 +88,5 @@ export default async (ctx) => { logo: icon, subtitle, author, - }); -}; + }; +} diff --git a/lib/routes/gdut/news.ts b/lib/routes/gdut/news.ts index d8fbf442a..7528fd6b1 100644 --- a/lib/routes/gdut/news.ts +++ b/lib/routes/gdut/news.ts @@ -158,7 +158,7 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return ({ title: `广东工业大学校内新闻网`, link: 'http://news.gdut.edu.cn', item: out.length > 0 ? out : articleList, diff --git a/lib/routes/gdut/oa-news.ts b/lib/routes/gdut/oa-news.ts index 094551678..3fb939d90 100644 --- a/lib/routes/gdut/oa-news.ts +++ b/lib/routes/gdut/oa-news.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import wait from '@/utils/wait'; @@ -71,7 +72,18 @@ function getArg(type) { ]); } -export default async (ctx) => { +export const route: Route = { + path: '/oa_news/:type?', + radar: { + source: ['oas.gdut.edu.cn/seeyon'], + target: '/oa_news/', + }, + name: 'Unknown', + maintainers: ['Jim Kirisame'], + handler, +}; + +async function handler(ctx) { const typeParam = ctx.req.param('type') ?? 'notice'; if (typeMap[typeParam] === undefined) { throw new Error('通知类型' + typeParam + '未定义'); @@ -202,10 +214,10 @@ export default async (ctx) => { data; } - ctx.set('data', { + return { title: `广东工业大学新闻通知网 - ` + type.name, link: site, description: `广东工业大学新闻通知网`, item: articles, - }); -}; + }; +} diff --git a/lib/routes/gelonghui/home.ts b/lib/routes/gelonghui/home.ts index 199b13923..59ec58343 100644 --- a/lib/routes/gelonghui/home.ts +++ b/lib/routes/gelonghui/home.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 { parseItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/home/:tag?', + categories: ['other'], + example: '/gelonghui/home', + parameters: { tag: '分类标签,见下表,默认为 `web_home_page`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '首页', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { tag = 'web_home_page' } = ctx.req.param(); const apiUrl = `https://www.gelonghui.com/api/channels/${tag}/articles/v8`; const { data } = await got(apiUrl); @@ -22,11 +41,11 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => parseItem(item, cache.tryGet))); - ctx.set('data', { + return { title: '格隆汇-财经资讯动态-股市行情', description: '格隆汇为中国投资者出海投资及中国公司出海融资,提供海外投资,港股开户行情,科创板股票发行数据、资讯、研究、交易等一站式服务,目前业务范围主要涉及港股与美股两大市场,未来将陆续开通台湾、日本、印度、欧洲等市场.', image: 'https://cdn.gelonghui.com/static/web/www.ico.la.ico', link: 'https://www.gelonghui.com', item: items, - }); -}; + }; +} diff --git a/lib/routes/gelonghui/hot-article.ts b/lib/routes/gelonghui/hot-article.ts index e8e822d7b..ce754043f 100644 --- a/lib/routes/gelonghui/hot-article.ts +++ b/lib/routes/gelonghui/hot-article.ts @@ -1,9 +1,32 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/hot-article/:type?', + categories: ['other'], + example: '/gelonghui/hot-article', + parameters: { type: '`day` 为日排行,`week` 为周排行,默认为 `day`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gelonghui.com/'], + target: '/hot-article', + }, + name: '最热文章', + maintainers: [], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') === 'week' ? 1 : 0; const baseUrl = `https://www.gelonghui.com`; const { data: response } = await got(baseUrl); @@ -24,11 +47,11 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => parseItem(item, cache.tryGet))); - ctx.set('data', { + return { title: `最热文章 - ${type === 0 ? '日排行' : '周排行'} - 格隆汇`, description: '格隆汇为中国投资者出海投资及中国公司出海融资,提供海外投资,港股开户行情,科创板股票发行数据、资讯、研究、交易等一站式服务,目前业务范围主要涉及港股与美股两大市场,未来将陆续开通台湾、日本、印度、欧洲等市场.', image: 'https://cdn.gelonghui.com/static/web/www.ico.la.ico', link: baseUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gelonghui/keyword.ts b/lib/routes/gelonghui/keyword.ts index 5d034f83c..4efe4ced9 100644 --- a/lib/routes/gelonghui/keyword.ts +++ b/lib/routes/gelonghui/keyword.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 { parseItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/keyword/:keyword', + categories: ['other'], + example: '/gelonghui/keyword/早报', + parameters: { keyword: '搜索关键字' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '搜索关键字', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const keyword = ctx.req.param('keyword'); const currentUrl = `https://www.gelonghui.com/api/post/search/v4`; const { data } = await got(currentUrl, { @@ -26,10 +45,10 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => parseItem(item, cache.tryGet))); - ctx.set('data', { + return { title: `格隆汇 - 关键词 “${keyword}” 的文章`, link: currentUrl, item: items, description: `找到关于 “ ${keyword} ”的文章,共${data.totalCount}个结果`, - }); -}; + }; +} diff --git a/lib/routes/gelonghui/live.ts b/lib/routes/gelonghui/live.ts index 8593f70f7..6dd3ce639 100644 --- a/lib/routes/gelonghui/live.ts +++ b/lib/routes/gelonghui/live.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 baseUrl = 'https://www.gelonghui.com'; -export default async (ctx) => { +export const route: Route = { + path: '/live', + categories: ['other'], + example: '/gelonghui/live', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gelonghui.com/live', 'gelonghui.com/'], + }, + name: '实时快讯', + maintainers: [], + handler, +}; + +async function handler() { const apiUrl = `${baseUrl}/api/live-channels/all/lives/v4`; const { data: { result }, @@ -24,11 +46,11 @@ export default async (ctx) => { pubDate: parseDate(i.createTimestamp, 'X'), })); - ctx.set('data', { + return { title: '格隆汇快讯-7x24小时市场快讯-财经市场热点', description: '格隆汇快讯栏目提供外汇投资实时行情,外汇投资交易,外汇投资炒股,证券等内容,实时更新,格隆汇未来将陆续开通台湾、日本、印度、欧洲等市场.', image: 'https://cdn.gelonghui.com/static/web/www.ico.la.ico', link: `${baseUrl}/live`, item: items, - }); -}; + }; +} diff --git a/lib/routes/gelonghui/subject.ts b/lib/routes/gelonghui/subject.ts index 094181ce8..12a91d4e3 100644 --- a/lib/routes/gelonghui/subject.ts +++ b/lib/routes/gelonghui/subject.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 { parseItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/subject/:id', + categories: ['other'], + example: '/gelonghui/subject/4', + parameters: { id: '主题编号,可在主题页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gelonghui.com/subject/:id'], + }, + name: '主题文章', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const apiUrl = `https://www.gelonghui.com/api/subjects/${id}/contents`; const pageUrl = `https://www.gelonghui.com/subject/${id}`; @@ -27,11 +49,11 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => parseItem(item, cache.tryGet))); - ctx.set('data', { + return { title: `格隆汇 - 主题 ${$('span.user-nick').text()} 的文章`, description: $('div.user-name').parent().children('p').text(), image: $('.subject-list-title').find('img').attr('data-src'), link: `https://www.gelonghui.com/subject/${id}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/gelonghui/user.ts b/lib/routes/gelonghui/user.ts index 116263d20..b99033e2d 100644 --- a/lib/routes/gelonghui/user.ts +++ b/lib/routes/gelonghui/user.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 { parseItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/user/:id', + categories: ['other'], + example: '/gelonghui/user/5273', + parameters: { id: '用户编号,可在用户页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gelonghui.com/user/:id'], + }, + name: '用户文章', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const { id: userId } = ctx.req.param(); const currentUrl = 'https://www.gelonghui.com/api/community/dynamic/my-dynamics/v2'; const { data } = await got(currentUrl, { @@ -24,11 +46,11 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => parseItem(item, cache.tryGet))); - ctx.set('data', { + return { title: `格隆汇 - 用户 ${data.result[0].user.nick} 的文章`, description: data.result.find((i) => i.user).user.brief, image: data.result.find((i) => i.user).user.avatar.split('@')[0], link: data.result.find((i) => i.user).user.route.replace('https://m.gelonghui.com', 'https://www.gelonghui.com'), item: items, - }); -}; + }; +} diff --git a/lib/routes/geocaching/blogs.ts b/lib/routes/geocaching/blogs.ts index 10341fe28..8c0806698 100644 --- a/lib/routes/geocaching/blogs.ts +++ b/lib/routes/geocaching/blogs.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: '/blogs', + categories: ['programming'], + example: '/geocaching/blogs', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['geocaching.com/blog/', 'geocaching.com/'], + }, + name: 'Official Blogs', + maintainers: ['HankChow'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://www.geocaching.com'; const { data: response } = await got(`${baseUrl}/blog/wp-json/wp/v2/posts`, { searchParams: { @@ -20,11 +42,11 @@ export default async (ctx) => { category: item._embedded['wp:term'][0].map((category) => category.name), })); - ctx.set('data', { + return { title: 'Geocaching Blog', link: `${baseUrl}/blog/`, image: `${baseUrl}/blog/favicon.ico`, description: 'Geocaching 博客更新', item: items, - }); -}; + }; +} diff --git a/lib/routes/getdr/index.ts b/lib/routes/getdr/index.ts index 4f1ebdba8..98d42b145 100644 --- a/lib/routes/getdr/index.ts +++ b/lib/routes/getdr/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: ['getdr.com/'], + target: '', + }, + name: 'Unknown', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://getdr.com/最新詐騙情報'; const apiUrl = 'https://public-api.wordpress.com/rest/v1.1/sites/demogetdr.wordpress.com/posts'; @@ -40,9 +52,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '最新詐騙情報 - 趨勢科技防詐達人', link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/getitfree/index.ts b/lib/routes/getitfree/index.ts index a56beea4f..08443ab3f 100644 --- a/lib/routes/getitfree/index.ts +++ b/lib/routes/getitfree/index.ts @@ -1,10 +1,18 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import { apiSlug, rootUrl, bakeFilterSearchParams, bakeFiltersWithPair, bakeUrl, fetchData, getFilterNameForTitle, getFilterParamsForUrl, parseFilterStr } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/:filter{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const filter = ctx.req.param('filter'); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 50; @@ -43,9 +51,9 @@ export default async (ctx) => { const subtitle = getFilterNameForTitle(filtersWithPair); - ctx.set('data', { + return { ...(await fetchData(currentUrl)), item: items, title: `Getitfree${subtitle ? ` | ${subtitle}` : ''}`, - }); -}; + }; +} diff --git a/lib/routes/gettr/user.ts b/lib/routes/gettr/user.ts index c812fcb73..826ac26d9 100644 --- a/lib/routes/gettr/user.ts +++ b/lib/routes/gettr/user.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 actionMap = { shares_pst: 'Shared a post: ', }; -export default async (ctx) => { +export const route: Route = { + path: '/user/:id', + categories: ['new-media'], + example: '/gettr/user/jasonmillerindc', + parameters: { id: 'User id' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gettr.com/user/:id'], + }, + name: 'User timeline', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://gettr.com'; const apiHost = 'https://api.gettr.com'; const mediaHost = 'https://media.gettr.com'; @@ -48,12 +70,12 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `${userInfo.nickname} on Gettr`, description: userInfo.dsc, link: `${baseUrl}/user/${id}`, image: `${mediaHost}/${userInfo.ico}`, language: 'en', item: items, - }); -}; + }; +} diff --git a/lib/routes/gf-cn/news.ts b/lib/routes/gf-cn/news.ts index e20a395af..733f620e6 100644 --- a/lib/routes/gf-cn/news.ts +++ b/lib/routes/gf-cn/news.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import timezone from '@/utils/timezone'; @@ -8,7 +9,28 @@ const titles = { 3: '公告', }; -export default async (ctx) => { +export const route: Route = { + path: '/news/:category?', + categories: ['reading'], + example: '/gf-cn/news', + parameters: { category: '分类,见下表,默认为新闻' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['sunborngame.com/:category', 'sunborngame.com/'], + }, + name: '情报局', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? '1'; const rootUrl = 'https://gf-cn.sunborngame.com'; @@ -43,9 +65,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${titles[category]} - 少女前线 - 情报局`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gihyo/group.ts b/lib/routes/gihyo/group.ts index deffdfb80..3c2567b9e 100644 --- a/lib/routes/gihyo/group.ts +++ b/lib/routes/gihyo/group.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: '/list/group/:id', + categories: ['design'], + example: '/gihyo/list/group/Ubuntu-Weekly-Recipe', + parameters: { id: 'Series' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gihyo.jp/list/group/:id'], + }, + name: 'Series', + maintainers: ['masakichi'], + handler, +}; + +async function handler(ctx) { const groupId = ctx.req.param('id'); const baseUrl = 'https://gihyo.jp'; @@ -38,11 +60,11 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title, link, description, language, item, - }); -}; + }; +} diff --git a/lib/routes/gitee/repos/commits.ts b/lib/routes/gitee/repos/commits.ts index eb271c042..be4f96f4b 100644 --- a/lib/routes/gitee/repos/commits.ts +++ b/lib/routes/gitee/repos/commits.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'; @@ -7,7 +8,28 @@ const md = MarkdownIt({ html: true, }); -export default async (ctx) => { +export const route: Route = { + path: '/commits/:owner/:repo', + categories: ['design'], + example: '/gitee/commits/y_project/RuoYi', + parameters: { owner: '用户名', repo: '仓库名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gitee.com/:owner/:repo/commits'], + }, + name: '仓库提交', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { owner, repo } = ctx.req.param(); const apiUrl = `https://gitee.com/api/v5/repos/${owner}/${repo}/commits`; @@ -34,9 +56,9 @@ export default async (ctx) => { link: item.html_url, })); - ctx.set('data', { + return { title: `${owner}/${repo} - 提交`, link: `https://gitee.com/${owner}/${repo}/commits`, item: items, - }); -}; + }; +} diff --git a/lib/routes/gitee/repos/events.ts b/lib/routes/gitee/repos/events.ts index 36f6905e4..4753c8300 100644 --- a/lib/routes/gitee/repos/events.ts +++ b/lib/routes/gitee/repos/events.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'; @@ -7,7 +8,28 @@ const md = MarkdownIt({ html: true, }); -export default async (ctx) => { +export const route: Route = { + path: '/events/:owner/:repo', + categories: ['design'], + example: '/gitee/events/y_project/RuoYi', + parameters: { owner: '用户名', repo: '仓库名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gitee.com/:owner/:repo'], + }, + name: '仓库动态', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { owner, repo } = ctx.req.param(); const apiUrl = `https://gitee.com/api/v5/repos/${owner}/${repo}/events`; @@ -60,9 +82,9 @@ export default async (ctx) => { return item; }); - ctx.set('data', { + return { title: `${owner}/${repo} - 仓库动态`, link: `https://gitee.com/${owner}/${repo}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/gitee/repos/releases.ts b/lib/routes/gitee/repos/releases.ts index f38db10e5..c436233e8 100644 --- a/lib/routes/gitee/repos/releases.ts +++ b/lib/routes/gitee/repos/releases.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { config } from '@/config'; @@ -6,7 +7,28 @@ const md = MarkdownIt({ html: true, }); -export default async (ctx) => { +export const route: Route = { + path: '/releases/:owner/:repo', + categories: ['design'], + example: '/gitee/releases/y_project/RuoYi', + parameters: { owner: '用户名', repo: '仓库名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gitee.com/:owner/:repo/releases'], + }, + name: '仓库 Releases', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { owner, repo } = ctx.req.param(); const response = await got(`https://gitee.com/api/v5/repos/${owner}/${repo}/releases`, { @@ -26,9 +48,9 @@ export default async (ctx) => { link: `https://gitee.com/${owner}/${repo}/releases/${item.tag_name}`, })); - ctx.set('data', { + return { title: `${owner}/${repo} - 发行版`, link: `https://gitee.com/${owner}/${repo}/releases`, item: items, - }); -}; + }; +} diff --git a/lib/routes/gitee/users/events.ts b/lib/routes/gitee/users/events.ts index 1c590330b..133eb023d 100644 --- a/lib/routes/gitee/users/events.ts +++ b/lib/routes/gitee/users/events.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'; @@ -7,7 +8,28 @@ const md = MarkdownIt({ html: true, }); -export default async (ctx) => { +export const route: Route = { + path: '/events/:username', + categories: ['design'], + example: '/gitee/events/y_project', + parameters: { username: '用户名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gitee.com/:username'], + }, + name: '用户公开动态', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const username = ctx.req.param('username'); const apiUrl = `https://gitee.com/api/v5/users/${username}/events/public`; @@ -78,9 +100,9 @@ export default async (ctx) => { return item; }); - ctx.set('data', { + return { title: `${username} - 公开动态`, link: `https://gitee.com/${username}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/github/branches.ts b/lib/routes/github/branches.ts index bac3c769c..f7c25256b 100644 --- a/lib/routes/github/branches.ts +++ b/lib/routes/github/branches.ts @@ -1,7 +1,29 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/branches/:user/:repo', + categories: ['design'], + example: '/github/branches/DIYgod/RSSHub', + parameters: { user: 'User name', repo: 'Repo name' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['github.com/:user/:repo/branches', 'github.com/:user/:repo'], + }, + name: 'Repo Branches', + maintainers: ['max-arnold'], + handler, +}; + +async function handler(ctx) { const user = ctx.req.param('user'); const repo = ctx.req.param('repo'); @@ -19,7 +41,7 @@ export default async (ctx) => { }); const data = response.data; - ctx.set('data', { + return { title: `${user}/${repo} Branches`, link: `${host}/branches/all`, item: data.map((item) => ({ @@ -27,5 +49,5 @@ export default async (ctx) => { description: item.name, link: `${host}/commits/${item.name}`, })), - }); -}; + }; +} diff --git a/lib/routes/github/comments.ts b/lib/routes/github/comments.ts index c2e7b61f2..4c074ca4d 100644 --- a/lib/routes/github/comments.ts +++ b/lib/routes/github/comments.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'; @@ -19,7 +20,18 @@ const typeDict = { }, }; -export default async (ctx) => { +export const route: Route = { + path: ['/comments/:user/:repo/:type/:number', '/comments/:user/:repo/:number?'], + radar: { + source: ['github.com/:user/:repo/:type'], + target: '/comments/:user/:repo', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const user = ctx.req.param('user'); const repo = ctx.req.param('repo'); const number = ctx.req.param('number') && isNaN(Number.parseInt(ctx.req.param('number'))) ? 1 : Number.parseInt(ctx.req.param('number')); @@ -35,7 +47,7 @@ export default async (ctx) => { }; await (isNaN(number) ? allIssues(ctx, user, repo, limit, headers) : singleIssue(ctx, user, repo, number, limit, headers)); -}; +} async function allIssues(ctx, user, repo, limit, headers) { const response = await got(`${apiUrl}/repos/${user}/${repo}/issues/comments`, { @@ -73,11 +85,11 @@ async function allIssues(ctx, user, repo, limit, headers) { used: Number.parseInt(response.headers['x-ratelimit-used']), }; - ctx.set('data', { + return { title: `${user}/${repo}: Issue & Pull request comments`, link: `${rootUrl}/${user}/${repo}`, item: items, - }); + }; ctx.set('json', { title: `${user}/${repo}: Issue & Pull request comments`, @@ -165,11 +177,11 @@ async function singleIssue(ctx, user, repo, number, limit, headers) { } } - ctx.set('data', { + return { title: `${user}/${repo}: ${typeDict[type].title} #${number} - ${issue.title}`, link: issue.html_url, item: items, - }); + }; ctx.set('json', { title: `${user}/${repo}: ${typeDict[type].title} #${number} - ${issue.title}`, diff --git a/lib/routes/github/contributors.ts b/lib/routes/github/contributors.ts index 21769dd9f..059f97249 100644 --- a/lib/routes/github/contributors.ts +++ b/lib/routes/github/contributors.ts @@ -1,7 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/contributors/:user/:repo/:order?/:anon?', + categories: ['design'], + example: '/github/contributors/DIYgod/RSSHub', + parameters: { user: 'User name', repo: 'Repo name', order: 'Sort order by commit numbers, desc and asc (descending by default)', anon: 'Show anonymous users. Defaults to no, use any values for yes.' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['github.com/:user/:repo/graphs/contributors', 'github.com/:user/:repo'], + target: '/contributors/:user/:repo', + }, + name: 'Repo Contributors', + maintainers: ['zoenglinghou'], + handler, +}; + +async function handler(ctx) { const { user, repo, order, anon } = ctx.req.param(); const host = `https://github.com/${user}/${repo}`; @@ -70,10 +93,10 @@ export default async (ctx) => { } ); - ctx.set('data', { + return { title: `${user}/${repo} Contributors`, link: `${host}/graphs/contributors`, description: `New contributors for ${user}/${repo}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/github/file.ts b/lib/routes/github/file.ts index 11846cacf..101b683a3 100644 --- a/lib/routes/github/file.ts +++ b/lib/routes/github/file.ts @@ -1,8 +1,20 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { config } from '@/config'; import queryString from 'query-string'; -export default async (ctx) => { +export const route: Route = { + path: '/file/:user/:repo/:branch/:filepath{.+}', + radar: { + source: ['github.com/:user/:repo/blob/:branch/*filepath'], + target: '/file/:user/:repo/:branch/:filepath', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const user = ctx.req.param('user'); const repo = ctx.req.param('repo'); const branch = ctx.req.param('branch'); @@ -37,9 +49,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `GitHub File - ${user}/${repo}/${branch}/${filepath}`, link: fileUrl, item: resultItems, - }); -}; + }; +} diff --git a/lib/routes/github/follower.ts b/lib/routes/github/follower.ts index c411f1891..d2d69bc8a 100644 --- a/lib/routes/github/follower.ts +++ b/lib/routes/github/follower.ts @@ -1,7 +1,29 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/user/followers/:user', + categories: ['design'], + example: '/github/user/followers/HenryQW', + parameters: { user: 'GitHub username' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['github.com/:user'], + }, + name: 'User Followers', + maintainers: ['HenryQW'], + handler, +}; + +async function handler(ctx) { if (!config.github || !config.github.access_token) { throw new Error('GitHub follower RSS is disabled due to the lack of relevant config'); } @@ -36,7 +58,7 @@ export default async (ctx) => { const data = response.data.data.user.followers.edges; - ctx.set('data', { + return { allowEmpty: true, title: `${user}'s followers`, link: host, @@ -46,5 +68,5 @@ export default async (ctx) => { description: `${follower.node.login}
    `, link: `https://github.com/${follower.node.login}`, })), - }); -}; + }; +} diff --git a/lib/routes/github/gist.ts b/lib/routes/github/gist.ts index 478aaa43e..99d918c53 100644 --- a/lib/routes/github/gist.ts +++ b/lib/routes/github/gist.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { config } from '@/config'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/gist/:gistId', + categories: ['design'], + example: '/github/gist/d2c152bb7179d07015f336b1a0582679', + parameters: { gistId: 'Gist ID' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gist.github.com/:owner/:gistId/revisions', 'gist.github.com/:owner/:gistId/stargazers', 'gist.github.com/:owner/:gistId/forks', 'gist.github.com/:owner/:gistId'], + }, + name: 'Gist Commits', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const gistId = ctx.req.param('gistId'); const headers = { Accept: 'application/vnd.github.v3+json' }; @@ -24,12 +46,12 @@ export default async (ctx) => { pubDate: parseDate(item.committed_at), // e.g. 2022-09-02T11:09:56Z })); - ctx.set('data', { + return { allowEmpty: true, title: `${response.owner.login} / ${Object.values(response.files)[0].filename}`, description: response.description, image: response.owner.avatar_url, link: `${response.html_url}/revisions`, item: items, - }); -}; + }; +} diff --git a/lib/routes/github/issue.ts b/lib/routes/github/issue.ts index 9820d9e8a..069d54141 100644 --- a/lib/routes/github/issue.ts +++ b/lib/routes/github/issue.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { config } from '@/config'; import MarkdownIt from 'markdown-it'; @@ -8,7 +9,29 @@ const md = MarkdownIt({ import queryString from 'query-string'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/issue/:user/:repo/:state?/:labels?', + categories: ['design'], + example: '/github/issue/vuejs/core/all/wontfix', + parameters: { user: 'GitHub username', repo: 'GitHub repo name', state: 'the state of the issues. Can be either `open`, `closed`, or `all`. Default: `open`.', labels: 'a list of comma separated label names' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['github.com/:user/:repo/issues', 'github.com/:user/:repo/issues/:id', 'github.com/:user/:repo'], + target: '/issue/:user/:repo', + }, + name: 'Repo Issues', + maintainers: ['HenryQW', 'AndreyMZ'], + handler, +}; + +async function handler(ctx) { const user = ctx.req.param('user'); const repo = ctx.req.param('repo'); const state = ctx.req.param('state'); @@ -36,7 +59,7 @@ export default async (ctx) => { }); const data = response.data; - ctx.set('data', { + return { allowEmpty: true, title: `${user}/${repo}${state ? ' ' + state.replace(/^\S/, (s) => s.toUpperCase()) : ''} Issues${labels ? ' - ' + labels : ''}`, link: host, @@ -49,5 +72,5 @@ export default async (ctx) => { author: item.user.login, link: `${host}/${item.number}`, })), - }); -}; + }; +} diff --git a/lib/routes/github/notifications.ts b/lib/routes/github/notifications.ts index faab6e724..4820da983 100644 --- a/lib/routes/github/notifications.ts +++ b/lib/routes/github/notifications.ts @@ -1,10 +1,32 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; const apiUrl = 'https://api.github.com'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/notifications', + categories: ['design'], + example: '/github/notifications', + parameters: {}, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['github.com/notifications'], + }, + name: 'Notifications', + maintainers: ['zhzy0077'], + handler, +}; + +async function handler(ctx) { if (!config.github || !config.github.access_token) { throw new Error('GitHub trending RSS is disabled due to the lack of relevant config'); } @@ -33,11 +55,11 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: 'Github Notifications', link: 'https://github.com/notifications', item: items, - }); + }; ctx.set('json', { title: 'Github Notifications', @@ -50,4 +72,4 @@ export default async (ctx) => { used: Number.parseInt(response.headers['X-RateLimit-Used']), }, }); -}; +} diff --git a/lib/routes/github/pulls.ts b/lib/routes/github/pulls.ts index 06c5930af..f85accffa 100644 --- a/lib/routes/github/pulls.ts +++ b/lib/routes/github/pulls.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { config } from '@/config'; import MarkdownIt from 'markdown-it'; @@ -7,7 +8,29 @@ const md = MarkdownIt({ }); import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/pull/:user/:repo/:state?/:labels?', + categories: ['design'], + example: '/github/pull/DIYgod/RSSHub', + parameters: { user: 'User name', repo: 'Repo name', state: 'the state of pull requests. Can be either `open`, `closed`, or `all`. Default: `open`.', labels: 'a list of comma separated label names' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['github.com/:user/:repo/pulls', 'github.com/:user/:repo/pulls/:id', 'github.com/:user/:repo'], + target: '/pull/:user/:repo', + }, + name: 'Repo Pull Requests', + maintainers: [], + handler, +}; + +async function handler(ctx) { const user = ctx.req.param('user'); const repo = ctx.req.param('repo'); const state = ctx.req.param('state') ?? 'open'; @@ -32,7 +55,7 @@ export default async (ctx) => { }); const data = response.data.filter((item) => item.pull_request); - ctx.set('data', { + return { allowEmpty: true, title: `${user}/${repo} Pull requests`, link: host, @@ -43,5 +66,5 @@ export default async (ctx) => { pubDate: parseDate(item.created_at), link: item.html_url, })), - }); -}; + }; +} diff --git a/lib/routes/github/pulse.ts b/lib/routes/github/pulse.ts index 1489fbaae..c7458e4d1 100644 --- a/lib/routes/github/pulse.ts +++ b/lib/routes/github/pulse.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -8,7 +9,32 @@ import md5 from '@/utils/md5'; import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; -export default async (ctx) => { +export const route: Route = { + path: '/pulse/:user/:repo/:period?', + categories: ['design'], + example: '/github/pulse/DIYgod/RSSHub', + parameters: { + user: 'User name', + repo: 'Repo name', + period: "Time frame, selected from a repository's Pulse/Insights page. Possible values are: `daily`, `halfweekly`, `weekly`, or `monthly`. Default: `weekly`. If your RSS client supports it, consider aligning the polling frequency of the feed to the period.", + }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['github.com/:user/:repo/pulse', 'github.com/:user/:repo/pulse/:period'], + }, + name: 'Repo Pulse', + maintainers: ['jameschensmith'], + handler, +}; + +async function handler(ctx) { const { user, repo, period } = ctx.req.param(); const periods = ['daily', 'halfweekly', 'weekly', 'monthly']; @@ -71,7 +97,7 @@ export default async (ctx) => { .toArray(); } - ctx.set('data', { + return { title: `${user}/${repo} ${pulsePeriod} Pulse`, link, item: [ @@ -86,5 +112,5 @@ export default async (ctx) => { pubDate: parseDate(periodTo), }, ], - }); -}; + }; +} diff --git a/lib/routes/github/repos.ts b/lib/routes/github/repos.ts index 9f96b399f..4b5989193 100644 --- a/lib/routes/github/repos.ts +++ b/lib/routes/github/repos.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { config } from '@/config'; import queryString from 'query-string'; -export default async (ctx) => { +export const route: Route = { + path: '/repos/:user', + categories: ['design'], + example: '/github/repos/DIYgod', + parameters: { user: 'GitHub username' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['github.com/:user'], + }, + name: 'User Repo', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { const user = ctx.req.param('user'); const headers = {}; @@ -18,7 +40,7 @@ export default async (ctx) => { headers, }); const data = response.data; - ctx.set('data', { + return { allowEmpty: true, title: `${user}'s GitHub repositories`, link: `https://github.com/${user}`, @@ -30,5 +52,5 @@ export default async (ctx) => { pubDate: new Date(item.created_at).toUTCString(), link: item.html_url, })), - }); -}; + }; +} diff --git a/lib/routes/github/search.ts b/lib/routes/github/search.ts index 154fc2fdd..3f6424665 100644 --- a/lib/routes/github/search.ts +++ b/lib/routes/github/search.ts @@ -1,10 +1,29 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import * as url from 'node:url'; const host = 'https://github.com'; -export default async (ctx) => { +export const route: Route = { + path: '/search/:query/:sort?/:order?', + categories: ['design'], + example: '/github/search/RSSHub/bestmatch/desc', + parameters: { query: 'search keyword', sort: 'Sort options (default to bestmatch)', order: 'Sort order, desc and asc (desc descending by default)' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Search Result', + maintainers: ['LogicJake'], + handler, +}; + +async function handler(ctx) { const query = ctx.req.param('query'); let sort = ctx.req.param('sort') || 'bestmatch'; const order = ctx.req.param('order') || 'desc'; @@ -32,10 +51,10 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { allowEmpty: true, title: `${query}的搜索结果`, link, item: out, - }); -}; + }; +} diff --git a/lib/routes/github/star.ts b/lib/routes/github/star.ts index ef5b7fdce..169aa7a69 100644 --- a/lib/routes/github/star.ts +++ b/lib/routes/github/star.ts @@ -1,7 +1,29 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/stars/:user/:repo', + categories: ['design'], + example: '/github/stars/DIYGod/RSSHub', + parameters: { user: 'GitHub username', repo: 'GitHub repo name' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['github.com/:user/:repo/stargazers', 'github.com/:user/:repo'], + }, + name: 'Repo Stars', + maintainers: ['HenryQW'], + handler, +}; + +async function handler(ctx) { if (!config.github || !config.github.access_token) { throw new Error('GitHub star RSS is disabled due to the lack of relevant config'); } @@ -37,7 +59,7 @@ export default async (ctx) => { const data = response.data.data.repository.stargazers.edges.reverse(); - ctx.set('data', { + return { allowEmpty: true, title: `${user}/${repo}’s stargazers`, link: host, @@ -47,5 +69,5 @@ export default async (ctx) => { description: `${follower.node.login}
    `, link: `https://github.com/${follower.node.login}`, })), - }); -}; + }; +} diff --git a/lib/routes/github/starred-repos.ts b/lib/routes/github/starred-repos.ts index e28cfbdee..6ec1aaade 100644 --- a/lib/routes/github/starred-repos.ts +++ b/lib/routes/github/starred-repos.ts @@ -1,7 +1,29 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/starred_repos/:user', + categories: ['design'], + example: '/github/starred_repos/DIYgod', + parameters: { user: 'User name' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['github.com/:user'], + }, + name: 'User Starred Repositories', + maintainers: ['LanceZhu'], + handler, +}; + +async function handler(ctx) { if (!config.github || !config.github.access_token) { throw new Error('GitHub star RSS is disabled due to the lack of relevant config'); } @@ -45,7 +67,7 @@ export default async (ctx) => { const data = response.data.data.user.starredRepositories.edges; - ctx.set('data', { + return { allowEmpty: true, title: `${user}’s starred repositories`, link: host, @@ -59,5 +81,5 @@ export default async (ctx) => { pubDate: new Date(repo.starredAt), link: repo.node.url, })), - }); -}; + }; +} diff --git a/lib/routes/github/topic.ts b/lib/routes/github/topic.ts index d501d2453..7c3ddd925 100644 --- a/lib/routes/github/topic.ts +++ b/lib/routes/github/topic.ts @@ -1,15 +1,37 @@ +import { Route } from '@/types'; import { load } from 'cheerio'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/topics/:name/:qs?', + categories: ['design'], + example: '/github/topics/framework', + parameters: { name: 'Topic name, which can be found in the URL of the corresponding [Topics Page](https://github.com/topics/framework)', qs: 'Query string, like `l=php&o=desc&s=stars`. Details listed as follows:' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['github.com/topics'], + }, + name: 'Topics', + maintainers: ['queensferryme'], + handler, +}; + +async function handler(ctx) { const link = `https://github.com/topics/${ctx.req.param('name')}`; const { data, url } = await got(link, { searchParams: new URLSearchParams(ctx.req.param('qs')), }); const $ = load(data); - ctx.set('data', { + return { title: $('title').text(), description: $('.markdown-body').text().trim(), link: url, @@ -37,5 +59,5 @@ export default async (ctx) => { pubDate, }; }), - }); -}; + }; +} diff --git a/lib/routes/github/trending.ts b/lib/routes/github/trending.ts index 00da08b82..a191734cd 100644 --- a/lib/routes/github/trending.ts +++ b/lib/routes/github/trending.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -7,7 +8,33 @@ import { art } from '@/utils/render'; import { load } from 'cheerio'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/trending/:since/:language/:spoken_language?', + categories: ['design'], + example: '/github/trending/daily/javascript/en', + parameters: { + since: "time frame, available in [Trending page](https://github.com/trending/javascript?since=monthly) 's URL, possible values are: `daily`, `weekly` or `monthly`", + language: "the feed language, available in [Trending page](https://github.com/trending/javascript?since=monthly) 's URL, don't filter option is `any`", + spoken_language: "natural language, available in [Trending page](https://github.com/trending/javascript?since=monthly) 's URL", + }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['github.com/trending'], + target: '/trending/:since', + }, + name: 'Trending', + maintainers: ['DIYgod', 'jameschensmith'], + handler, +}; + +async function handler(ctx) { if (!config.github || !config.github.access_token) { throw new Error('GitHub trending RSS is disabled due to the lack of relevant config'); } @@ -73,7 +100,7 @@ export default async (ctx) => { return { ...found, ...repo }; }); - ctx.set('data', { + return { title: $('title').text(), link: trendingUrl, item: repos.map((r) => ({ @@ -88,5 +115,5 @@ export default async (ctx) => { }), link: `https://github.com/${r.nameWithOwner}`, })), - }); -}; + }; +} diff --git a/lib/routes/github/wiki.ts b/lib/routes/github/wiki.ts index 1c6443ed3..19ff4540e 100644 --- a/lib/routes/github/wiki.ts +++ b/lib/routes/github/wiki.ts @@ -1,10 +1,33 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; const baseUrl = 'https://github.com'; -export default async (ctx) => { +export const route: Route = { + path: '/wiki/:user/:repo/:page?', + categories: ['design'], + example: '/github/wiki/flutter/flutter/Roadmap', + parameters: { user: 'User / Org name', repo: 'Repo name', page: 'Page slug, can be found in URL, empty means Home' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['github.com/:user/:repo/wiki/:page/_history', 'github.com/:user/:repo/wiki/:page', 'github.com/:user/:repo/wiki/_history', 'github.com/:user/:repo/wiki'], + target: '/wiki/:user/:repo/:page', + }, + name: 'Wiki History', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { user, repo, page } = ctx.req.param(); const url = `${baseUrl}/${user}/${repo}/wiki${page ? `/${page}` : ''}/_history`; @@ -24,9 +47,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `${$('.gh-header-title').text()} - ${user}/${repo}`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/gitpod/blog.ts b/lib/routes/gitpod/blog.ts index f7afe3ed9..f940a43fc 100644 --- a/lib/routes/gitpod/blog.ts +++ b/lib/routes/gitpod/blog.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 { rootUrl } from './utils'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/blog', + categories: ['design'], + example: '/gitpod/blog', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gitpod.io/blog', 'gitpod.io/'], + }, + name: 'Blog', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 10; const response = await got(rootUrl + '/blog'); const $ = load(response.data); @@ -46,13 +68,13 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: rootUrl + '/blog', description: $('meta[name="description"]').attr('content'), language: 'en-US', item: items, - }); + }; ctx.set('json', { title: $('title').text(), @@ -61,4 +83,4 @@ export default async (ctx) => { language: 'en-US', item: items, }); -}; +} diff --git a/lib/routes/gitpod/changelog.ts b/lib/routes/gitpod/changelog.ts index f2a64ffa0..e91aecac9 100644 --- a/lib/routes/gitpod/changelog.ts +++ b/lib/routes/gitpod/changelog.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 { rootUrl } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/changelog', + categories: ['design'], + example: '/gitpod/changelog', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gitpod.io/changelog', 'gitpod.io/'], + }, + name: 'Changelog', + maintainers: ['TonyRL'], + handler, +}; + +async function handler() { const response = await got(rootUrl + '/changelog'); const $ = load(response.data); const items = $('div[class^=changelog-entry]') @@ -25,11 +47,11 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('title').text(), link: rootUrl + '/changelog', description: $('meta[name="description"]').attr('content'), language: 'en-US', item: items, - }); -}; + }; +} diff --git a/lib/routes/globallawreview/index.ts b/lib/routes/globallawreview/index.ts index c67e53e48..b46e043e5 100644 --- a/lib/routes/globallawreview/index.ts +++ b/lib/routes/globallawreview/index.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: '/', + radar: { + source: ['globallawreview.org/Magazine/GetIssueContentList', 'globallawreview.org/'], + target: '', + }, + name: 'Unknown', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; const rootUrl = 'http://www.globallawreview.org'; @@ -45,11 +57,11 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { item: items, title: $('title').text(), link: currentUrl, language: 'zh-cn', author: '中国社会科学院法学研究所', - }); -}; + }; +} diff --git a/lib/routes/gocn/jobs.ts b/lib/routes/gocn/jobs.ts index 659cf158c..de612407f 100644 --- a/lib/routes/gocn/jobs.ts +++ b/lib/routes/gocn/jobs.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { renderHTML } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/jobs', + categories: ['design'], + example: '/gocn/jobs', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gocn.vip/'], + }, + name: '招聘', + maintainers: ['AtlanCI', 'CcccFz'], + handler, +}; + +async function handler() { const api_url = 'https://gocn.vip/api/files?spaceGuid=Gd7OHl¤tPage=1&sort=1'; const base_url = 'https://gocn.vip/c/3lQ6GbD5ny/s/Gd7OHl'; const job_url = 'https://gocn.vip/c/3lQ6GbD5ny'; @@ -19,10 +41,10 @@ export default async (ctx) => { author: item.nickname, })); - ctx.set('data', { + return { title: `GoCN社区-招聘`, link: base_url, description: `获取GoCN站点招聘`, item: items, - }); -}; + }; +} diff --git a/lib/routes/gocn/news.ts b/lib/routes/gocn/news.ts index ab958efdf..b2aa1dba2 100644 --- a/lib/routes/gocn/news.ts +++ b/lib/routes/gocn/news.ts @@ -1,8 +1,16 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { renderHTML } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: ['/', '/news'], + name: 'Unknown', + maintainers: ['AtlanCI', 'CcccFz'], + handler, +}; + +async function handler() { const base_url = 'https://gocn.vip/c/3lQ6GbD5ny/home'; const article_url = 'https://gocn.vip/c/3lQ6GbD5ny'; const api_url = 'https://gocn.vip/api/home/page'; @@ -22,10 +30,10 @@ export default async (ctx) => { author: item.nickname, })); - ctx.set('data', { + return { title: `GoCN社区-最新动态`, link: base_url, description: `获取GoCN站点最新动态`, item: items, - }); -}; + }; +} diff --git a/lib/routes/gocn/topics.ts b/lib/routes/gocn/topics.ts index 4ba8c36ff..f10798177 100644 --- a/lib/routes/gocn/topics.ts +++ b/lib/routes/gocn/topics.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { renderHTML } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/topics', + categories: ['design'], + example: '/gocn/topics', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gocn.vip/'], + }, + name: '每日新闻', + maintainers: ['AtlanCI', 'CcccFz'], + handler, +}; + +async function handler() { const base_url = 'https://gocn.vip/c/3lQ6GbD5ny/home'; const article_url = 'https://gocn.vip/c/3lQ6GbD5ny'; const api_url = 'https://gocn.vip/api/files?spaceGuid=Gd7BTB¤tPage=1&sort=1'; @@ -22,10 +44,10 @@ export default async (ctx) => { author: item.nickname, })); - ctx.set('data', { + return { title: `GoCN社区-每日新闻`, link: base_url, description: `获取GoCN站点每日新闻`, item: items, - }); -}; + }; +} diff --git a/lib/routes/gofans/index.ts b/lib/routes/gofans/index.ts index c305cbd52..1ff2e25dc 100644 --- a/lib/routes/gofans/index.ts +++ b/lib/routes/gofans/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: '/:kind?', + categories: ['university'], + example: '/gofans', + parameters: { kind: 'Platform, either `macos` or `ios`, empty means both (default)' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '最新限免 / 促销应用', + maintainers: ['HenryQW'], + handler, +}; + +async function handler(ctx) { const { kind = '' } = ctx.req.param(); const baseUrl = 'https://gofans.cn'; @@ -35,10 +54,10 @@ export default async (ctx) => { category: item.primary_genre_name, })); - ctx.set('data', { + return { title: '最新限免 / 促销应用', link: baseUrl, description: 'GoFans:最新限免 / 促销应用', item: items, - }); -}; + }; +} diff --git a/lib/routes/gogoanimehd/recent-releases.ts b/lib/routes/gogoanimehd/recent-releases.ts index 5b8aa52b2..ac941484a 100644 --- a/lib/routes/gogoanimehd/recent-releases.ts +++ b/lib/routes/gogoanimehd/recent-releases.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: '/recent-releases', + categories: ['program-update'], + example: '/gogoanimehd/recent-releases', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['developer.anitaku.to/'], + }, + name: 'Recent Releases', + maintainers: ['user4302'], + handler, +}; + +async function handler() { const rootUrl = 'https://anitaku.to/home.html'; const response = await got({ @@ -29,9 +51,9 @@ export default async (ctx) => { return structuredData; }); - ctx.set('data', { + return { title: $('title').text(), link: rootUrl, item: arrayOfItems, - }); -}; + }; +} diff --git a/lib/routes/google/album.ts b/lib/routes/google/album.ts index d77427720..fc3eef39d 100644 --- a/lib/routes/google/album.ts +++ b/lib/routes/google/album.ts @@ -1,6 +1,25 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/album/:id', + categories: ['anime'], + example: '/google/album/msFFnAzKmQmWj76EA', + parameters: { id: 'album ID, can be found in URL, for example, `https://photos.app.goo.gl/msFFnAzKmQmWj76EA` to `msFFnAzKmQmWj76EA`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Public Albums', + maintainers: ['hoilc'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const url = `https://photos.app.goo.gl/${id}?_imcp=1`; @@ -16,7 +35,7 @@ export default async (ctx) => { const list = info[1]; - ctx.set('data', { + return { title: `${owner_name} 创建的 Google 相册 - ${album_name}`, link: url, item: list.slice(0, 50).map((item) => { @@ -32,5 +51,5 @@ export default async (ctx) => { guid: item[0], }; }), - }); -}; + }; +} diff --git a/lib/routes/google/alerts.ts b/lib/routes/google/alerts.ts index 6185459f5..f619bc6ef 100644 --- a/lib/routes/google/alerts.ts +++ b/lib/routes/google/alerts.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: '/alerts/:keyword', + categories: ['other'], + example: '/google/alerts/RSSHub', + parameters: { keyword: 'Keyword' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Alerts', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const keyword = ctx.req.param('keyword'); const { data: response, url: link } = await got('https://www.google.com/alerts/preview', { @@ -25,9 +44,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `Google Alerts - ${keyword}`, link, item: items, - }); -}; + }; +} diff --git a/lib/routes/google/citations.ts b/lib/routes/google/citations.ts index d8eca8d93..85600a16b 100644 --- a/lib/routes/google/citations.ts +++ b/lib/routes/google/citations.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: '/citations/:id', + categories: ['finance'], + example: '/google/citations/mlmE4JMAAAAJ', + parameters: { id: 'N' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Author Citations', + maintainers: ['KellyHwong', 'const7'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const BASE_URL = `https://scholar.google.com`; const url = `https://scholar.google.com/citations?user=${id}`; @@ -35,10 +54,10 @@ export default async (ctx) => { guid: itemUrl, }; }); - ctx.set('data', { + return { title: `Google Scholar: ${name}`, link: url, description, item: out, - }); -}; + }; +} diff --git a/lib/routes/google/doodles.ts b/lib/routes/google/doodles.ts index 951aceb56..29165a3ca 100644 --- a/lib/routes/google/doodles.ts +++ b/lib/routes/google/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/:language?', + categories: ['anime'], + example: '/google/doodles/zh-CN', + parameters: { language: 'Language, default to `zh-CN`, for other language values, you can get it from [Google Doodles official website](https://www.google.com/doodles)' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Update', + maintainers: ['xyqfer'], + handler, +}; + +async function handler(ctx) { const { language = 'zh-CN' } = ctx.req.param(); const current = new Date(); const year = current.getFullYear(); @@ -15,7 +34,7 @@ export default async (ctx) => { }, }); - ctx.set('data', { + return { title: 'Google Doodles', link, item: @@ -31,5 +50,5 @@ export default async (ctx) => { link: `https://www.google.com/search?q=${encodeURIComponent(item.query)}`, }; }), - }); -}; + }; +} diff --git a/lib/routes/google/extension.ts b/lib/routes/google/extension.ts index 1daccf8c6..00f451497 100644 --- a/lib/routes/google/extension.ts +++ b/lib/routes/google/extension.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: '/chrome/extension/:id', + categories: ['university'], + example: '/google/chrome/extension/kefjpfngnndepjbopdmoebkipbgkggaa', + parameters: { id: 'Extension id, can be found in extension url' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['chromewebstore.google.com/detail/:name/:id'], + }, + name: 'Extension Update', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const response = await got({ @@ -12,7 +34,7 @@ export default async (ctx) => { const version = 'v' + $('.pDlpAd').text(); - ctx.set('data', { + return { title: $('.Pa2dE').text() + ' - Google Chrome Extension', link: `https://chrome.google.com/webstore/detail/${id}`, item: [ @@ -25,5 +47,5 @@ export default async (ctx) => { author: $('.yNyGQd').text(), }, ], - }); -}; + }; +} diff --git a/lib/routes/google/fonts.ts b/lib/routes/google/fonts.ts index fd7d63cf4..fc64c26e6 100644 --- a/lib/routes/google/fonts.ts +++ b/lib/routes/google/fonts.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -15,7 +16,25 @@ const titleMap = { style: 'Number of styles', }; -export default async (ctx) => { +export const route: Route = { + path: '/fonts/:sort?', + categories: ['live'], + example: '/google/fonts/date', + parameters: { sort: 'Sorting type, see below, default to `date`' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Google Fonts', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const sort = ctx.req.param('sort') ?? 'date'; const limit = ctx.req.param('limit') ?? 25; @@ -29,7 +48,7 @@ export default async (ctx) => { const response = await got.get(googleFontsAPI); const data = response.data.items.slice(0, limit); - ctx.set('data', { + return { title: `Google Fonts - ${titleMap[sort]}`, link: 'https://fonts.google.com', item: @@ -42,5 +61,5 @@ export default async (ctx) => { link: `https://fonts.google.com/specimen/${item.family.replaceAll(/\s/g, '+')}`, pubDate: parseDate(item.lastModified, 'YYYY-MM-DD'), })), - }); -}; + }; +} diff --git a/lib/routes/google/news.ts b/lib/routes/google/news.ts index d37465dae..f041f32d2 100644 --- a/lib/routes/google/news.ts +++ b/lib/routes/google/news.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 * as path from 'node:path'; const baseUrl = 'https://news.google.com'; -export default async (ctx) => { +export const route: Route = { + path: '/news/:category/:locale', + categories: ['traditional-media'], + example: '/google/news/Top stories/hl=en-US&gl=US&ceid=US:en', + parameters: { category: 'Category Title', locale: 'locales, could be found behind `?`, including `hl`, `gl`, and `ceid` as parameters' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'News', + maintainers: ['zoenglinghou'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category'); const locale = ctx.req.param('locale'); @@ -62,9 +81,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('title').text(), link: categoryUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/google/scholar.ts b/lib/routes/google/scholar.ts index c0dd45fa0..124d2802d 100644 --- a/lib/routes/google/scholar.ts +++ b/lib/routes/google/scholar.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: '/scholar/:query', + categories: ['finance'], + example: '/google/scholar/data+visualization', + parameters: { query: 'query statement which supports「Basic」and「Advanced」modes' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Keywords Monitoring', + maintainers: ['HenryQW'], + handler, +}; + +async function handler(ctx) { let params = ctx.req.param('query'); let query = params; let description = `Google Scholar Monitor Query: ${query}`; @@ -36,10 +55,10 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `Google Scholar Monitor: ${query}`, link: url, description, item: out, - }); -}; + }; +} diff --git a/lib/routes/google/search.ts b/lib/routes/google/search.ts index f396977c6..d35666fd7 100644 --- a/lib/routes/google/search.ts +++ b/lib/routes/google/search.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 { 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/:language?', + categories: ['other'], + example: '/google/search/rss/zh-CN,zh', + parameters: { keyword: 'Keyword', language: 'Accept-Language. Example: zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Search', + maintainers: ['CaoMeiYouRen'], + handler, +}; + +async function handler(ctx) { const { keyword, language } = ctx.req.param(); const searchParams = new URLSearchParams({ q: keyword, @@ -57,10 +76,10 @@ export default async (ctx) => { false ); - ctx.set('data', { + return { title: `${keyword} - Google Search`, description: `${keyword} - Google Search`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/anhui/kjt.ts b/lib/routes/gov/anhui/kjt.ts index 905b31ff1..e6e3f56ed 100644 --- a/lib/routes/gov/anhui/kjt.ts +++ b/lib/routes/gov/anhui/kjt.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: '/anhui/kjt/*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const params = ctx.params[0] ?? 'kjzx/tzgg'; const rootUrl = 'http://kjt.ah.gov.cn'; @@ -49,9 +57,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/beijing/bjedu/gh.ts b/lib/routes/gov/beijing/bjedu/gh.ts index e040ff310..1761f413b 100644 --- a/lib/routes/gov/beijing/bjedu/gh.ts +++ b/lib/routes/gov/beijing/bjedu/gh.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: '/beijing/bjedu/gh/:urlPath?', + categories: ['study'], + example: '/gov/beijing/bjedu/gh', + parameters: { urlPath: '路径,默认为 `zxtzgg`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gh.bjedu.gov.cn/ghsite/:urlPath/index.html', 'gh.bjedu.gov.cn/ghsite/:urlPath'], + target: '/beijing/bjedu/gh/:urlPath', + }, + name: '通用', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://gh.bjedu.cn'; const { urlPath = 'zxtzgg' } = ctx.req.param(); @@ -41,9 +64,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), link, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/beijing/bphc/index.ts b/lib/routes/gov/beijing/bphc/index.ts index 939810785..e5e37f345 100644 --- a/lib/routes/gov/beijing/bphc/index.ts +++ b/lib/routes/gov/beijing/bphc/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'; @@ -19,7 +20,14 @@ const mapping = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/beijing/bphc/*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const rootUrl = 'https://gycpt.bphc.com.cn'; const defaultPath = 'announcement'; @@ -48,9 +56,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: obj.title, link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/beijing/jw/tzgg.ts b/lib/routes/gov/beijing/jw/tzgg.ts index 8a857f234..73684420a 100644 --- a/lib/routes/gov/beijing/jw/tzgg.ts +++ b/lib/routes/gov/beijing/jw/tzgg.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: '/beijing/jw/tzgg', + categories: ['study'], + example: '/gov/beijing/jw/tzgg', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['jw.beijing.gov.cn/tzgg'], + }, + name: '通知公告', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'http://jw.beijing.gov.cn'; const currentUrl = `${rootUrl}/tzgg`; @@ -50,10 +72,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '北京市教育委员会 - 通知公告', link: currentUrl, item: items, description: $('meta[name="ColumnDescription"]').attr('content'), - }); -}; + }; +} diff --git a/lib/routes/gov/beijing/kw/index.ts b/lib/routes/gov/beijing/kw/index.ts index 7fa950828..be65a97e2 100644 --- a/lib/routes/gov/beijing/kw/index.ts +++ b/lib/routes/gov/beijing/kw/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 rootUrl = 'http://kw.beijing.gov.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/beijing/kw/:channel', + name: 'Unknown', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const channel = ctx.req.param('channel'); const url = `${rootUrl}/col/${channel}/index.html`; @@ -37,9 +45,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `北京市科学技术委员会、中关村科技园区管理委员会 - ${title}`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/caac/cjwt.ts b/lib/routes/gov/caac/cjwt.ts index a63989653..0e52a42f2 100644 --- a/lib/routes/gov/caac/cjwt.ts +++ b/lib/routes/gov/caac/cjwt.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: '/caac/cjwt/:category?', + categories: ['study'], + example: '/gov/caac/cjwt', + parameters: { category: '分类,见下表,默认为全部' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['caac.gov.cn/HDJL/'], + target: '/caac/cjwt', + }, + 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) : 30; @@ -43,7 +66,7 @@ export default async (ctx) => { const icon = new URL('images/weixinLogo.jpg', rootUrl).href; const subtitle = '公众留言'; - ctx.set('data', { + return { item: items, title: [author, subtitle, category].filter(Boolean).join(' - '), link: currentUrl, @@ -55,5 +78,5 @@ export default async (ctx) => { subtitle, author, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/gov/cac/index.ts b/lib/routes/gov/cac/index.ts index db82fcdb0..95d46dcbd 100644 --- a/lib/routes/gov/cac/index.ts +++ b/lib/routes/gov/cac/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'; import timezone from '@/utils/timezone'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/cac/*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const path = ctx.params[0]; const host = 'http://www.cac.gov.cn'; const homepage = `${host}/index.htm`; @@ -57,9 +65,9 @@ export default async (ctx) => { description: title, }; }); - ctx.set('data', { + return { title: $1('head title').text(), link: completeUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/ccdi/index.ts b/lib/routes/gov/ccdi/index.ts index 7ab6271a0..7f870aa69 100644 --- a/lib/routes/gov/ccdi/index.ts +++ b/lib/routes/gov/ccdi/index.ts @@ -1,7 +1,15 @@ +import { Route } from '@/types'; import { getSubPath } from '@/utils/common-utils'; import { rootUrl, parseNewsList, parseArticle } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/ccdi/*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const defaultPath = '/yaowenn/'; let pathname = getSubPath(ctx).replaceAll(/(^\/ccdi|\/$)/g, ''); @@ -11,9 +19,9 @@ export default async (ctx) => { const { list, title } = await parseNewsList(currentUrl, '.list_news_dl2 li', ctx); const items = await Promise.all(list.map((item) => parseArticle(item))); - ctx.set('data', { + return { title, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/chinamine-safety/xw.ts b/lib/routes/gov/chinamine-safety/xw.ts index a997f69b7..26c456e95 100644 --- a/lib/routes/gov/chinamine-safety/xw.ts +++ b/lib/routes/gov/chinamine-safety/xw.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 { parseDate } from '@/utils/parse-date'; import { rootUrl, processItems, fetchData } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/chinamine-safety/xw/:category{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { category = 'yjglbyw' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; @@ -31,8 +39,8 @@ export default async (ctx) => { items = await processItems(items, cache.tryGet); - ctx.set('data', { + return { item: items, ...fetchData($, currentUrl), - }); -}; + }; +} diff --git a/lib/routes/gov/chinamine-safety/zfxxgk.ts b/lib/routes/gov/chinamine-safety/zfxxgk.ts index 3c32b9271..8c7fe865c 100644 --- a/lib/routes/gov/chinamine-safety/zfxxgk.ts +++ b/lib/routes/gov/chinamine-safety/zfxxgk.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, processItems, fetchData } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/chinamine-safety/zfxxgk/:category{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { category = 'fdzdgknr/tzgg' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; @@ -30,8 +38,8 @@ export default async (ctx) => { items = await processItems(items, cache.tryGet); - ctx.set('data', { + return { item: items, ...fetchData($, currentUrl), - }); -}; + }; +} diff --git a/lib/routes/gov/chinatax/latest.ts b/lib/routes/gov/chinatax/latest.ts index d3c4bbb0d..040d84847 100644 --- a/lib/routes/gov/chinatax/latest.ts +++ b/lib/routes/gov/chinatax/latest.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: '/chinatax/latest', + categories: ['study'], + example: '/gov/chinatax/latest', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.chinatax.gov.cn/*'], + }, + name: '最新文件', + maintainers: ['nczitzk', 'fuzy112'], + handler, +}; + +async function handler() { const link = `http://www.chinatax.gov.cn/chinatax/n810341/n810755/index.html`; const response = await got({ @@ -41,9 +63,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '国家税务总局 - 最新文件', link, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/chongqing/gzw.ts b/lib/routes/gov/chongqing/gzw.ts index 4d770d941..58e79b639 100644 --- a/lib/routes/gov/chongqing/gzw.ts +++ b/lib/routes/gov/chongqing/gzw.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: '/chongqing/gzw/:category{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { category = 'tzgg_191' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 15; @@ -50,7 +58,7 @@ export default async (ctx) => { const icon = new URL('favicon.ico', rootUrl).href; - ctx.set('data', { + return { item: items, title: `${$('title').text()} - ${$('meta[name="ColumnName"]').prop('content')}`, link: currentUrl, @@ -62,5 +70,5 @@ export default async (ctx) => { subtitle: $('meta[name="ColumnKeywords"]').prop('content'), author: $('meta[name="SiteName"]').prop('content'), allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/gov/chongqing/rsks.ts b/lib/routes/gov/chongqing/rsks.ts index 0bbb949c5..4c47d651b 100644 --- a/lib/routes/gov/chongqing/rsks.ts +++ b/lib/routes/gov/chongqing/rsks.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'; const rsksUrl = 'https://rlsbj.cq.gov.cn/ywzl/rsks/tzgg_109374/'; -export default async (ctx) => { +export const route: Route = { + path: '/chongqing/rsks', + categories: ['study'], + example: '/gov/chongqing/rsks', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['rlsbj.cq.gov.cn/'], + }, + name: 'Unknown', + maintainers: ['Mai19930513'], + handler, +}; + +async function handler() { const { data: response } = await got(rsksUrl); const $ = load(response); // 获取考试信息标题 @@ -31,9 +53,9 @@ export default async (ctx) => { }) ) ); - ctx.set('data', { + return { title: '重庆人事考试通知公告', link: rsksUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/chongqing/sydwgkzp.ts b/lib/routes/gov/chongqing/sydwgkzp.ts index a09d4c017..a161db78a 100644 --- a/lib/routes/gov/chongqing/sydwgkzp.ts +++ b/lib/routes/gov/chongqing/sydwgkzp.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 sydwgkzpUrl = 'https://rlsbj.cq.gov.cn/zwxx_182/sydw/'; -export default async (ctx) => { +export const route: Route = { + path: '/chongqing/sydwgkzp', + categories: ['study'], + example: '/gov/chongqing/sydwgkzp', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['rlsbj.cq.gov.cn/'], + }, + name: '人力社保局', + maintainers: ['MajexH'], + handler, +}; + +async function handler() { const { data: response } = await got(sydwgkzpUrl); const $ = load(response); @@ -40,9 +62,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '重庆市事业单位公开招聘', link: sydwgkzpUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/cmse/fxrw.ts b/lib/routes/gov/cmse/fxrw.ts index 4c78ad9ec..b9d95933c 100644 --- a/lib/routes/gov/cmse/fxrw.ts +++ b/lib/routes/gov/cmse/fxrw.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: '/cmse/fxrw', + categories: ['study'], + example: '/gov/cmse/fxrw', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.cmse.gov.cn/fxrw'], + }, + name: '飞行任务', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'http://www.cmse.gov.cn'; const currentUrl = `${rootUrl}/fxrw/`; @@ -35,9 +57,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/cmse/index.ts b/lib/routes/gov/cmse/index.ts index 642f0d73c..bffb1fab1 100644 --- a/lib/routes/gov/cmse/index.ts +++ b/lib/routes/gov/cmse/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 timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; -export default async (ctx) => { +export const route: Route = { + path: '/cmse/*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const path = getSubPath(ctx).replaceAll(/(^\/cmse|\/$)/g, ''); const rootUrl = 'http://www.cmse.gov.cn'; @@ -63,9 +71,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/cnnic/index.ts b/lib/routes/gov/cnnic/index.ts index f3ce9f77f..4a797167a 100644 --- a/lib/routes/gov/cnnic/index.ts +++ b/lib/routes/gov/cnnic/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: '/cnnic/*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const path = getSubPath(ctx).replaceAll(/^\/cnnic/g, ''); const rootUrl = 'http://www.cnnic.net.cn'; @@ -48,9 +56,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/csrc/auditstatus.ts b/lib/routes/gov/csrc/auditstatus.ts index 60e56619c..aa8bc2abf 100644 --- a/lib/routes/gov/csrc/auditstatus.ts +++ b/lib/routes/gov/csrc/auditstatus.ts @@ -1,10 +1,29 @@ +import { Route } from '@/types'; import { load } from 'cheerio'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; import md5 from '@/utils/md5'; -export default async (ctx) => { +export const route: Route = { + path: '/csrc/auditstatus/:apply_id', + categories: ['study'], + example: '/gov/csrc/auditstatus/9ce91cf2d750ee62de27fbbcb05fa483', + parameters: { apply_id: '事项类别id,`https://neris.csrc.gov.cn/alappl/home/xkDetail` 列表中各地址的 appMatrCde 参数' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '申请事项进度', + maintainers: ['hillerliao'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://neris.csrc.gov.cn'; const { apply_id } = ctx.req.param(); const itemUrl = `${baseUrl}/alappl/home1/onlinealog`; @@ -46,9 +65,9 @@ export default async (ctx) => { return single; }); - ctx.set('data', { + return { title: `${$('.zx2 div').attr('title')} - 申请事项进度查询 - 中国证监会`, link: res.url, item: out, - }); -}; + }; +} diff --git a/lib/routes/gov/csrc/news.ts b/lib/routes/gov/csrc/news.ts index cc3c06c73..51b2b6c34 100644 --- a/lib/routes/gov/csrc/news.ts +++ b/lib/routes/gov/csrc/news.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 timezone from '@/utils/timezone'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/csrc/news/:suffix{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const baseUrl = 'http://www.csrc.gov.cn'; const { suffix = 'c100028/common_xq_list.shtml' } = ctx.req.param(); const link = `${baseUrl}/csrc/${suffix}`; @@ -64,10 +72,10 @@ export default async (ctx) => { ); } - ctx.set('data', { + return { title: `中国证券监督管理委员会 - ${data?.data.channelName || $('head title').text()}`, link, image: 'http://www.csrc.gov.cn/favicon.ico', item: out, - }); -}; + }; +} diff --git a/lib/routes/gov/customs/list.ts b/lib/routes/gov/customs/list.ts index 50e4fefe4..9bb7ed911 100644 --- a/lib/routes/gov/customs/list.ts +++ b/lib/routes/gov/customs/list.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { load } from 'cheerio'; import { host, puppeteerGet } from './utils'; @@ -6,7 +7,29 @@ import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; import puppeteer from '@/utils/puppeteer'; -export default async (ctx) => { +export const route: Route = { + path: '/customs/list/:gchannel?', + categories: ['study'], + example: '/gov/customs/list/paimai', + parameters: { gchannel: '支持 `paimai` 及 `fagui` 2个频道,默认为 `paimai`' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.customs.gov.cn/'], + target: '/customs/list', + }, + name: '拍卖信息 / 海关法规', + maintainers: ['Jeason0228', 'TonyRL', 'he1q'], + handler, +}; + +async function handler(ctx) { const { gchannel = 'paimai' } = ctx.req.param(); let channelName = ''; let link = ''; @@ -74,9 +97,9 @@ export default async (ctx) => { browser.close(); - ctx.set('data', { + return { title: `中国海关-${channelName}`, link, item: out, - }); -}; + }; +} diff --git a/lib/routes/gov/dianbai/dianbai.ts b/lib/routes/gov/dianbai/dianbai.ts index 79637e328..01851d937 100644 --- a/lib/routes/gov/dianbai/dianbai.ts +++ b/lib/routes/gov/dianbai/dianbai.ts @@ -1,6 +1,14 @@ +import { Route } from '@/types'; import { gdgov } from '../general/general'; -export default async (ctx) => { +export const route: Route = { + path: '/dianbai/*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const info = { defaultPath: 'zwgk/zcjd/', list_element: '.news_list li a', @@ -15,4 +23,4 @@ export default async (ctx) => { pubDate_format: undefined, }; await gdgov(info, ctx); -}; +} diff --git a/lib/routes/gov/forestry/gjlckjdjt.ts b/lib/routes/gov/forestry/gjlckjdjt.ts index 1fdb12206..d0ee1bfe3 100644 --- a/lib/routes/gov/forestry/gjlckjdjt.ts +++ b/lib/routes/gov/forestry/gjlckjdjt.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: '/forestry/gjlckjdjt/:category?', + categories: ['study'], + example: '/gov/forestry/gjlckjdjt', + 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 = 'gjlckjdjt' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; @@ -85,7 +104,7 @@ export default async (ctx) => { const icon = new URL('favicon.ico', rootUrl).href; - ctx.set('data', { + return { item: items, title: $('title').text(), link: currentUrl, @@ -96,5 +115,5 @@ export default async (ctx) => { subtitle: $('div.weizhi').contents().last().text(), author: '国家林业和草原局', allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/gov/gaozhou/gaozhou.ts b/lib/routes/gov/gaozhou/gaozhou.ts index bcdf4ccdf..38f2025ad 100644 --- a/lib/routes/gov/gaozhou/gaozhou.ts +++ b/lib/routes/gov/gaozhou/gaozhou.ts @@ -1,6 +1,14 @@ +import { Route } from '@/types'; import { gdgov } from '../general/general'; -export default async (ctx) => { +export const route: Route = { + path: '/gaozhou/*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const info = { defaultPath: 'zcjd/', list_element: '.newslist li a', @@ -16,4 +24,4 @@ export default async (ctx) => { pubDate_format: undefined, }; await gdgov(info, ctx); -}; +} diff --git a/lib/routes/gov/general/general.ts b/lib/routes/gov/general/general.ts index 93ca7e796..335563ed7 100644 --- a/lib/routes/gov/general/general.ts +++ b/lib/routes/gov/general/general.ts @@ -230,11 +230,11 @@ const gdgov = async (info, ctx) => { }) ); - ctx.set('data', { + return { title: name, link: currentUrl, item: items, - }); + }; }; export { gdgov }; diff --git a/lib/routes/gov/guangdong/tqyb/sncsyjxh.ts b/lib/routes/gov/guangdong/tqyb/sncsyjxh.ts index d0b975569..0bf1d99d5 100644 --- a/lib/routes/gov/guangdong/tqyb/sncsyjxh.ts +++ b/lib/routes/gov/guangdong/tqyb/sncsyjxh.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'; const rootUrl = 'http://www.tqyb.com.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/guangdong/tqyb/sncsyjxh', + categories: ['travel'], + example: '/gov/guangdong/tqyb/sncsyjxh', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.tqyb.com.cn/gz/weatherAlarm/otherCity/'], + }, + name: '广东省内城市预警信号', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler() { const sncsyjxhJsUrl = `${rootUrl}/data/gzWeather/otherCityAlarm.js`; const response = await got.get(sncsyjxhJsUrl); @@ -32,9 +54,9 @@ export default async (ctx) => { guid: timezone(parseDate(item.datetime, 'YYYY年MM月DD日 HH:mm'), +8) + item.cname + item.sigtypename, })); - ctx.set('data', { + return { title: '广东省内城市预警信号', link: `http://www.tqyb.com.cn/gz/weatherAlarm/otherCity/`, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/guangdong/tqyb/tfxtq.ts b/lib/routes/gov/guangdong/tqyb/tfxtq.ts index b8a3bee13..f91b2eb3f 100644 --- a/lib/routes/gov/guangdong/tqyb/tfxtq.ts +++ b/lib/routes/gov/guangdong/tqyb/tfxtq.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'; const rootUrl = 'http://www.tqyb.com.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/guangdong/tqyb/tfxtq', + categories: ['travel'], + example: '/gov/guangdong/tqyb/tfxtq', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.tqyb.com.cn/gz/weatherAlarm/suddenWeather/'], + }, + name: '突发性天气提示', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler() { const tfxtqJsUrl = `${rootUrl}/data/gzWeather/weatherTips.js`; const response = await got.get(tfxtqJsUrl); @@ -25,9 +47,9 @@ export default async (ctx) => { guid: parseDate(item.ddate) + item.title, })); - ctx.set('data', { + return { title: '突发性天气提示', link: 'http://www.tqyb.com.cn/gz/weatherAlarm/suddenWeather/', item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/gz/index.ts b/lib/routes/gov/gz/index.ts index ce1a99fe3..3c9a58a14 100644 --- a/lib/routes/gov/gz/index.ts +++ b/lib/routes/gov/gz/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'; @@ -11,7 +12,14 @@ const urlMap = { zcjd: 'zcjd/zcjd', }; -export default async (ctx) => { +export const route: Route = { + path: '/gz/:channel/:category', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const channel = ctx.req.param('channel'); const category = ctx.req.param('category'); const url = `${rootUrl}/${channel}/${urlMap[category]}/`; @@ -30,9 +38,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `广州市人民政府 - ${$('.main_title').text()}`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/hebei/czt.ts b/lib/routes/gov/hebei/czt.ts index 1d5218339..0a662cc5f 100644 --- a/lib/routes/gov/hebei/czt.ts +++ b/lib/routes/gov/hebei/czt.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: '/hebei/czt/xwdt/:category?', + categories: ['study'], + example: '/gov/hebei/czt/xwdt', + 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') ?? 'gzdt'; const rootUrl = 'http://czt.hebei.gov.cn'; @@ -46,9 +65,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/huazhou/huazhou.ts b/lib/routes/gov/huazhou/huazhou.ts index b4ac540c5..69dc3826a 100644 --- a/lib/routes/gov/huazhou/huazhou.ts +++ b/lib/routes/gov/huazhou/huazhou.ts @@ -1,6 +1,14 @@ +import { Route } from '@/types'; import { gdgov } from '../general/general'; -export default async (ctx) => { +export const route: Route = { + path: '/huazhou/*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const info = { defaultPath: 'syzl/zcjd/', list_element: '.list-content li a', @@ -16,4 +24,4 @@ export default async (ctx) => { pubDate_format: undefined, }; await gdgov(info, ctx); -}; +} diff --git a/lib/routes/gov/huizhou/zwgk/index.ts b/lib/routes/gov/huizhou/zwgk/index.ts index dcad5bfc8..d8e65c57d 100644 --- a/lib/routes/gov/huizhou/zwgk/index.ts +++ b/lib/routes/gov/huizhou/zwgk/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 rootURL = 'http://www.huizhou.gov.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/huizhou/zwgk/:category?', + categories: ['study'], + example: '/gov/huizhou/zwgk/jgdt', + parameters: { category: '资讯类别,可以从网址中得到,默认为政务要闻' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '惠州市人民政府', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const cate = ctx.req.param('category') ?? 'zwyw'; const url = `${rootURL}/zwgk/hzsz/${cate}`; @@ -51,9 +70,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `惠州市人民政府 - ${title}`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/hunan/changsha/major-email.ts b/lib/routes/gov/hunan/changsha/major-email.ts index 895ce041c..afc511c99 100644 --- a/lib/routes/gov/hunan/changsha/major-email.ts +++ b/lib/routes/gov/hunan/changsha/major-email.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 iconv from 'iconv-lite'; import { parseDate } from '@/utils/parse-date'; const baseUrl = 'http://wlwz.changsha.gov.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/hunan/changsha/major-email', + categories: ['study'], + example: '/gov/hunan/changsha/major-email', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['wlwz.changsha.gov.cn/webapp/cs2020/email/*'], + }, + name: '长沙市人民政府', + maintainers: ['shansing'], + handler, +}; + +async function handler() { const listPage = await got('http://wlwz.changsha.gov.cn/webapp/cs2020/email/index.jsp', { responseType: 'buffer', }); @@ -45,9 +67,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '来信反馈 - 长沙市市长信箱', link: `${baseUrl}/webapp/cs2020/email/index.jsp`, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/immiau/news.ts b/lib/routes/gov/immiau/news.ts index c32180838..93481bdcd 100644 --- a/lib/routes/gov/immiau/news.ts +++ b/lib/routes/gov/immiau/news.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; @@ -18,7 +19,25 @@ const reqBodyByYear = (year) => ({ const getItemUrl = (id) => `https://immi.homeaffairs.gov.au/news-media/archive/article?itemId=${id}`; -export default async (ctx) => { +export const route: Route = { + path: '/immiau/news', + categories: ['study'], + example: '/gov/immiau/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Immigration and Citizenship - News', + maintainers: ['liu233w'], + handler, +}; + +async function handler() { const res = await got({ method: 'post', url, @@ -34,10 +53,10 @@ export default async (ctx) => { link: getItemUrl(item.Id), })); - ctx.set('data', { + return { title: 'News - Immigration and Citizenship', link: 'https://immi.homeaffairs.gov.au/news-media/archive', description: 'Australia Government, Department of Home Affairs', item: list, - }); -}; + }; +} diff --git a/lib/routes/gov/jgjcndrc/index.ts b/lib/routes/gov/jgjcndrc/index.ts index 355134905..1263552ac 100644 --- a/lib/routes/gov/jgjcndrc/index.ts +++ b/lib/routes/gov/jgjcndrc/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: '/jgjcndrc/:id?', + categories: ['study'], + example: '/gov/jgjcndrc', + parameters: { id: '栏目 id,见下表,默认为 692,即通知公告' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '价格监测中心', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const { id = 'sytzgg' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; @@ -47,7 +66,7 @@ export default async (ctx) => { const subtitle = $('li.L').first().text(); const image = new URL($('img.logo2').prop('src'), rootUrl).href; - ctx.set('data', { + return { item: items, title: `${author} - ${subtitle}`, link: currentUrl, @@ -56,5 +75,5 @@ export default async (ctx) => { image, subtitle, author, - }); -}; + }; +} diff --git a/lib/routes/gov/jinan/healthcommission/medical-exam-notice.ts b/lib/routes/gov/jinan/healthcommission/medical-exam-notice.ts index 23d1e4ca6..cd6b1cae1 100644 --- a/lib/routes/gov/jinan/healthcommission/medical-exam-notice.ts +++ b/lib/routes/gov/jinan/healthcommission/medical-exam-notice.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: '/jinan/healthcommission/medical_exam_notice', + categories: ['study'], + example: '/gov/jinan/healthcommission/medical_exam_notice', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['jnmhc.jinan.gov.cn/*'], + }, + name: '获取国家医师资格考试通知', + maintainers: ['tzjyxb'], + handler, +}; + +async function handler() { const baseUrl = 'https://jnmhc.jinan.gov.cn'; const res = await got('https://jnmhc.jinan.gov.cn/module/web/jpage/dataproxy.jsp', { @@ -22,7 +44,7 @@ export default async (ctx) => { const list = $('record'); - ctx.set('data', { + return { title: '济南卫建委-执业考试通知', link: `${baseUrl}/col/col14418/index.html`, item: list @@ -45,5 +67,5 @@ export default async (ctx) => { }; }) .get(), - }); -}; + }; +} diff --git a/lib/routes/gov/maoming/maoming.ts b/lib/routes/gov/maoming/maoming.ts index 24870eb6c..b92599091 100644 --- a/lib/routes/gov/maoming/maoming.ts +++ b/lib/routes/gov/maoming/maoming.ts @@ -1,7 +1,15 @@ +import { Route } from '@/types'; import { getSubPath } from '@/utils/common-utils'; import { gdgov } from '../general/general'; -export default async (ctx) => { +export const route: Route = { + path: '/maoming/*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const path = getSubPath(ctx) .split('/') .filter((item) => item !== ''); @@ -371,4 +379,4 @@ export default async (ctx) => { // pubDate_format, }; await gdgov(info, ctx); -}; +} diff --git a/lib/routes/gov/maonan/maonan.ts b/lib/routes/gov/maonan/maonan.ts index 9ca30007f..e306a04f8 100644 --- a/lib/routes/gov/maonan/maonan.ts +++ b/lib/routes/gov/maonan/maonan.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 host = 'http://www.maonan.gov.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/maonan/:category', + categories: ['study'], + example: '/gov/maonan/zwgk', + parameters: { category: '分类名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '茂名市茂南区人民政府', + maintainers: ['ShuiHuo'], + handler, +}; + +async function handler(ctx) { let id = ''; let name = ''; @@ -93,9 +112,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `茂名市茂南区人民政府 - ${name}`, link: `${host}/${id}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/mee/ywdt.ts b/lib/routes/gov/mee/ywdt.ts index 43345c1cf..acb82377e 100644 --- a/lib/routes/gov/mee/ywdt.ts +++ b/lib/routes/gov/mee/ywdt.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -14,7 +15,29 @@ const columns = { gsgg: { name: '公示公告', order: 6 }, }; -export default async (ctx) => { +export const route: Route = { + path: '/mee/ywdt/:category?', + categories: ['study'], + example: '/gov/mee/ywdt/hjywnews', + parameters: { category: '分类名,预设 `szyw`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.mee.gov.cn/ywdt/:category'], + target: '/mee/ywdt/:category', + }, + name: '要闻动态', + maintainers: ['liuxsdev'], + handler, +}; + +async function handler(ctx) { const cate = ctx.req.param('category') ?? 'szyw'; const url = `${baseUrl}ywdt/`; const title = `${columns[cate].name} - 要闻动态 - 中华人民共和国生态环境部`; @@ -73,9 +96,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/mem/sgcc.ts b/lib/routes/gov/mem/sgcc.ts index df51ceba5..ee1992027 100644 --- a/lib/routes/gov/mem/sgcc.ts +++ b/lib/routes/gov/mem/sgcc.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: '/mem/gk/sgcc/:category?', + categories: ['study'], + example: '/gov/mem/gk/sgcc/tbzdsgdcbg', + parameters: { category: '分类,见下表,默认为挂牌督办' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.mem.gov.cn/gk/sgcc/:category'], + target: '/mem/gk/sgcc/:category', + }, + name: '事故及灾害查处', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const { category = 'sggpdbqk' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; @@ -50,7 +73,7 @@ export default async (ctx) => { const icon = new URL($('link[rel="shortcut icon"]').prop('href'), rootUrl).href; - ctx.set('data', { + return { item: items, title: $('title').text(), link: currentUrl, @@ -61,5 +84,5 @@ export default async (ctx) => { logo: icon, subtitle: $('meta[name="ColumnName"]').prop('content'), author: $('meta[name="SiteName"]').prop('content'), - }); -}; + }; +} diff --git a/lib/routes/gov/mfa/wjdt.ts b/lib/routes/gov/mfa/wjdt.ts index 810ab8e76..700ebaec6 100644 --- a/lib/routes/gov/mfa/wjdt.ts +++ b/lib/routes/gov/mfa/wjdt.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,14 @@ const categories = { zcjd: 'zcjd', }; -export default async (ctx) => { +export const route: Route = { + path: ['/fmprc/:category?', '/mfa/wjdt/:category?'], + name: 'Unknown', + maintainers: ['nicolaszf', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'gjldrhd'; const rootUrl = 'https://www.mfa.gov.cn'; @@ -60,9 +68,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/mgs/mgs.ts b/lib/routes/gov/mgs/mgs.ts index c15747664..9d4db6587 100644 --- a/lib/routes/gov/mgs/mgs.ts +++ b/lib/routes/gov/mgs/mgs.ts @@ -1,6 +1,14 @@ +import { Route } from '@/types'; import { gdgov } from '../general/general'; -export default async (ctx) => { +export const route: Route = { + path: '/mgs/*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const info = { defaultPath: 'zwgk/zcjd/', list_element: '.list_con li a', @@ -16,4 +24,4 @@ export default async (ctx) => { pubDate_format: undefined, }; await gdgov(info, ctx); -}; +} diff --git a/lib/routes/gov/miit/wjfb.ts b/lib/routes/gov/miit/wjfb.ts index 957397755..11b424ed2 100644 --- a/lib/routes/gov/miit/wjfb.ts +++ b/lib/routes/gov/miit/wjfb.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://www.miit.gov.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/miit/wjfb/:ministry', + categories: ['study'], + example: '/gov/miit/wjfb/ghs', + parameters: { ministry: '部门缩写,可以在对应 URL 中获取' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['miit.gov.cn/jgsj/:ministry/wjfb/index.html'], + }, + name: '文件发布', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const ministry = ctx.req.param('ministry'); const url = `${rootUrl}/jgsj/${ministry}/wjfb/index.html`; @@ -54,9 +76,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `工业和信息化部 - ${title} 文件发布`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/miit/wjgs.ts b/lib/routes/gov/miit/wjgs.ts index b83385934..ebf986a27 100644 --- a/lib/routes/gov/miit/wjgs.ts +++ b/lib/routes/gov/miit/wjgs.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://www.miit.gov.cn'; const siteUrl = `${baseUrl}/zwgk/wjgs/index.html`; -export default async (ctx) => { +export const route: Route = { + path: '/miit/wjgs', + categories: ['study'], + example: '/gov/miit/wjgs', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '文件公示', + maintainers: ['Yoge-Code'], + handler, +}; + +async function handler() { const response = await got(siteUrl); const $ = load(response.data); const buildStatic = $('script[parseType=buildstatic]'); @@ -55,9 +74,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('head title').text()} - ${$('meta[name=SiteName]').attr('content')}`, link: siteUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/miit/yjzj.ts b/lib/routes/gov/miit/yjzj.ts index 25dda0427..032b90c13 100644 --- a/lib/routes/gov/miit/yjzj.ts +++ b/lib/routes/gov/miit/yjzj.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://www.miit.gov.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/miit/yjzj', + categories: ['study'], + example: '/gov/miit/yjzj', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['miit.gov.cn/gzcy/yjzj/index.html'], + }, + name: '意见征集', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler() { const url = `${rootUrl}/gzcy/yjzj/index.html`; const cookieResponse = await got(url); @@ -52,9 +74,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `工业和信息化部 - 意见征集`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/miit/zcjd.ts b/lib/routes/gov/miit/zcjd.ts index a6bf1e780..aae7f3f80 100644 --- a/lib/routes/gov/miit/zcjd.ts +++ b/lib/routes/gov/miit/zcjd.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://www.miit.gov.cn'; const siteUrl = `${baseUrl}/zwgk/zcjd/index.html`; -export default async (ctx) => { +export const route: Route = { + path: '/miit/zcjd', + categories: ['study'], + example: '/gov/miit/zcjd', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '政策解读', + maintainers: ['Yoge-Code'], + handler, +}; + +async function handler() { const response = await got(siteUrl); const $ = load(response.data); const buildStatic = $('script[parseType=buildstatic]'); @@ -55,9 +74,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('head title').text()} - ${$('meta[name=SiteName]').attr('content')}`, link: siteUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/miit/zcwj.ts b/lib/routes/gov/miit/zcwj.ts index ff6060045..698da5e78 100644 --- a/lib/routes/gov/miit/zcwj.ts +++ b/lib/routes/gov/miit/zcwj.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: '/miit/zcwj', + categories: ['study'], + example: '/gov/miit/zcwj', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '政策文件', + maintainers: ['Yoge-Code'], + handler, +}; + +async function handler() { const base_url = 'http://www.miit.gov.cn/n1146295/n1652858/'; const response = await got.get(base_url); const $ = load(response.data); @@ -45,10 +64,10 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: '中国工业化和信息部', link: 'http://www.miit.gov.cn', description: '政策文件', item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/mmht/mmht.ts b/lib/routes/gov/mmht/mmht.ts index f34cd17a8..ee5acd9c2 100644 --- a/lib/routes/gov/mmht/mmht.ts +++ b/lib/routes/gov/mmht/mmht.ts @@ -1,6 +1,14 @@ +import { Route } from '@/types'; import { gdgov } from '../general/general'; -export default async (ctx) => { +export const route: Route = { + path: '/mmht/*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const info = { defaultPath: 'xwzx/zcjd/', list_element: '#main21l_main_dk > table > tbody > tr > td:nth-child(2) a', @@ -16,4 +24,4 @@ export default async (ctx) => { pubDate_format: undefined, }; await gdgov(info, ctx); -}; +} diff --git a/lib/routes/gov/moa/moa.ts b/lib/routes/gov/moa/moa.ts index 9e41408d9..ae3213d26 100644 --- a/lib/routes/gov/moa/moa.ts +++ b/lib/routes/gov/moa/moa.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 { parseRelativeDate } from '@/utils/parse-date'; const hostUrl = 'http://www.moa.gov.cn/'; const hostUrlObj = new URL(hostUrl); // 用于在下面判断host -export default async (ctx) => { +export const route: Route = { + path: '/moa/:suburl{.+}', + radar: { + source: ['moa.gov.cn/'], + target: '/moa/:suburl', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const rawSuburl = ctx.req.param('suburl'); const suburl = rawSuburl.slice(-1) === '/' ? rawSuburl : rawSuburl + '/'; @@ -44,7 +56,7 @@ export default async (ctx) => { }) ); } -}; +} // 处理文章列表, 从那里获得一堆要爬取的页面, 然后爬取 async function dealChannel(suburl, selectors) { diff --git a/lib/routes/gov/moa/zdscxx.ts b/lib/routes/gov/moa/zdscxx.ts index b9ddaf05c..24284138e 100644 --- a/lib/routes/gov/moa/zdscxx.ts +++ b/lib/routes/gov/moa/zdscxx.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: ['/moa/sjzxfb/:category{.+}?', '/moa/zdscxx/: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) : 5; @@ -87,7 +95,7 @@ export default async (ctx) => { const description = '数据'; const icon = new URL('favicon.ico', rootUrl).href; - ctx.set('data', { + return { item: items, title: `${title} - ${description} - ${category}`, link: currentUrl, @@ -99,5 +107,5 @@ export default async (ctx) => { subtitle: category, author: title, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/gov/moe/moe.ts b/lib/routes/gov/moe/moe.ts index 1f95a1694..856678b89 100644 --- a/lib/routes/gov/moe/moe.ts +++ b/lib/routes/gov/moe/moe.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,25 @@ const typesIdMap = [ { type: 'edu_focus_news', id: 'eight_con2 .pchide>.TRS_Editor', name: '教育要闻' }, ]; -export default async (ctx) => { +export const route: Route = { + path: '/moe/:type', + categories: ['study'], + example: '/gov/moe/policy_anal', + parameters: { type: '分类名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '新闻', + maintainers: ['Crawler995'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); let id = ''; let name = ''; @@ -35,7 +54,7 @@ export default async (ctx) => { const $ = load(response.data); const newsLis = $('div#' + id + '>ul>li'); - ctx.set('data', { + return { title: name, link: moeUrl, item: await Promise.all( @@ -82,5 +101,5 @@ export default async (ctx) => { }) .get() ), - }); -}; + }; +} diff --git a/lib/routes/gov/moe/s78.ts b/lib/routes/gov/moe/s78.ts index 28c76cadf..d0044f495 100644 --- a/lib/routes/gov/moe/s78.ts +++ b/lib/routes/gov/moe/s78.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: '/moe/s78/:column', + categories: ['study'], + example: '/gov/moe/s78/A13', + parameters: { column: '司局 ID,可在 URL 找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['moe.gov.cn/s78/:column/tongzhi', 'moe.gov.cn/s78/:column'], + }, + name: '司局通知', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://www.moe.gov.cn'; const column = ctx.req.param('column'); const link = `${baseUrl}/s78/${column}/tongzhi/`; @@ -37,9 +59,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('meta[name="ColumnType"]').attr('content')} - ${$('head title').text()}`, link, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/mof/bond.ts b/lib/routes/gov/mof/bond.ts index da85c494f..15fd58d4d 100644 --- a/lib/routes/gov/mof/bond.ts +++ b/lib/routes/gov/mof/bond.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,25 @@ import { parseDate } from '@/utils/parse-date'; const domain = 'gks.mof.gov.cn'; const theme = 'guozaiguanli'; -export default async (ctx) => { +export const route: Route = { + path: '/mof/bond/:category?', + categories: ['study'], + example: '/gov/mof/bond', + parameters: { category: '专题,见下表,默认为国债管理工作动态' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '专题', + maintainers: ['la3rence'], + handler, +}; + +async function handler(ctx) { const { category = 'gzfxgzdt' } = ctx.req.param(); const currentUrl = `https://${domain}/ztztz/${theme}/${category}/`; const { data: response } = await got(currentUrl); @@ -42,11 +61,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { item: items, title, link: currentUrl, description: `${description} - ${siteName}`, author, - }); -}; + }; +} diff --git a/lib/routes/gov/mofcom/article.ts b/lib/routes/gov/mofcom/article.ts index 12fca9ab8..4e7f66757 100644 --- a/lib/routes/gov/mofcom/article.ts +++ b/lib/routes/gov/mofcom/article.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { load } from 'cheerio'; import got from '@/utils/got'; @@ -6,7 +7,14 @@ import timezone from '@/utils/timezone'; const host = 'http://www.mofcom.gov.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/mofcom/article/:suffix{.+}', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const suffix = ctx.req.param('suffix'); const url = `http://www.mofcom.gov.cn/article/${suffix}/`; const { data: res } = await got(url); @@ -60,10 +68,10 @@ export default async (ctx) => { }) ) ); - ctx.set('data', { + return { title: $('head > title').text(), description: $('meta[name=description]').attr('content'), link: url, item: list, - }); -}; + }; +} diff --git a/lib/routes/gov/moj/aac/news.ts b/lib/routes/gov/moj/aac/news.ts index dd3912e8e..5a722b40a 100644 --- a/lib/routes/gov/moj/aac/news.ts +++ b/lib/routes/gov/moj/aac/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 { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; const baseUrl = 'https://www.aac.moj.gov.tw'; -export default async (ctx) => { +export const route: Route = { + path: '/moj/aac/news/:type?', + categories: ['study'], + example: '/gov/moj/aac/news', + parameters: { type: '資料大類,留空為全部' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '最新消息', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); const url = `${baseUrl}/7204/7246/?Page=1&PageSize=40${type ? `&type=${type}` : ''}`; const response = await got(url); @@ -41,10 +60,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), link: url, item: items, language: 'zh-TW', - }); -}; + }; +} diff --git a/lib/routes/gov/moj/lfyjzj.ts b/lib/routes/gov/moj/lfyjzj.ts index d48c93701..00ee68e59 100644 --- a/lib/routes/gov/moj/lfyjzj.ts +++ b/lib/routes/gov/moj/lfyjzj.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 DOMAIN = 'www.moj.gov.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/moj/lfyjzj', + categories: ['study'], + example: '/gov/moj/lfyjzj', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.moj.gov.cn/lfyjzj/lflfyjzj/*', 'www.moj.gov.cn/pub/sfbgw/lfyjzj/lflfyjzj/*'], + }, + name: '立法意见征集', + maintainers: ['la3rence'], + handler, +}; + +async function handler() { const rootUrl = `https://${DOMAIN}`; const currentUrl = `${rootUrl}/lfyjzj/lflfyjzj/index.html`; const { data: response } = await got(currentUrl); @@ -43,12 +65,12 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { item: items, title: theme, link: currentUrl, description, author: siteName, icon, - }); -}; + }; +} diff --git a/lib/routes/gov/mot/index.ts b/lib/routes/gov/mot/index.ts index b16d653d7..0eef6b4aa 100644 --- a/lib/routes/gov/mot/index.ts +++ b/lib/routes/gov/mot/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: '/mot/:category{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { category = 'tongjishuju/gonglu' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; @@ -54,7 +62,7 @@ export default async (ctx) => { const image = new URL($('a.navbar-brand img').prop('src'), rootUrl).href; - ctx.set('data', { + return { item: items, title: $('title').text(), link: currentUrl, @@ -63,5 +71,5 @@ export default async (ctx) => { image, subtitle: $('meta[name="ColumnName"]').prop('content'), author: $('meta[name="SiteName"]').prop('content'), - }); -}; + }; +} diff --git a/lib/routes/gov/ndrc/fggz.ts b/lib/routes/gov/ndrc/fggz.ts index 9daacd699..4fdd1abec 100644 --- a/lib/routes/gov/ndrc/fggz.ts +++ b/lib/routes/gov/ndrc/fggz.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: '/ndrc/fggz/: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) : 25; @@ -50,7 +58,7 @@ export default async (ctx) => { const image = $('div.logo a img').prop('src'); - ctx.set('data', { + return { item: items, title: $('title').text(), link: currentUrl, @@ -59,5 +67,5 @@ export default async (ctx) => { image, subtitle: $('meta[name="ColumnName"]').prop('content'), author: $('meta[name="SiteName"]').prop('content'), - }); -}; + }; +} diff --git a/lib/routes/gov/ndrc/xwdt.ts b/lib/routes/gov/ndrc/xwdt.ts index 9347975ff..4e69e942e 100644 --- a/lib/routes/gov/ndrc/xwdt.ts +++ b/lib/routes/gov/ndrc/xwdt.ts @@ -1,8 +1,16 @@ +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: '/ndrc/xwdt/:category{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') || 'xwfb'; const rootUrl = 'https://www.ndrc.gov.cn'; @@ -47,9 +55,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/nea/ghs.ts b/lib/routes/gov/nea/ghs.ts index 2aa988fa1..e10c9f272 100644 --- a/lib/routes/gov/nea/ghs.ts +++ b/lib/routes/gov/nea/ghs.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: '/nea/sjzz/ghs', + categories: ['study'], + example: '/gov/nea/sjzz/ghs', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['nea.gov.cn/sjzz/ghs/'], + }, + name: '发展规划司', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 35; const rootUrl = 'https://www.nea.gov.cn'; @@ -47,7 +69,7 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { item: items, title: $('title').text(), link: currentUrl, @@ -55,5 +77,5 @@ export default async (ctx) => { language: 'zh', subtitle: $('meta[name="ColumnType"]').prop('content'), author: $('meta[name="ColumnKeywords"]').prop('content'), - }); -}; + }; +} diff --git a/lib/routes/gov/news/index.ts b/lib/routes/gov/news/index.ts index 5c12983fb..0aaa8ef4f 100644 --- a/lib/routes/gov/news/index.ts +++ b/lib/routes/gov/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'; @@ -5,7 +6,25 @@ import logger from '@/utils/logger'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -export default async (ctx) => { +export const route: Route = { + path: '/news/:uid', + categories: ['study'], + example: '/gov/news/bm', + parameters: { uid: '分类名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '政府新闻', + maintainers: ['EsuRt'], + handler, +}; + +async function handler(ctx) { const uid = ctx.req.param('uid'); const originDomain = 'https://www.gov.cn'; let url = ''; @@ -46,7 +65,7 @@ export default async (ctx) => { list = $('.news_box .list li:not(.line)'); } - ctx.set('data', { + return { title, link: url, item: await Promise.all( @@ -102,5 +121,5 @@ export default async (ctx) => { }); }) ), - }); -}; + }; +} diff --git a/lib/routes/gov/nifdc/index.ts b/lib/routes/gov/nifdc/index.ts index 38a1e283b..8f7741889 100644 --- a/lib/routes/gov/nifdc/index.ts +++ b/lib/routes/gov/nifdc/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: '/nifdc/:path{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { path = 'bshff/ylqxbzhgl/qxggtzh' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; @@ -64,7 +72,7 @@ export default async (ctx) => { const image = new URL($('div.logo img').prop('src'), currentUrl).href; const icon = new URL($('link[rel="shortcut icon"]').prop('href'), currentUrl).href; - ctx.set('data', { + return { item: items, title: $('title').text().replace(/----/, ' - '), link: currentUrl, @@ -75,5 +83,5 @@ export default async (ctx) => { logo: icon, subtitle: $('meta[ name="ColumnName"]').prop('content'), author: $('meta[name="SiteName"]').prop('content'), - }); -}; + }; +} diff --git a/lib/routes/gov/nmpa/generic.ts b/lib/routes/gov/nmpa/generic.ts index 010d54d72..aec478957 100644 --- a/lib/routes/gov/nmpa/generic.ts +++ b/lib/routes/gov/nmpa/generic.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; @@ -7,7 +8,14 @@ import { config } from '@/config'; const baseUrl = 'https://www.nmpa.gov.cn'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/nmpa/*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const path = ctx.params[0]; const url = `${baseUrl}/${path.endsWith('/') ? path.slice(0, -1) : path}/index.html`; const data = await cache.tryGet( @@ -53,10 +61,10 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: data.title, description: data.description, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/nopss/index.ts b/lib/routes/gov/nopss/index.ts index 0ea7e83c7..ffc8c3a4e 100644 --- a/lib/routes/gov/nopss/index.ts +++ b/lib/routes/gov/nopss/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'; @@ -6,7 +7,14 @@ 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: '/nopss/*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const params = getSubPath(ctx) === '/nopss' ? '/GB/219469' : getSubPath(ctx).replace(/^\/nopss/, ''); const rootUrl = 'http://www.nopss.gov.cn'; @@ -51,9 +59,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/npc/index.ts b/lib/routes/gov/npc/index.ts index e4bfc0ec8..acdbefc96 100644 --- a/lib/routes/gov/npc/index.ts +++ b/lib/routes/gov/npc/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'; import timezone from '@/utils/timezone'; -export default async (ctx) => { +export const route: Route = { + path: '/npc/:caty', + categories: ['study'], + example: '/gov/npc/c183', + parameters: { caty: '分类名,支持形如 `http://www.npc.gov.cn/npc/c2/*/` 的网站,传入 npc 之后的参数' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['npc.gov.cn/npc/c2/:caty'], + }, + name: '通用', + maintainers: ['233yeee'], + handler, +}; + +async function handler(ctx) { const caty = ctx.req.param('caty'); // 主页 const baseurl = `http://www.npc.gov.cn/npc/c2/${caty}/`; @@ -39,10 +61,10 @@ export default async (ctx) => { ) ); // 整合 - ctx.set('data', { + return { title, link: baseurl, description: title, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/nrta/dsj.ts b/lib/routes/gov/nrta/dsj.ts index 71a74c4aa..0dc830d84 100644 --- a/lib/routes/gov/nrta/dsj.ts +++ b/lib/routes/gov/nrta/dsj.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 asyncPool from 'tiny-async-pool'; -export default async (ctx) => { +export const route: Route = { + path: '/nrta/dsj/:category?', + categories: ['study'], + example: '/gov/nrta/dsj', + 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 = 'note' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 15; @@ -48,7 +67,7 @@ export default async (ctx) => { results.push(item); } - ctx.set('data', { + return { item: results, title: `${$('title').text()}-${$('div.headbottom_menu_selected').text()}`, link: currentUrl, @@ -56,5 +75,5 @@ export default async (ctx) => { language: 'zh-cn', image: $('img').first().prop('src'), author: '国家广播电影电视总局电视剧管理司', - }); -}; + }; +} diff --git a/lib/routes/gov/nrta/news.ts b/lib/routes/gov/nrta/news.ts index 75a1f1e93..ea5de2875 100644 --- a/lib/routes/gov/nrta/news.ts +++ b/lib/routes/gov/nrta/news.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,25 @@ const categories = { 114: '工作动态', }; -export default async (ctx) => { +export const route: Route = { + path: '/nrta/news/:category?', + categories: ['study'], + example: '/gov/nrta/news', + parameters: { category: '资讯类别,可从地址中获取,默认为总局要闻' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '分类', + maintainers: ['yuxinliu-alex'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 112; const rootUrl = 'http://www.nrta.gov.cn'; @@ -53,11 +72,11 @@ export default async (ctx) => { }) ) ); - ctx.set('data', { + return { title: category in categories ? categories[category] : '其他', link: `http://www.nrta.gov.cn/col/col${category}/index.html`, description: '国家广播电视总局', language: 'zh-cn', item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/nsfc/index.ts b/lib/routes/gov/nsfc/index.ts index a5c06fbf0..cfc790803 100644 --- a/lib/routes/gov/nsfc/index.ts +++ b/lib/routes/gov/nsfc/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'; @@ -6,7 +7,14 @@ import { parseDate } from '@/utils/parse-date'; import shortcuts from './shortcuts'; -export default async (ctx) => { +export const route: Route = { + path: '/nsfc/*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { let thePath = getSubPath(ctx).replace(/^\/nsfc/, ''); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; @@ -71,7 +79,7 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { item: items, title: `国家自然科学基金委员会 - ${$('#ess_essBREADCRUMB_lblBreadCrumb a.break') .toArray() @@ -83,5 +91,5 @@ export default async (ctx) => { language: 'zh-cn', subtitle: $('meta[name="KEYWORDS"]').prop('content'), author: $('meta[name="AUTHOR"]').prop('content'), - }); -}; + }; +} diff --git a/lib/routes/gov/pbc/goutongjiaoliu.ts b/lib/routes/gov/pbc/goutongjiaoliu.ts index 8be2c5c3f..7ec8f915e 100644 --- a/lib/routes/gov/pbc/goutongjiaoliu.ts +++ b/lib/routes/gov/pbc/goutongjiaoliu.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 timezone from '@/utils/timezone'; import puppeteer from '@/utils/puppeteer'; -export default async (ctx) => { +export const route: Route = { + path: '/pbc/goutongjiaoliu', + categories: ['other'], + example: '/gov/pbc/goutongjiaoliu', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['pbc.gov.cn/goutongjiaoliu/113456/113469/index.html'], + }, + name: '沟通交流', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const link = 'http://www.pbc.gov.cn/goutongjiaoliu/113456/113469/index.html'; const browser = await puppeteer({ stealth: true }); @@ -52,9 +74,9 @@ export default async (ctx) => { browser.close(); - ctx.set('data', { + return { title: '中国人民银行 - 沟通交流', link, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/pbc/gzlw.ts b/lib/routes/gov/pbc/gzlw.ts index 8c4b823e0..d6f827d87 100644 --- a/lib/routes/gov/pbc/gzlw.ts +++ b/lib/routes/gov/pbc/gzlw.ts @@ -1,10 +1,32 @@ +import { Route } from '@/types'; import { processItems } from './utils'; import got from '@/utils/got'; import { load } from 'cheerio'; const host = 'http://www.pbc.gov.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/pbc/gzlw', + categories: ['other'], + example: '/gov/pbc/gzlw', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['pbc.gov.cn/redianzhuanti/118742/4122386/4122692/index.html'], + }, + name: '工作论文', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler() { const url = `${host}/redianzhuanti/118742/4122386/4122692/index.html`; const response = await got.post(url); @@ -19,9 +41,9 @@ export default async (ctx) => { const items = await processItems(list); - ctx.set('data', { + return { title: '中国人民银行 工作论文', link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/pbc/trade-announcement.ts b/lib/routes/gov/pbc/trade-announcement.ts index 131c032b2..9f922b18c 100644 --- a/lib/routes/gov/pbc/trade-announcement.ts +++ b/lib/routes/gov/pbc/trade-announcement.ts @@ -1,10 +1,29 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; import puppeteer from '@/utils/puppeteer'; -export default async (ctx) => { +export const route: Route = { + path: '/pbc/tradeAnnouncement', + categories: ['other'], + example: '/gov/pbc/tradeAnnouncement', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '货币政策司公开市场交易公告', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const link = 'http://www.pbc.gov.cn/zhengcehuobisi/125207/125213/125431/125475/index.html'; const browser = await puppeteer(); @@ -51,9 +70,9 @@ export default async (ctx) => { browser.close(); - ctx.set('data', { + return { title: '中国人民银行 - 货币政策司公开市场交易公告', link, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/pbc/zcyj.ts b/lib/routes/gov/pbc/zcyj.ts index 11b00aa59..e7d6f0aab 100644 --- a/lib/routes/gov/pbc/zcyj.ts +++ b/lib/routes/gov/pbc/zcyj.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { processItems } from './utils'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -6,7 +7,17 @@ import timezone from '@/utils/timezone'; const host = 'http://www.pbc.gov.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/pbc/zcyj', + radar: { + source: ['pbc.gov.cn/redianzhuanti/118742/4122386/4122510/index.html'], + }, + name: 'Unknown', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler() { const url = `${host}/redianzhuanti/118742/4122386/4122510/index.html`; const response = await got.post(url); @@ -21,9 +32,9 @@ export default async (ctx) => { const items = await processItems(list); - ctx.set('data', { + return { title: '中国人民银行 政策研究', link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/safe/business.ts b/lib/routes/gov/safe/business.ts index 032300e2f..d7bd98992 100644 --- a/lib/routes/gov/safe/business.ts +++ b/lib/routes/gov/safe/business.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import { processZxfkItems } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/safe/business/:site?', + categories: ['study'], + example: '/gov/safe/business/beijing', + parameters: { site: '站点,见上表,默认为 beijing' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '业务咨询', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const { site = 'beijing' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 3; ctx.set('data', await processZxfkItems(site, 'ywzx', limit)); -}; +} diff --git a/lib/routes/gov/safe/complaint.ts b/lib/routes/gov/safe/complaint.ts index fcd8ad622..887067d7d 100644 --- a/lib/routes/gov/safe/complaint.ts +++ b/lib/routes/gov/safe/complaint.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import { processZxfkItems } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/safe/complaint/:site?', + categories: ['study'], + example: '/gov/safe/complaint/beijing', + parameters: { site: '站点,见上表,默认为 beijing' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '投诉建议', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const { site = 'beijing' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 5; ctx.set('data', await processZxfkItems(site, 'tsjy', limit)); -}; +} diff --git a/lib/routes/gov/samr/xgzlyhd.ts b/lib/routes/gov/samr/xgzlyhd.ts index fd058f501..994f775c4 100644 --- a/lib/routes/gov/samr/xgzlyhd.ts +++ b/lib/routes/gov/samr/xgzlyhd.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -37,7 +38,28 @@ const getOption = async (type, name) => { return; }; -export default async (ctx) => { +export const route: Route = { + path: '/samr/xgzlyhd/:category?/:department?', + categories: ['study'], + example: '/gov/samr/xgzlyhd', + parameters: { category: '留言类型,见下表,默认为全部', department: '回复部门,见下表,默认为全部' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['xgzlyhd.samr.gov.cn/gjjly/index'], + }, + name: '留言咨询', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const { category, department } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 10; @@ -87,7 +109,7 @@ export default async (ctx) => { const subtitle = [categoryOption ? categoryOption.name : undefined, departmentOption ? departmentOption.name : undefined].filter(Boolean).join(' - '); const icon = new URL($('link[rel="icon"]').prop('href'), rootUrl).href; - ctx.set('data', { + return { item: items, title: `${author}${title}${subtitle ? ` - ${subtitle}` : ''}`, link: currentUrl, @@ -99,5 +121,5 @@ export default async (ctx) => { subtitle, author, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/gov/sasac/generic.ts b/lib/routes/gov/sasac/generic.ts index 460d8a630..461286810 100644 --- a/lib/routes/gov/sasac/generic.ts +++ b/lib/routes/gov/sasac/generic.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: '/sasac/:path{.+}', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const path = ctx.req.param('path'); const baseUrl = 'http://www.sasac.gov.cn'; const url = `${baseUrl}/${path}/index.html`; @@ -34,10 +42,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text().trim(), link: url, image: 'http://www.sasac.gov.cn/dbsource/11869722/11869731.jpg', item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/sdb/sdb.ts b/lib/routes/gov/sdb/sdb.ts index fe548a7a7..f34015a38 100644 --- a/lib/routes/gov/sdb/sdb.ts +++ b/lib/routes/gov/sdb/sdb.ts @@ -1,6 +1,14 @@ +import { Route } from '@/types'; import { gdgov } from '../general/general'; -export default async (ctx) => { +export const route: Route = { + path: '/sdb/*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const info = { defaultPath: 'zwgk/zcjd/', list_element: '.art-list li a', @@ -16,4 +24,4 @@ export default async (ctx) => { pubDate_format: undefined, }; await gdgov(info, ctx); -}; +} diff --git a/lib/routes/gov/shaanxi/kjt.ts b/lib/routes/gov/shaanxi/kjt.ts index f8e0aa9d7..1bdfb5a9a 100644 --- a/lib/routes/gov/shaanxi/kjt.ts +++ b/lib/routes/gov/shaanxi/kjt.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: '/shaanxi/kjt/:id?', + categories: ['study'], + example: '/gov/shaanxi/kjt', + 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') ?? '221'; const rootUrl = 'https://kjt.shaanxi.gov.cn'; @@ -48,9 +67,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `陕西省科学技术厅 - ${$('.catnm').text()}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/shanghai/rsj/ksxm.ts b/lib/routes/gov/shanghai/rsj/ksxm.ts index 60e9abfa1..57100b0f2 100644 --- a/lib/routes/gov/shanghai/rsj/ksxm.ts +++ b/lib/routes/gov/shanghai/rsj/ksxm.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 = 'http://www.rsj.sh.gov.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/shanghai/rsj/ksxm', + categories: ['study'], + example: '/gov/shanghai/rsj/ksxm', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['rsj.sh.gov.cn/'], + }, + name: '上海市职业能力考试院 考试项目', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler() { const url = `${rootUrl}/ksyzc/wangz/kwaplist_300.jsp`; const response = await got({ @@ -34,9 +56,9 @@ export default async (ctx) => { })) .get(); - ctx.set('data', { + return { title: '上海市职业能力考试院 - 考试项目', link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/shanghai/wgj/wgj.ts b/lib/routes/gov/shanghai/wgj/wgj.ts index f4d787366..2ec6a44a1 100644 --- a/lib/routes/gov/shanghai/wgj/wgj.ts +++ b/lib/routes/gov/shanghai/wgj/wgj.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: '/shanghai/wgj/:page?', + categories: ['study'], + example: '/gov/shanghai/wgj', + parameters: { page: '页数,默认第 1 页' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['wsbs.wgj.sh.gov.cn/'], + target: '/shanghai/wgj', + }, + name: '上海市文旅局审批公告', + maintainers: ['gideonsenku'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'http://wsbs.wgj.sh.gov.cn'; const currentUrl = `${baseUrl}/shwgj_ywtb/core/web/welcome/index!toResultNotice.action`; const page = ctx.req.param('page') ?? 1; @@ -68,9 +91,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/shanghai/wsjkw/yqtb/index.ts b/lib/routes/gov/shanghai/wsjkw/yqtb/index.ts index 9ec3a6d12..d1fe79fb9 100644 --- a/lib/routes/gov/shanghai/wsjkw/yqtb/index.ts +++ b/lib/routes/gov/shanghai/wsjkw/yqtb/index.ts @@ -1,14 +1,36 @@ +import { Route } from '@/types'; import { load } from 'cheerio'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/shanghai/wsjkw/yqtb', + categories: ['study'], + example: '/gov/shanghai/wsjkw/yqtb', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['wsjkw.sh.gov.cn/'], + }, + name: '上海卫健委 疫情通报', + maintainers: ['zcf0508'], + handler, +}; + +async function handler() { const url = `https://wsjkw.sh.gov.cn/yqtb/index.html`; const res = await got.get(url); const $ = load(res.data); const list = $('.uli16.nowrapli.list-date li'); - ctx.set('data', { + return { title: '疫情通报-上海卫健委', link: url, item: @@ -29,5 +51,5 @@ export default async (ctx) => { }; }) .get(), - }); -}; + }; +} diff --git a/lib/routes/gov/shanghai/yjj/index.ts b/lib/routes/gov/shanghai/yjj/index.ts index dc36e188b..d7725c06e 100644 --- a/lib/routes/gov/shanghai/yjj/index.ts +++ b/lib/routes/gov/shanghai/yjj/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: '/shanghai/yjj/*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const params = getSubPath(ctx) === '/shanghai/yjj' ? '/shanghai/yjj/zx-ylqx' : getSubPath(ctx); const rootUrl = 'https://yjj.sh.gov.cn'; @@ -56,9 +64,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text().replace(/--/, ' - '), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/shenzhen/hrss/szksy/index.ts b/lib/routes/gov/shenzhen/hrss/szksy/index.ts index 9c34b8279..de6e154d4 100644 --- a/lib/routes/gov/shenzhen/hrss/szksy/index.ts +++ b/lib/routes/gov/shenzhen/hrss/szksy/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'; @@ -5,7 +6,28 @@ import timezone from '@/utils/timezone'; const rootURL = 'http://hrss.sz.gov.cn/'; -export default async (ctx) => { +export const route: Route = { + path: '/shenzhen/hrss/szksy/:caty/:page?', + categories: ['study'], + example: '/gov/shenzhen/hrss/szksy/bmxx/2', + parameters: { caty: '信息类别', page: '页码' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['xxgk.sz.gov.cn/cn/xxgk/zfxxgj/:caty'], + }, + name: '深圳市考试院', + maintainers: ['zlasd'], + handler, +}; + +async function handler(ctx) { const categoryID = ctx.req.param('caty'); const page = ctx.req.param('page') ?? '1'; @@ -33,9 +55,9 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title: '深圳市考试院 - ' + title, link: currentURL.href, item: list, - }); -}; + }; +} diff --git a/lib/routes/gov/shenzhen/xxgk/zfxxgj.ts b/lib/routes/gov/shenzhen/xxgk/zfxxgj.ts index 52a54b992..9d9298600 100644 --- a/lib/routes/gov/shenzhen/xxgk/zfxxgj.ts +++ b/lib/routes/gov/shenzhen/xxgk/zfxxgj.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -24,7 +25,25 @@ const config = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/shenzhen/xxgk/zfxxgj/:caty', + categories: ['study'], + example: '/gov/shenzhen/xxgk/zfxxgj/tzgg', + parameters: { caty: '信息类别' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '深圳市人民政府', + maintainers: ['laoxua'], + handler, +}; + +async function handler(ctx) { const cfg = config[ctx.req.param('caty')]; if (!cfg) { throw new Error('Bad category. See docs'); @@ -65,9 +84,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '广东省深圳市人民政府 - ' + cfg.title, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/shenzhen/zjj/index.ts b/lib/routes/gov/shenzhen/zjj/index.ts index dd70b6e98..f730b8328 100644 --- a/lib/routes/gov/shenzhen/zjj/index.ts +++ b/lib/routes/gov/shenzhen/zjj/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'; @@ -10,7 +11,28 @@ const config = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/shenzhen/zjj/xxgk/:caty', + categories: ['study'], + example: '/gov/shenzhen/zjj/xxgk/tzgg', + parameters: { caty: '信息类别' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['zjj.sz.gov.cn/xxgk/:caty'], + }, + name: '深圳市住房和建设局', + maintainers: ['lonn'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'http://zjj.sz.gov.cn/xxgk/'; const cfg = config[ctx.req.param('caty')]; if (!cfg) { @@ -37,9 +59,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: '深圳市住房和建设局 - ' + cfg.title, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/shenzhen/zzb/index.ts b/lib/routes/gov/shenzhen/zzb/index.ts index 79ae7e7cb..72d8126cb 100644 --- a/lib/routes/gov/shenzhen/zzb/index.ts +++ b/lib/routes/gov/shenzhen/zzb/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'; @@ -5,7 +6,28 @@ import timezone from '@/utils/timezone'; const rootURL = 'http://www.zzb.sz.gov.cn/'; -export default async (ctx) => { +export const route: Route = { + path: '/shenzhen/zzb/:caty/:page?', + categories: ['study'], + example: '/gov/shenzhen/zzb/tzgg', + parameters: { caty: '信息类别', page: '页码' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['zzb.sz.gov.cn/*'], + }, + name: '深圳市委组织部', + maintainers: ['zlasd'], + handler, +}; + +async function handler(ctx) { const categoryID = ctx.req.param('caty'); const page = ctx.req.param('page') ?? '1'; @@ -32,9 +54,9 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title: '深圳组工在线 - ' + title, link: currentURL.href, item: list, - }); -}; + }; +} diff --git a/lib/routes/gov/sichuan/deyang/govpublicinfo.ts b/lib/routes/gov/sichuan/deyang/govpublicinfo.ts index 23c6ff01a..3b10ba51b 100644 --- a/lib/routes/gov/sichuan/deyang/govpublicinfo.ts +++ b/lib/routes/gov/sichuan/deyang/govpublicinfo.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -75,7 +76,25 @@ const getInfoContent = (rootUrl, item) => } }); -export default async (ctx) => { +export const route: Route = { + path: '/sichuan/deyang/govpublicinfo/:countyName/:infoType?', + categories: ['study'], + example: '/gov/sichuan/deyang/govpublicinfo/绵竹市', + parameters: { countyName: '区县名(**其他区县整改中,暂时只支持`绵竹市`**)。德阳市、绵竹市、广汉市、什邡市、中江县、罗江区、旌阳区、高新区', infoType: '信息类型。默认值:fdzdnr-“法定主动内容”' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '政府公开信息', + maintainers: ['zytomorrow'], + handler, +}; + +async function handler(ctx) { const countyName = ctx.req.param('countyName'); const infoType = ctx.req.param('infoType') || 'fdzdnr'; const infoBasicUrl = basicInfoDict[countyName].infoType[infoType].basicUrl; @@ -83,7 +102,7 @@ export default async (ctx) => { const infoUrlList = await getInfoUrlList(rootUrl, infoBasicUrl); const items = await Promise.all(infoUrlList.map((item) => getInfoContent(rootUrl, item))); - ctx.set('data', { + return { title: `政府公开信息-${countyName}-${basicInfoDict[countyName].infoType[infoType].name}`, link: infoBasicUrl, item: items.map((item) => ({ @@ -92,5 +111,5 @@ export default async (ctx) => { link: item.link, pubDate: item.pubDate, })), - }); -}; + }; +} diff --git a/lib/routes/gov/sichuan/deyang/mztoday.ts b/lib/routes/gov/sichuan/deyang/mztoday.ts index d8c3a0f1a..582f23b99 100644 --- a/lib/routes/gov/sichuan/deyang/mztoday.ts +++ b/lib/routes/gov/sichuan/deyang/mztoday.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -119,13 +120,35 @@ const getInfoContent = (item) => }; }); -export default async (ctx) => { +export const route: Route = { + path: '/sichuan/deyang/mztoday/:infoType?', + categories: ['study'], + example: '/gov/sichuan/deyang/mztoday/zx', + parameters: { infoType: '信息栏目名称。默认最新(zx)' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.mztoday.gov.cn/*'], + target: '/sichuan/deyang/mztoday', + }, + name: '今日绵竹', + maintainers: ['zytomorrow'], + handler, +}; + +async function handler(ctx) { const infoType = ctx.req.param('infoType') || 'zx'; const infoBasicUrl = `${rootUrl}${basicInfoDict[infoType].url}`; const infoUrlList = await getInfoUrlList(infoBasicUrl); const items = await Promise.all(infoUrlList.map((item) => getInfoContent(item))); - ctx.set('data', { + return { title: `今日绵竹-${basicInfoDict[infoType].name}`, link: `${infoBasicUrl}1`, item: items.map((item) => ({ @@ -134,5 +157,5 @@ export default async (ctx) => { link: item.link, pubDate: item.pubDate, })), - }); -}; + }; +} diff --git a/lib/routes/gov/stats/index.ts b/lib/routes/gov/stats/index.ts index 39f396215..4b5b8b8c0 100644 --- a/lib/routes/gov/stats/index.ts +++ b/lib/routes/gov/stats/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: '/stats/*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 15; const defaultPath = '/sj/zxfb/'; @@ -109,9 +117,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/suzhou/doc.ts b/lib/routes/gov/suzhou/doc.ts index 429b008d0..4dce09ee4 100644 --- a/lib/routes/gov/suzhou/doc.ts +++ b/lib/routes/gov/suzhou/doc.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: '/suzhou/doc', + categories: ['study'], + example: '/gov/suzhou/doc', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.suzhou.gov.cn/szxxgk/front/xxgk_right.jsp', 'www.suzhou.gov.cn/'], + }, + name: '政府信息公开文件', + maintainers: ['EsuRt'], + handler, +}; + +async function handler() { const link = 'https://www.suzhou.gov.cn/szxxgk/front/xxgk_right.jsp'; const { data: response } = await got(link); @@ -39,9 +61,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '苏州市政府 - 政策公开文件', link, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/suzhou/fg.ts b/lib/routes/gov/suzhou/fg.ts index a752c2fd0..e32c8c570 100644 --- a/lib/routes/gov/suzhou/fg.ts +++ b/lib/routes/gov/suzhou/fg.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: '/suzhou/fg/:category{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { category = 'szfgw/ggl/nav_list' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; @@ -50,7 +58,7 @@ export default async (ctx) => { const subtitle = $('meta[name="ColumnName"]').prop('content'); const image = new URL($('div.logo img').prop('src'), rootUrl).href; - ctx.set('data', { + return { item: items, title: `${author} - ${subtitle}`, link: currentUrl, @@ -59,5 +67,5 @@ export default async (ctx) => { image, subtitle, author, - }); -}; + }; +} diff --git a/lib/routes/gov/suzhou/news.ts b/lib/routes/gov/suzhou/news.ts index 3dd492c3d..4361e66ff 100644 --- a/lib/routes/gov/suzhou/news.ts +++ b/lib/routes/gov/suzhou/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 { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -export default async (ctx) => { +export const route: Route = { + path: '/suzhou/news/:uid', + categories: ['study'], + example: '/gov/suzhou/news/news', + parameters: { uid: '栏目名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.suzhou.gov.cn/szsrmzf/:uid/nav_list.shtml'], + }, + name: '政府新闻', + maintainers: ['EsuRt', 'luyuhuang'], + handler, +}; + +async function handler(ctx) { const rootUrl = 'https://www.suzhou.gov.cn'; const uid = ctx.req.param('uid'); let url = ''; @@ -109,9 +131,9 @@ export default async (ctx) => { }); } - ctx.set('data', { + return { title, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/taiyuan/rsj.ts b/lib/routes/gov/taiyuan/rsj.ts index 6167b1ff1..55a5d53d6 100644 --- a/lib/routes/gov/taiyuan/rsj.ts +++ b/lib/routes/gov/taiyuan/rsj.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 rootURL = 'http://rsj.taiyuan.gov.cn/'; -export default async (ctx) => { +export const route: Route = { + path: '/taiyuan/rsj/:caty/:page?', + categories: ['study'], + example: '/gov/taiyuan/rsj/gggs', + parameters: { caty: '信息类别', page: '页码' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['rsj.taiyuan.gov.cn/*'], + }, + name: '太原市人力资源和社会保障局政府公开信息', + maintainers: ['2PoL'], + handler, +}; + +async function handler(ctx) { const categoryID = ctx.req.param('caty'); const page = ctx.req.param('page') ?? '1'; @@ -33,9 +55,9 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title: '太原市人力资源和社会保障局 - ' + title, link: currentURL.href, item: list, - }); -}; + }; +} diff --git a/lib/routes/gov/wuhan/whyw.ts b/lib/routes/gov/wuhan/whyw.ts index 9f1eaa82d..13bb0373d 100644 --- a/lib/routes/gov/wuhan/whyw.ts +++ b/lib/routes/gov/wuhan/whyw.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: '/wuhan/sy/whyw', + categories: ['study'], + example: '/gov/wuhan/sy/whyw', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['wuhan.gov.cn/sy/whyw/', 'wuhan.gov.cn/whyw', 'wuhan.gov.cn/'], + }, + name: '武汉要闻', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'http://www.wuhan.gov.cn'; const currentUrl = `${rootUrl}/sy/whyw/`; const response = await got(currentUrl); @@ -37,9 +59,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('h2').text()} - 武汉市人民政府`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/xinyi/xinyi.ts b/lib/routes/gov/xinyi/xinyi.ts index 680a04c63..b79835f67 100644 --- a/lib/routes/gov/xinyi/xinyi.ts +++ b/lib/routes/gov/xinyi/xinyi.ts @@ -1,6 +1,14 @@ +import { Route } from '@/types'; import { gdgov } from '../general/general'; -export default async (ctx) => { +export const route: Route = { + path: '/xinyi/*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const info = { defaultPath: 'zwgk/zcjd/', list_element: '.newsList li a', @@ -16,4 +24,4 @@ export default async (ctx) => { pubDate_format: undefined, }; await gdgov(info, ctx); -}; +} diff --git a/lib/routes/gov/xuzhou/hrss.ts b/lib/routes/gov/xuzhou/hrss.ts index d68d23eee..abfd64762 100644 --- a/lib/routes/gov/xuzhou/hrss.ts +++ b/lib/routes/gov/xuzhou/hrss.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: '/xuzhou/hrss/:category?', + categories: ['study'], + example: '/gov/xuzhou/hrss', + 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://hrss.xz.gov.cn'; @@ -47,9 +66,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `徐州市人力资源和社会保障局 - ${category ? $('.wb-tree-items.current').text() : '通知公告'}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/zhejiang/gwy.ts b/lib/routes/gov/zhejiang/gwy.ts index b2367dec4..61a06a287 100644 --- a/lib/routes/gov/zhejiang/gwy.ts +++ b/lib/routes/gov/zhejiang/gwy.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: '/zhejiang/gwy/:category?/:column?', + categories: ['study'], + example: '/gov/zhejiang/gwy/1', + parameters: { category: '分类,见下表,默认为全部', column: '地市专栏,见下表,默认为全部' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['zjks.gov.cn/zjgwy/website/init.htm', 'zjks.gov.cn/zjgwy/website/queryDetail.htm', 'zjks.gov.cn/zjgwy/website/queryMore.htm'], + target: '/zhejiang/gwy', + }, + name: '通知', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const { category, column } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 50; @@ -73,7 +96,7 @@ export default async (ctx) => { const columnName = $('button.btn-success').last().text(); const categoryName = $('table').parent().prev().find('h5').text(); - ctx.set('data', { + return { item: items, title: `${$('title').text()} - ${columnName}${categoryName}`, link: currentUrl, @@ -81,5 +104,5 @@ export default async (ctx) => { subtitle: `${columnName}${categoryName}`, author: $('div.title-font').text(), allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/gov/zhengce/govall.ts b/lib/routes/gov/zhengce/govall.ts index 0ed51e36b..35ac81e2e 100644 --- a/lib/routes/gov/zhengce/govall.ts +++ b/lib/routes/gov/zhengce/govall.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: '/zhengce/govall/:advance?', + categories: ['study'], + example: '/gov/zhengce/govall/orpro=555¬pro=2&search_field=title', + parameters: { advance: '高级搜索选项,将作为请求参数直接添加到url后。目前已知的选项及其意义如下。' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.gov.cn/'], + target: '/zhengce/govall', + }, + name: '信息稿件', + maintainers: ['ciaranchen'], + handler, +}; + +async function handler(ctx) { const advance = ctx.req.param('advance'); const link = `http://sousuo.gov.cn/list.htm`; const params = new URLSearchParams({ @@ -48,9 +71,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '信息稿件 - 中国政府网', link: `${link}?${query}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/zhengce/index.ts b/lib/routes/gov/zhengce/index.ts index eb1099123..ab9c9b014 100644 --- a/lib/routes/gov/zhengce/index.ts +++ b/lib/routes/gov/zhengce/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: ['/zhengce/zuixin', '/zhengce/:category{.+}?'], + categories: ['study'], + example: '/gov/zhengce/zuixin', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.gov.cn/zhengce/zuixin.htm', 'www.gov.cn/'], + }, + name: '最新政策', + maintainers: ['SettingDust', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const { category = 'zuixin' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 20; @@ -85,7 +107,7 @@ export default async (ctx) => { const subtitle = $('meta[name="lanmu"]').prop('content'); const author = $('div.header_logo a[aria-label]').prop('aria-label'); - ctx.set('data', { + return { item: items, title: author && subtitle ? `${author} - ${subtitle}` : $('title').text(), link: currentUrl, @@ -96,5 +118,5 @@ export default async (ctx) => { logo: icon, subtitle, author, - }); -}; + }; +} diff --git a/lib/routes/gov/zhengce/wenjian.ts b/lib/routes/gov/zhengce/wenjian.ts index f4eecb529..c249e7b3d 100644 --- a/lib/routes/gov/zhengce/wenjian.ts +++ b/lib/routes/gov/zhengce/wenjian.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: '/zhengce/wenjian/:pcodeJiguan?', + categories: ['study'], + example: '/gov/zhengce/wenjian', + parameters: { pcodeJiguan: '文种分类。国令、国发、国函、国发明电、国办发、国办函、国办发明电、其他' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.gov.cn/'], + target: '/zhengce/wenjian', + }, + name: '最新文件', + maintainers: ['ciaranchen'], + handler, +}; + +async function handler(ctx) { const pcodeJiguan = ctx.req.param('pcodeJiguan'); const link = 'http://sousuo.gov.cn/list.htm'; const res = await got(link, { @@ -40,9 +63,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '最新文件 - 中国政府网', link: res.url, item: items, - }); -}; + }; +} diff --git a/lib/routes/gov/zhengce/zhengceku.ts b/lib/routes/gov/zhengce/zhengceku.ts index 074437185..8f0b80c22 100644 --- a/lib/routes/gov/zhengce/zhengceku.ts +++ b/lib/routes/gov/zhengce/zhengceku.ts @@ -1,6 +1,25 @@ +import { Route } from '@/types'; import buildData from '@/utils/common-config'; -export default async (ctx) => { +export const route: Route = { + path: '/zhengce/zhengceku/:department', + categories: ['study'], + example: '/gov/zhengce/zhengceku/bmwj', + parameters: { department: '库名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '国务院政策文件库', + maintainers: ['zxx-457'], + handler, +}; + +async function handler(ctx) { const department = ctx.req.param('department'); const link = `http://www.gov.cn/zhengce/zhengceku/${department}/`; @@ -22,4 +41,4 @@ export default async (ctx) => { }, }) ); -}; +} diff --git a/lib/routes/greasyfork/feedback.ts b/lib/routes/greasyfork/feedback.ts index f625e936b..7e8da0266 100644 --- a/lib/routes/greasyfork/feedback.ts +++ b/lib/routes/greasyfork/feedback.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: '/scripts/:script/feedback', + categories: ['university'], + example: '/greasyfork/scripts/431691-bypass-all-shortlinks/feedback', + parameters: { script: 'Script id, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['greasyfork.org/:language/scripts/:script/feedback'], + }, + name: 'Script Feedback', + maintainers: ['miles170'], + handler, +}; + +async function handler(ctx) { const script = ctx.req.param('script'); const rootUrl = 'https://greasyfork.org'; const currentUrl = `${rootUrl}/scripts/${script}/feedback`; const response = await got(currentUrl); const $ = load(response.data); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, description: $('meta[name=description]').attr('content'), @@ -27,5 +49,5 @@ export default async (ctx) => { link: rootUrl + discussionTitle.attr('href'), }; }), - }); -}; + }; +} diff --git a/lib/routes/greasyfork/scripts.ts b/lib/routes/greasyfork/scripts.ts index 8812d0595..2d5399cdb 100644 --- a/lib/routes/greasyfork/scripts.ts +++ b/lib/routes/greasyfork/scripts.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: ['/:language/:domain?', '/scripts/sort/:sort/:language?'], + categories: ['university'], + example: '/greasyfork/en/google.com', + parameters: { language: "language, located on the top right corner of Greasy Fork's search page, set to `all` for including all languages", domain: "the script's target domain" }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['greasyfork.org/:language', 'greasyfork.org/:language/scripts/by-site/:domain?'], + }, + name: 'Script Update', + maintainers: ['imlonghao', 'miles170'], + handler, +}; + +async function handler(ctx) { const language = ctx.req.param('language') === 'all' ? 'zh-CN' : ctx.req.param('language') || 'zh-CN'; const domain = ctx.req.param('domain'); const filter_locale = ctx.req.param('language') === 'all' ? 0 : 1; @@ -20,7 +42,7 @@ export default async (ctx) => { const $ = load(res.data); const list = $('.script-list').find('article'); - ctx.set('data', { + return { title: $('title').first().text(), link: currentUrl, description: $('meta[name=description]').attr('content'), @@ -40,5 +62,5 @@ export default async (ctx) => { .join(', '), }; }), - }); -}; + }; +} diff --git a/lib/routes/greasyfork/versions.ts b/lib/routes/greasyfork/versions.ts index 52eb6df63..cecdbbc83 100644 --- a/lib/routes/greasyfork/versions.ts +++ b/lib/routes/greasyfork/versions.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: '/scripts/:script/versions', + categories: ['university'], + example: '/greasyfork/scripts/431691-bypass-all-shortlinks/versions', + parameters: { script: 'Script id, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['greasyfork.org/:language/scripts/:script/versions'], + }, + name: 'Script Version History', + maintainers: ['miles170'], + handler, +}; + +async function handler(ctx) { const script = ctx.req.param('script'); const currentUrl = `https://greasyfork.org/scripts/${script}/versions`; const response = await got(currentUrl); const $ = load(response.data); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, description: $('meta[name=description]').attr('content'), @@ -25,5 +47,5 @@ export default async (ctx) => { link: versionNumberLink.attr('href'), }; }), - }); -}; + }; +} diff --git a/lib/routes/grist/featured.ts b/lib/routes/grist/featured.ts index 5352e8eb0..8c40946a5 100644 --- a/lib/routes/grist/featured.ts +++ b/lib/routes/grist/featured.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { getData, getList } from './utils'; import got from '@/utils/got'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/featured', + categories: ['traditional-media'], + example: '/grist/featured', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['grist.org/'], + }, + name: 'Featured', + maintainers: ['Rjnishant530'], + handler, +}; + +async function handler() { const baseUrl = 'https://grist.org/'; const { data: response } = await got(baseUrl); const $ = load(response); @@ -20,7 +42,7 @@ export default async (ctx) => { const itemData = await Promise.all(listItems.map((item) => cache.tryGet(item.link, async () => (await getData(`https://grist.org/wp-json/wp/v2/posts?slug='${item.link}'&_embed`))[0]))); const items = await getList(itemData); - ctx.set('data', { + return { title: 'Gist Featured Articles', link: baseUrl, item: items, @@ -28,5 +50,5 @@ export default async (ctx) => { logo: 'https://grist.org/wp-content/uploads/2021/03/cropped-Grist-Favicon.png?w=192', icon: 'https://grist.org/wp-content/uploads/2021/03/cropped-Grist-Favicon.png?w=32', language: 'en-us', - }); -}; + }; +} diff --git a/lib/routes/grist/index.ts b/lib/routes/grist/index.ts index a48cdb742..18027f38e 100644 --- a/lib/routes/grist/index.ts +++ b/lib/routes/grist/index.ts @@ -1,13 +1,24 @@ +import { Route } from '@/types'; import { getData, getList } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['grist.org/articles/'], + }, + name: 'Unknown', + maintainers: ['Rjnishant530'], + handler, +}; + +async function handler() { const baseUrl = 'https://grist.org'; const route = '/wp-json/wp/v2/posts?_embed'; const data = await getData(`${baseUrl}${route}`); const items = await getList(data); - ctx.set('data', { + return { title: 'Gist Latest Articles', link: `${baseUrl}/articles`, item: items, @@ -15,5 +26,5 @@ export default async (ctx) => { logo: 'https://grist.org/wp-content/uploads/2021/03/cropped-Grist-Favicon.png?w=192', icon: 'https://grist.org/wp-content/uploads/2021/03/cropped-Grist-Favicon.png?w=32', language: 'en-us', - }); -}; + }; +} diff --git a/lib/routes/grist/series.ts b/lib/routes/grist/series.ts index 4f0450b9b..61ef7b360 100644 --- a/lib/routes/grist/series.ts +++ b/lib/routes/grist/series.ts @@ -1,6 +1,28 @@ +import { Route } from '@/types'; import { getData, getList } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/series/:series', + categories: ['traditional-media'], + example: '/grist/series/best-of-grist', + parameters: { series: 'Find in the URL which has /series/' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['grist.org/series/:series'], + }, + name: 'Series', + maintainers: ['Rjnishant530'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://grist.org'; const searchRoute = '/wp-json/wp/v2/series?slug='; const articleRoute = '/wp-json/wp/v2/posts?series='; @@ -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: `${series[0].toUpperCase() + series.slice(1)} - Gist Articles`, link: `${baseUrl}/${series}`, item: items, @@ -17,5 +39,5 @@ export default async (ctx) => { logo: 'https://grist.org/wp-content/uploads/2021/03/cropped-Grist-Favicon.png?w=192', icon: 'https://grist.org/wp-content/uploads/2021/03/cropped-Grist-Favicon.png?w=32', language: 'en-us', - }); -}; + }; +} diff --git a/lib/routes/grist/topic.ts b/lib/routes/grist/topic.ts index 177d57662..9cfcdcf4d 100644 --- a/lib/routes/grist/topic.ts +++ b/lib/routes/grist/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/:topic', + categories: ['traditional-media'], + example: '/grist/topic/extreme-heat', + parameters: { topic: 'Any Topic from Table below' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['grist.org/:topic'], + }, + name: 'Topic', + maintainers: ['Rjnishant530'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://grist.org'; const searchRoute = '/wp-json/wp/v2/categories?slug='; const articleRoute = '/wp-json/wp/v2/posts?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: `${topic[0].toUpperCase() + topic.slice(1)} - Gist Articles`, link: `${baseUrl}/${topic}`, item: items, @@ -17,5 +39,5 @@ export default async (ctx) => { logo: 'https://grist.org/wp-content/uploads/2021/03/cropped-Grist-Favicon.png?w=192', icon: 'https://grist.org/wp-content/uploads/2021/03/cropped-Grist-Favicon.png?w=32', language: 'en-us', - }); -}; + }; +} diff --git a/lib/routes/grubstreet/index.ts b/lib/routes/grubstreet/index.ts index f3ae2d141..4fcbe8e36 100644 --- a/lib/routes/grubstreet/index.ts +++ b/lib/routes/grubstreet/index.ts @@ -1,9 +1,21 @@ +import { Route } from '@/types'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['grubstreet.com/'], + target: '', + }, + name: 'Unknown', + maintainers: ['loganrockmore'], + handler, +}; + +async function handler(ctx) { const url = `https://www.grubstreet.com/_components/newsfeed/instances/grubstreet-index@published`; const title = `Grub Street`; const description = `New York Magazine's Food and Restaurant Blog`; ctx.set('data', await utils.getData(ctx, url, title, description)); -}; +} diff --git a/lib/routes/guancha/headline.ts b/lib/routes/guancha/headline.ts index 4a71e98ca..5f62d50fe 100644 --- a/lib/routes/guancha/headline.ts +++ b/lib/routes/guancha/headline.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: '/headline', + categories: ['traditional-media'], + example: '/guancha/headline', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['guancha.cn/GuanChaZheTouTiao', 'guancha.cn/'], + }, + name: '头条', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://www.guancha.cn'; const currentUrl = `${rootUrl}/GuanChaZheTouTiao/list_1.shtml`; @@ -44,9 +66,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '观察者网 - 头条', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/guancha/index.ts b/lib/routes/guancha/index.ts index 6f282dee0..c07e6997b 100644 --- a/lib/routes/guancha/index.ts +++ b/lib/routes/guancha/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -34,7 +35,28 @@ const config = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + categories: ['traditional-media'], + example: '/guancha', + parameters: { category: '分类,见下表,默认为全部' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['guancha.cn/'], + }, + name: '首页', + maintainers: ['nczitzk', 'Jeason0228'], + handler, +}; + +async function handler(ctx) { const total = 10; const category = ctx.req.param('category') ?? 'all'; const rootUrl = 'https://www.guancha.cn'; @@ -145,9 +167,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `观察者网 - ${config[category].title}`, link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/guancha/member.ts b/lib/routes/guancha/member.ts index c79a7189f..14753406c 100644 --- a/lib/routes/guancha/member.ts +++ b/lib/routes/guancha/member.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; @@ -9,7 +10,29 @@ const titles = { huodongs: '观学院', }; -export default async (ctx) => { +export const route: Route = { + path: '/member/:category?', + categories: ['traditional-media'], + example: '/guancha/member/recommend', + parameters: { category: '分类,见下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['guancha.cn/'], + target: '/:category?', + }, + name: '观学院', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'recommend'; const rootUrl = 'https://member.guancha.cn'; @@ -78,9 +101,9 @@ export default async (ctx) => { }); } - ctx.set('data', { + return { title: `观学院 - ${titles[category]}`, link: `${rootUrl}/index.html`, item: items, - }); -}; + }; +} diff --git a/lib/routes/guancha/personalpage.ts b/lib/routes/guancha/personalpage.ts index 7f20c4117..f8b10c5fd 100644 --- a/lib/routes/guancha/personalpage.ts +++ b/lib/routes/guancha/personalpage.ts @@ -1,6 +1,25 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/personalpage/:uid', + categories: ['traditional-media'], + example: '/guancha/personalpage/243983', + parameters: { uid: '用户id, 可在URL中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '个人主页文章', + maintainers: ['Jeason0228'], + handler, +}; + +async function handler(ctx) { const uid = ctx.req.param('uid'); const host = 'https://user.guancha.cn'; const link = `https://app.guancha.cn/user/get-published-list?page_size=20&page_no=1&uid=${uid}`; @@ -57,7 +76,7 @@ export default async (ctx) => { } return time; } - ctx.set('data', { + return { title: `${user_nick}-观察者-风闻社区`, link, description: `${user_nick} 的个人主页`, @@ -68,5 +87,5 @@ export default async (ctx) => { link: `https://user.guancha.cn/main/content?id=${item.id}`, author: item.user_nick, })), - }); -}; + }; +} diff --git a/lib/routes/guancha/topic.ts b/lib/routes/guancha/topic.ts index 857547ef6..60ff65588 100644 --- a/lib/routes/guancha/topic.ts +++ b/lib/routes/guancha/topic.ts @@ -1,9 +1,21 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseRelativeDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/topic/:id/:order?', + radar: { + source: ['guancha.cn/'], + target: '/:category?', + }, + name: 'Unknown', + maintainers: ['occupy5', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? '0'; const order = ctx.req.param('order') ?? '1'; @@ -49,9 +61,9 @@ export default async (ctx) => { $('h1.title span').remove(); - ctx.set('data', { + return { title: `观察者网 - ${id === '0' ? '风闻' : $('h1.title').text()}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/guangdiu/cheaps.ts b/lib/routes/guangdiu/cheaps.ts index bcf1cf31b..15377dfc2 100644 --- a/lib/routes/guangdiu/cheaps.ts +++ b/lib/routes/guangdiu/cheaps.ts @@ -1,10 +1,29 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseRelativeDate } from '@/utils/parse-date'; const host = 'https://guangdiu.com'; -export default async (ctx) => { +export const route: Route = { + path: '/cheaps/:query?', + categories: ['game'], + example: '/guangdiu/cheaps/k=clothes', + parameters: { query: '链接参数,对应网址问号后的内容' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '九块九', + maintainers: ['fatpandac'], + handler, +}; + +async function handler(ctx) { const query = ctx.req.param('query') ?? ''; const url = `${host}/cheaps.php${query ? `?${query}` : ''}`; @@ -20,9 +39,9 @@ export default async (ctx) => { })) .get(); - ctx.set('data', { + return { title: `逛丢 - 九块九`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/guangdiu/index.ts b/lib/routes/guangdiu/index.ts index 295a429d5..35c3c4e66 100644 --- a/lib/routes/guangdiu/index.ts +++ b/lib/routes/guangdiu/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 { parseRelativeDate } from '@/utils/parse-date'; const host = 'https://guangdiu.com'; -export default async (ctx) => { +export const route: Route = { + path: '/:query?', + categories: ['game'], + example: '/guangdiu/k=daily', + parameters: { query: '链接参数,对应网址问号后的内容' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '国内折扣 / 海外折扣', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const query = ctx.req.param('query') ?? ''; const url = query === 'c=us' ? `${host}/?c=us` : `${host}/${query ? `cate.php?${query}` : ''}`; @@ -32,9 +51,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `逛丢 - ${query.includes('c=us') ? '海外' : '国内'}`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/guangdiu/rank.ts b/lib/routes/guangdiu/rank.ts index 55b3238ab..18d449ed5 100644 --- a/lib/routes/guangdiu/rank.ts +++ b/lib/routes/guangdiu/rank.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 { parseRelativeDate } from '@/utils/parse-date'; const host = 'https://guangdiu.com'; -export default async (ctx) => { +export const route: Route = { + path: '/rank', + categories: ['game'], + example: '/guangdiu/rank', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['guangdiu.com/rank'], + }, + name: '一小时风云榜', + maintainers: ['fatpandac'], + handler, +}; + +async function handler() { const url = `${host}/rank.php`; const response = await got(url); @@ -31,9 +53,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `逛丢 - 一小时风云榜`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/guangdiu/search.ts b/lib/routes/guangdiu/search.ts index 3a8b6d1b0..45afeddab 100644 --- a/lib/routes/guangdiu/search.ts +++ b/lib/routes/guangdiu/search.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 { parseRelativeDate } from '@/utils/parse-date'; const host = 'https://guangdiu.com'; -export default async (ctx) => { +export const route: Route = { + path: '/search/:query?', + categories: ['game'], + example: '/guangdiu/search/k=百度网盘', + parameters: { query: '链接参数,对应网址问号后的内容' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '关键字搜索', + maintainers: ['Huzhixin00'], + handler, +}; + +async function handler(ctx) { const query = ctx.req.param('query') ?? ''; const url = `${host}/${query ? `search.php?${query}` : ''}`; const response = await got(url); @@ -33,9 +52,9 @@ export default async (ctx) => { const match = /q=(.+)/.exec(query); - ctx.set('data', { + return { title: `逛丢 - ${match[1]}`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/guangzhoumetro/news.ts b/lib/routes/guangzhoumetro/news.ts index 637c0047f..99cc85d8e 100644 --- a/lib/routes/guangzhoumetro/news.ts +++ b/lib/routes/guangzhoumetro/news.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: '/news', + categories: ['shopping'], + example: '/guangzhoumetro/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '新闻', + maintainers: ['HankChow'], + handler, +}; + +async function handler() { const newsUrl = 'https://www.gzmtr.com/ygwm/xwzx/gsxw/'; const response = await got(newsUrl); const data = response.data; @@ -23,7 +42,7 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title: '广州地铁新闻', url: newsUrl, description: '广州地铁新闻', @@ -33,5 +52,5 @@ export default async (ctx) => { link: item.link, author: item.author, })), - }); -}; + }; +} diff --git a/lib/routes/guanhai/index.ts b/lib/routes/guanhai/index.ts index 506ccc9aa..313d7d4b9 100644 --- a/lib/routes/guanhai/index.ts +++ b/lib/routes/guanhai/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 timezone from '@/utils/timezone'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['guanhai.com.cn/'], + target: '', + }, + name: 'Unknown', + maintainers: ['TonyRL'], + handler, +}; + +async function handler() { const { data: response } = await got('https://www.guanhai.com.cn'); const $ = load(response); @@ -45,11 +57,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), description: $('head meta[name=description]').text(), image: 'https://www.guanhai.com.cn/favicon.ico', link: 'https://www.guanhai.com.cn', item: items, - }); -}; + }; +} diff --git a/lib/routes/guduodata/daily.ts b/lib/routes/guduodata/daily.ts index 334e6cf20..9714594e5 100644 --- a/lib/routes/guduodata/daily.ts +++ b/lib/routes/guduodata/daily.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -30,7 +31,28 @@ const types = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/daily', + categories: ['other'], + example: '/guduodata/daily', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['data.guduodata.com/'], + }, + name: '日榜', + maintainers: ['Gem1ni'], + handler, +}; + +async function handler() { const now = dayjs().valueOf(); // yestoday const yestoday = dayjs().subtract(1, 'day').format('YYYY-MM-DD'); @@ -43,7 +65,7 @@ export default async (ctx) => { url: `${host}/show/datalist?type=DAILY&category=${category.toUpperCase()}&date=${yestoday}`, })) ); - ctx.set('data', { + return { title: `骨朵数据 - 日榜`, link: host, description: yestoday, @@ -63,5 +85,5 @@ export default async (ctx) => { }) ) ), - }); -}; + }; +} diff --git a/lib/routes/gumroad/index.ts b/lib/routes/gumroad/index.ts index cab880e15..a1cec8c69 100644 --- a/lib/routes/gumroad/index.ts +++ b/lib/routes/gumroad/index.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 { isValidHost } from '@/utils/valid-host'; -export default async (ctx) => { +export const route: Route = { + path: '/:username/:products', + categories: ['game'], + example: '/gumroad/afkmaster/Eve10', + parameters: { username: 'username, can be found in URL', products: 'products name, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Products', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const username = ctx.req.param('username'); const products = ctx.req.param('products'); if (!isValidHost(username)) { @@ -34,9 +53,9 @@ export default async (ctx) => { }, ]; - ctx.set('data', { + return { link: url, title: `Gumroad - ${userFullName}/${title}`, item, - }); -}; + }; +} diff --git a/lib/routes/guokr/channel.ts b/lib/routes/guokr/channel.ts index 9d1ce8495..048a6c5f6 100644 --- a/lib/routes/guokr/channel.ts +++ b/lib/routes/guokr/channel.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; @@ -17,7 +18,28 @@ const channelMap = { pretty: 'beauty', }; -export default async (ctx) => { +export const route: Route = { + path: '/:channel', + categories: ['traditional-media'], + example: '/guokr/calendar', + parameters: { channel: '专栏类别' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['guokr.com/'], + }, + name: '果壳网专栏', + maintainers: ['DHPO', 'hoilc'], + handler, +}; + +async function handler(ctx) { const channel = channelMap[ctx.req.param('channel')] ?? ctx.req.param('channel'); const response = await got(`https://www.guokr.com/apis/minisite/article.json?retrieve_type=by_wx&channel_key=${channel}&offset=0&limit=10`); @@ -40,9 +62,9 @@ export default async (ctx) => { })) ); - ctx.set('data', { + return { title: `果壳网 ${channel_name}`, link: channel_url, item: result, - }); -}; + }; +} diff --git a/lib/routes/guokr/scientific.ts b/lib/routes/guokr/scientific.ts index c8784c505..c5ca6c7d0 100644 --- a/lib/routes/guokr/scientific.ts +++ b/lib/routes/guokr/scientific.ts @@ -1,13 +1,35 @@ +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: '/scientific', + categories: ['traditional-media'], + example: '/guokr/scientific', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['guokr.com/scientific', 'guokr.com/'], + }, + name: '科学人', + maintainers: ['alphardex', 'nczitzk'], + handler, +}; + +async function handler() { const response = await got('https://www.guokr.com/apis/minisite/article.json?retrieve_type=by_subject&limit=20&offset=0'); const result = response.data.result; - ctx.set('data', { + return { title: '果壳网 科学人', link: 'https://www.guokr.com/scientific', description: '果壳网 科学人', @@ -27,5 +49,5 @@ export default async (ctx) => { }) ) ), - }); -}; + }; +} diff --git a/lib/routes/gxmzu/ai.ts b/lib/routes/gxmzu/ai.ts index 246f3ba88..d36819853 100644 --- a/lib/routes/gxmzu/ai.ts +++ b/lib/routes/gxmzu/ai.ts @@ -1,18 +1,40 @@ +import { Route } from '@/types'; import { getNoticeList } from './utils'; const url = 'https://ai.gxmzu.edu.cn/index/tzgg.htm'; const host = 'https://ai.gxmzu.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/aitzgg', + categories: ['forecast'], + example: '/gxmzu/aitzgg', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ai.gxmzu.edu.cn/index/tzgg.htm', 'ai.gxmzu.edu.cn/'], + }, + name: '人工智能学院通知公告', + maintainers: ['real-jiakai'], + handler, +}; + +async function handler(ctx) { const out = await getNoticeList(ctx, url, host, 'a', '.timestyle55267', { title: '.titlestyle55269', content: '#vsb_content', date: '.timestyle55269', }); - ctx.set('data', { + return { title: '广西民族大学人工智能学院 -- 通知公告', link: url, item: out, - }); -}; + }; +} diff --git a/lib/routes/gxmzu/lib.ts b/lib/routes/gxmzu/lib.ts index 5e299e876..2dd1eede5 100644 --- a/lib/routes/gxmzu/lib.ts +++ b/lib/routes/gxmzu/lib.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; // 导入got库,该库用来请求网页数据 import got from '@/utils/got'; @@ -13,7 +14,28 @@ const url = 'https://library.gxmzu.edu.cn/news/news_list.jsp?urltype=tree.TreeTe // 广西民大图书馆网址 const host = 'https://library.gxmzu.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/libzxxx', + categories: ['forecast'], + example: '/gxmzu/libzxxx', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['library.gxmzu.edu.cn/news/news_list.jsp', 'library.gxmzu.edu.cn/'], + }, + name: '图书馆最新消息', + maintainers: ['real-jiakai'], + handler, +}; + +async function handler() { // 发起Http请求,获取网页数据 const response = await got(url); @@ -60,12 +82,12 @@ export default async (ctx) => { ); // 生成RSS源 - ctx.set('data', { + return { // 项目标题 title: '广西民族大学图书馆 -- 最新消息', // 项目链接 link: url, // items的内容 item: out, - }); -}; + }; +} diff --git a/lib/routes/gxmzu/yjs.ts b/lib/routes/gxmzu/yjs.ts index c42a91e92..2816031d6 100644 --- a/lib/routes/gxmzu/yjs.ts +++ b/lib/routes/gxmzu/yjs.ts @@ -1,18 +1,40 @@ +import { Route } from '@/types'; import { getNoticeList } from './utils'; const url = 'https://yjs.gxmzu.edu.cn/tzgg/zsgg.htm'; const host = 'https://yjs.gxmzu.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/yjszsgg', + categories: ['forecast'], + example: '/gxmzu/yjszsgg', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['yjs.gxmzu.edu.cn/tzgg/zsgg.htm', 'yjs.gxmzu.edu.cn/'], + }, + name: '研究生院招生公告', + maintainers: ['real-jiakai'], + handler, +}; + +async function handler(ctx) { const out = await getNoticeList(ctx, url, host, 'a', '.timestyle55267', { title: '.titlestyle55269', content: '#vsb_content', date: '.timestyle55269', }); - ctx.set('data', { + return { title: '广西民族大学研究生院 -- 招生公告', link: url, item: out, - }); -}; + }; +} diff --git a/lib/routes/gzdaily/app.ts b/lib/routes/gzdaily/app.ts index 526955c70..e4bcafb17 100644 --- a/lib/routes/gzdaily/app.ts +++ b/lib/routes/gzdaily/app.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: '/app/:column?', + categories: ['bbs'], + example: '/gzdaily/app/74', + parameters: { column: '栏目 ID,点击对应栏目后在地址栏找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '客户端', + maintainers: ['TimWu007'], + handler, +}; + +async function handler(ctx) { const column = ctx.req.param('column') ?? 74; const currentUrl = `https://app.gzdaily.cn/app_if/getArticles?columnId=${column}&page=1`; @@ -49,9 +68,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `广州日报客户端 - ${colName}`, link: `https://www.gzdaily.cn/amucsite/web/index.html#/home/${column}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/gzhu/yjs.ts b/lib/routes/gzhu/yjs.ts index 39012a6af..947cbcbba 100644 --- a/lib/routes/gzhu/yjs.ts +++ b/lib/routes/gzhu/yjs.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: '/yjs', + categories: ['forecast'], + example: '/gzhu/yjs', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['yjsy.gzhu.edu.cn/zsxx/zsdt/zsdt.htm', 'yjsy.gzhu.edu.cn/'], + }, + name: '研究生院招生动态', + maintainers: ['shengmaosu'], + handler, +}; + +async function handler() { const link = 'https://yjsy.gzhu.edu.cn/zsxx/zsdt/zsdt.htm'; const response = await got(link, { https: { @@ -12,7 +34,7 @@ export default async (ctx) => { const $ = load(response.data); const list = $('.picnews_cont li'); - ctx.set('data', { + return { title: '广州大学研究生院', link, description: '广州大学研招网通知公告', @@ -27,5 +49,5 @@ export default async (ctx) => { pubDate: parseDate(a.text(), 'YYYY-MM-DD'), }; }), - }); -}; + }; +} diff --git a/lib/routes/hackernews/index.ts b/lib/routes/hackernews/index.ts index 46c8a6335..63c2632e5 100644 --- a/lib/routes/hackernews/index.ts +++ b/lib/routes/hackernews/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: '/:section?/:type?/:user?', + categories: ['design'], + example: '/hackernews/threads/comments_list/dang', + parameters: { section: '内容分区,见上表,默认为 `index`', type: '链接类型,见上表,默认为 `sources`', user: '设定用户,只在 `threads` 和 `submitted` 分区有效' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ycombinator.com/:section', 'ycombinator.com/'], + }, + name: '用户', + maintainers: ['nczitzk', 'xie-dongping'], + handler, +}; + +async function handler(ctx) { const section = ctx.req.param('section') ?? 'index'; const type = ctx.req.param('type') ?? 'sources'; const user = ctx.req.param('user') ?? ''; @@ -105,9 +127,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/hackertalk/index.ts b/lib/routes/hackertalk/index.ts index f3a766fe7..7e1bdf63d 100644 --- a/lib/routes/hackertalk/index.ts +++ b/lib/routes/hackertalk/index.ts @@ -1,16 +1,28 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import MarkdownIt from 'markdown-it'; const md = MarkdownIt(); -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['hackertalk.net/'], + target: '', + }, + name: 'Unknown', + maintainers: ['hyoban'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ?? 25; const response = await got(`https://api.hackertalk.net/v1/posts?limit=${limit}&orderBy=time`); const data = response.data.data; - ctx.set('data', { + return { title: '黑客说的最新帖子', link: 'https://hackertalk.net/?tab=new', description: '黑客说 - 技术驱动优质交流', @@ -20,5 +32,5 @@ export default async (ctx) => { pubDate: parseDate(item.createdAt), link: `https://hackertalk.net/posts/${item.id}`, })), - }); -}; + }; +} diff --git a/lib/routes/hacking8/index.ts b/lib/routes/hacking8/index.ts index 4cad38f5f..8fd833625 100644 --- a/lib/routes/hacking8/index.ts +++ b/lib/routes/hacking8/index.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: '/:category?', + categories: ['design'], + example: '/hacking8', + parameters: { category: '分类,见下表,默认为最近更新' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['hacking8.com/index/:category', 'hacking8.com/'], + }, + name: '信息流', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'index'; const rootUrl = 'https://i.hacking8.com'; @@ -37,9 +59,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `Hacking8 安全信息流 - ${$('div.btn-group a.btn-primary').text()}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/hacking8/search.ts b/lib/routes/hacking8/search.ts index 6c405c437..82fa25f8c 100644 --- a/lib/routes/hacking8/search.ts +++ b/lib/routes/hacking8/search.ts @@ -1,9 +1,32 @@ +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: '/search/:keyword?', + categories: ['design'], + example: '/hacking8/search/rsshub', + parameters: { keyword: '关键字,默认为空' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['hacking8.com/index/:category', 'hacking8.com/'], + target: '/:category?', + }, + name: '搜索', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const keyword = ctx.req.param('keyword') ?? ''; const rootUrl = 'https://i.hacking8.com'; @@ -37,12 +60,12 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `Hacking8 安全信息流 - ${$('title') .text() .replaceAll(/总数:\d+/g, '') .trim()}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/hackmd/profile.ts b/lib/routes/hackmd/profile.ts index e1866f4d2..caa977071 100644 --- a/lib/routes/hackmd/profile.ts +++ b/lib/routes/hackmd/profile.ts @@ -1,6 +1,25 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/profile/:path', + categories: ['design'], + example: '/hackmd/profile/hackmd', + parameters: { path: 'userpath or teampath' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Profile', + maintainers: ['Yukaii', 'kaiix'], + handler, +}; + +async function handler(ctx) { const path = ctx.req.param('path'); const response = await got({ @@ -10,7 +29,7 @@ export default async (ctx) => { const data = response.data; const profile = data.user || data.team; - ctx.set('data', { + return { title: `${profile.name}'s Profile`, link: `https://hackmd.io/@${path}`, description: `${profile.name}'s profile on HackMD`, @@ -20,5 +39,5 @@ export default async (ctx) => { pubDate: new Date(note.publishedAt).toUTCString(), link: `https://hackmd.io/@${path}/${note.permalink || note.shortId}`, })), - }); -}; + }; +} diff --git a/lib/routes/hackyournews/index.ts b/lib/routes/hackyournews/index.ts index 8e6cf2f50..741bbe150 100644 --- a/lib/routes/hackyournews/index.ts +++ b/lib/routes/hackyournews/index.ts @@ -1,9 +1,21 @@ +import { Route } from '@/types'; // Require necessary modules import got from '@/utils/got'; // a customised got import { load } from 'cheerio'; // an HTML parser with a jQuery-like API import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['hackyournews.com/'], + target: '', + }, + name: 'Unknown', + maintainers: ['ftiasch'], + handler, +}; + +async function handler() { const baseUrl = 'https://hackyournews.com'; const { data: response } = await got(baseUrl); const $ = load(response); @@ -42,9 +54,9 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title: 'Index', link: baseUrl, item, - }); -}; + }; +} diff --git a/lib/routes/hafu/news.ts b/lib/routes/hafu/news.ts index fae6da36e..163e30e66 100644 --- a/lib/routes/hafu/news.ts +++ b/lib/routes/hafu/news.ts @@ -1,15 +1,34 @@ +import { Route } from '@/types'; import parseList from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/news/:type?', + categories: ['forecast'], + example: '/hafu/news/ggtz', + parameters: { type: '分类,见下表(默认为 `ggtz`)' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '河南财政金融学院', + maintainers: [], + handler, +}; + +async function handler(ctx) { // set default router type const type = ctx.req.param('type') ?? 'ggtz'; const { link, title, resultList } = await parseList(ctx, type); - ctx.set('data', { + return { title, link, description: '河南财政金融学院 - 公告通知', item: resultList, - }); -}; + }; +} diff --git a/lib/routes/hakkatv/type.ts b/lib/routes/hakkatv/type.ts index 480945028..afb5ea961 100644 --- a/lib/routes/hakkatv/type.ts +++ b/lib/routes/hakkatv/type.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'; @@ -7,7 +8,29 @@ const typeMap = ['hakka', 'political', 'medical', 'local', 'international']; const baseUrl = 'https://www.hakkatv.org.tw'; const apiUrl = 'https://api.hakkatv.org.tw'; -export default async (ctx) => { +export const route: Route = { + path: '/news/:type?', + categories: ['bbs'], + example: '/hakkatv/news', + parameters: { type: '新聞,見下表,留空為全部' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['hakkatv.org.tw/news'], + target: '/news', + }, + name: '新聞首頁', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); const allData = type @@ -55,11 +78,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '新聞首頁 - 客家電視台', description: '客家電視是屬於全民、以至於全世界客家族群的頻道,亦是為傳播客家文化而存在,定位為「全體客家族群之媒體」。', link: `${baseUrl}/news`, language: 'zh-TW', item: items, - }); -}; + }; +} diff --git a/lib/routes/hameln/chapter.ts b/lib/routes/hameln/chapter.ts index 206a92740..ecc0343c7 100644 --- a/lib/routes/hameln/chapter.ts +++ b/lib/routes/hameln/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 timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/chapter/:id', + categories: ['government'], + example: '/hameln/chapter/264928', + parameters: { id: 'Novel id, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['syosetu.org/novel/: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://syosetu.org/novel/${id}`; @@ -38,14 +60,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/harvard/health/blog.ts b/lib/routes/harvard/health/blog.ts index 398bea22a..e89e337e4 100644 --- a/lib/routes/harvard/health/blog.ts +++ b/lib/routes/harvard/health/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: '/health/blog', + categories: ['traditional-media'], + example: '/harvard/health/blog', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.health.harvard.edu/blog'], + }, + name: 'Health Blog', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://www.health.harvard.edu'; const currentUrl = `${rootUrl}/blog`; @@ -45,9 +67,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'Harvard Health Blog', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/hashnode/blog.ts b/lib/routes/hashnode/blog.ts index f29697e9f..d7dc6fcd0 100644 --- a/lib/routes/hashnode/blog.ts +++ b/lib/routes/hashnode/blog.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'; const baseApiUrl = 'https://api.hashnode.com'; -export default async (ctx) => { +export const route: Route = { + path: '/blog/:username', + categories: ['programming'], + example: '/hashnode/blog/inklings', + parameters: { username: '博主名称,用户头像 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['hashnode.dev/'], + }, + name: '用户博客', + maintainers: ['hnrainll'], + handler, +}; + +async function handler(ctx) { const username = ctx.req.param('username'); if (!username) { return; @@ -47,7 +69,7 @@ export default async (ctx) => { } const list = publication.posts; - ctx.set('data', { + return { title: `Hashnode by ${username}`, link: userUrl, item: list @@ -61,5 +83,5 @@ export default async (ctx) => { link: `${userUrl}/${item.slug}`, })) .filter((item) => item !== ''), - }); -}; + }; +} diff --git a/lib/routes/hbooker/chapter.ts b/lib/routes/hbooker/chapter.ts index a6b0014ab..691290844 100644 --- a/lib/routes/hbooker/chapter.ts +++ b/lib/routes/hbooker/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: '/chapter/:id', + categories: ['government'], + example: '/hbooker/chapter/100113279', + parameters: { id: '小说 id, 可在对应小说页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['hbooker.com/book/:id'], + }, + name: '章节', + maintainers: ['keocheung'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const limit = Number.parseInt(ctx.req.query('limit')) || 10; @@ -37,11 +59,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `欢乐书客 ${$('div.book-title h1').text()}`, link: `${baseUrl}/book/${id}`, description: $('div.book-desc').text(), image: $('div.book-cover img').attr('src'), item: items, - }); -}; + }; +} diff --git a/lib/routes/hbr/topic.ts b/lib/routes/hbr/topic.ts index a0d71ede8..c71b38ffb 100644 --- a/lib/routes/hbr/topic.ts +++ b/lib/routes/hbr/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?/:type?', + categories: ['traditional-media'], + example: '/hbr/topic/leadership', + parameters: { topic: 'Topic, can be found in URL, Leadership by default', type: 'Type, see below, Latest by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['hbr.org/topic/:topic?', 'hbr.org/'], + }, + name: 'Topic', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const topic = ctx.req.param('topic') ?? 'leadership'; const type = ctx.req.param('type') ?? 'Latest'; @@ -49,9 +71,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('title').eq(0).text()} - ${type}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/hdu/cs/notice.ts b/lib/routes/hdu/cs/notice.ts index cc34ba853..8dc6a2865 100644 --- a/lib/routes/hdu/cs/notice.ts +++ b/lib/routes/hdu/cs/notice.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,28 @@ const getSingleRecord = async () => { ); }; -export default async (ctx) => { +export const route: Route = { + path: '/cs', + categories: ['forecast'], + example: '/hdu/cs', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['computer.hdu.edu.cn/6738/list.htm'], + }, + name: '计算机学院 - 通知公告', + maintainers: ['legr4ndk'], + handler, +}; + +async function handler() { const items = await getSingleRecord(); const out = await Promise.all( items.map((item) => @@ -46,10 +68,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '杭电计算机-通知公告', description: '杭州电子科技大学计算机学院-通知公告', link: host, item: out, - }); -}; + }; +} diff --git a/lib/routes/hdu/cs/pg.ts b/lib/routes/hdu/cs/pg.ts index 16d9de1d3..586311cee 100644 --- a/lib/routes/hdu/cs/pg.ts +++ b/lib/routes/hdu/cs/pg.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,28 @@ const getSingleRecord = async () => { ); }; -export default async (ctx) => { +export const route: Route = { + path: '/cs/pg', + categories: ['forecast'], + example: '/hdu/cs/pg', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['computer.hdu.edu.cn/6769/list.htm'], + }, + name: '计算机学院 - 研究生通知', + maintainers: ['legr4ndk'], + handler, +}; + +async function handler() { const items = await getSingleRecord(); const out = await Promise.all( items.map((item) => @@ -46,10 +68,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '杭电计算机-研究生通知', description: '杭州电子科技大学计算机学院-研究生教学通知', link: host, item: out, - }); -}; + }; +} diff --git a/lib/routes/hebtv/nong-bo-shi-zai-xing-dong.ts b/lib/routes/hebtv/nong-bo-shi-zai-xing-dong.ts index 272cda3d8..8c7622045 100644 --- a/lib/routes/hebtv/nong-bo-shi-zai-xing-dong.ts +++ b/lib/routes/hebtv/nong-bo-shi-zai-xing-dong.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -11,7 +12,28 @@ import * as path from 'node:path'; const baseUrl = 'https://web.cmc.hebtv.com/cms/rmt0336/19/19js/st/ds/nmpd/nbszxd/index.shtml'; -export default async (ctx) => { +export const route: Route = { + path: '/nbszxd', + categories: ['bbs'], + example: '/hebtv/nbszxd', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: true, + supportPodcast: true, + supportScihub: false, + }, + radar: { + source: ['web.cmc.hebtv.com/cms/rmt0336/19/19js/st/ds/nmpd/nbszxd/index.shtml'], + }, + name: '农博士在行动', + maintainers: ['iamqiz', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 40; const apiRootUrl = 'http://api.cmc.hebtv.com'; @@ -95,7 +117,7 @@ export default async (ctx) => { const author = description.split(/,/)[0]; const icon = $('link[rel="shortcut icon"]').prop('href'); - ctx.set('data', { + return { item: items, title: $('title').text(), link: baseUrl, @@ -109,5 +131,5 @@ export default async (ctx) => { itunes_author: author, itunes_category: 'News', allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/hellobtc/information.ts b/lib/routes/hellobtc/information.ts index 18ca8ee30..02182cafb 100644 --- a/lib/routes/hellobtc/information.ts +++ b/lib/routes/hellobtc/information.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 titleMap = { application: '应用', }; -export default async (ctx) => { +export const route: Route = { + path: '/information/:channel?', + categories: ['traditional-media'], + example: '/hellobtc/information/latest', + parameters: { channel: '类型,可填 `latest` 和 `application` 及最新和应用,默认为最新' }, + 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') ?? 'latest'; const url = rootUrl; @@ -44,9 +63,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `白话区块链 - 首页 ${titleMap[channel]}`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/hellobtc/kepu.ts b/lib/routes/hellobtc/kepu.ts index 740294fa7..700bb0bf6 100644 --- a/lib/routes/hellobtc/kepu.ts +++ b/lib/routes/hellobtc/kepu.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -28,7 +29,25 @@ const titleMap = { public_blockchain: '公链', }; -export default async (ctx) => { +export const route: Route = { + path: '/kepu/:channel?', + categories: ['traditional-media'], + example: '/hellobtc/kepu/latest', + parameters: { channel: '类型,见下表,默认为最新' }, + 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') ?? 'latest'; const url = `${rootUrl}/kepu.html`; @@ -57,9 +76,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `白话区块链 - 科普 ${titleMap[channel]}`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/hellobtc/news.ts b/lib/routes/hellobtc/news.ts index afb57d801..95b636371 100644 --- a/lib/routes/hellobtc/news.ts +++ b/lib/routes/hellobtc/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'; @@ -5,7 +6,28 @@ import timezone from '@/utils/timezone'; const rootUrl = 'https://www.hellobtc.com'; -export default async (ctx) => { +export const route: Route = { + path: '/news', + categories: ['traditional-media'], + example: '/hellobtc/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['hellobtc.com/news'], + }, + name: '快讯', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler() { const url = `${rootUrl}/news`; const response = await got(url); @@ -21,9 +43,9 @@ export default async (ctx) => { .filter(Boolean) .get(); - ctx.set('data', { + return { title: `白话区块链 - 快讯`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/hellogithub/index.ts b/lib/routes/hellogithub/index.ts index a2f3aa849..738fe890a 100644 --- a/lib/routes/hellogithub/index.ts +++ b/lib/routes/hellogithub/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -13,7 +14,25 @@ const sorts = { last: '最近', }; -export default async (ctx) => { +export const route: Route = { + path: ['/article/:sort?/:id?', '/:sort?/:id?'], + categories: ['design'], + example: '/hellogithub/article', + parameters: { sort: '排序方式,见下表,默认为 `hot`,即热门', id: '标签 id,可在对应标签页 URL 中找到,默认为全部标签' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '文章', + maintainers: ['moke8', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const sort = ctx.req.param('sort') ?? 'hot'; const id = ctx.req.param('id') ?? ''; const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 20; @@ -98,9 +117,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `HelloGithub - ${sorts[sort]}${tag || ''}项目`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/hellogithub/report.ts b/lib/routes/hellogithub/report.ts index cf6da0aa3..44b0c2b17 100644 --- a/lib/routes/hellogithub/report.ts +++ b/lib/routes/hellogithub/report.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -12,7 +13,14 @@ const types = { 'db-engines': '数据库', }; -export default async (ctx) => { +export const route: Route = { + path: ['/ranking/:type?', '/report/:type?'], + name: 'Unknown', + maintainers: ['moke8', 'nczitzk'], + handler, +}; + +async function handler(ctx) { let type = ctx.req.param('type') ?? 'tiobe'; type = type === 'webserver' ? 'netcraft' : type === 'db' ? 'db-engines' : type; @@ -51,9 +59,9 @@ export default async (ctx) => { }, ]; - ctx.set('data', { + return { title: `HelloGitHub - ${types[type]}排行榜`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/hellogithub/volume.ts b/lib/routes/hellogithub/volume.ts index ff9e9db81..85a9ae5f3 100644 --- a/lib/routes/hellogithub/volume.ts +++ b/lib/routes/hellogithub/volume.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -14,7 +15,14 @@ art.defaults.imports.render = function (string) { return md.render(string); }; -export default async (ctx) => { +export const route: Route = { + path: ['/month', '/volume'], + name: 'Unknown', + maintainers: ['moke8', 'nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://hellogithub.com'; const apiUrl = 'https://api.hellogithub.com/v1/periodical/'; @@ -46,9 +54,9 @@ export default async (ctx) => { }, ]; - ctx.set('data', { + return { title: 'HelloGithub - 月刊', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/hex-rays/index.ts b/lib/routes/hex-rays/index.ts index f284b2b13..5ff38ca8d 100644 --- a/lib/routes/hex-rays/index.ts +++ b/lib/routes/hex-rays/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: '/news', + categories: ['design'], + example: '/hex-rays/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['hex-rays.com/', 'hex-rays.com/blog'], + }, + name: 'Hex-Rays News', + maintainers: ['hellodword ', 'TonyRL'], + handler, +}; + +async function handler() { const link = 'https://www.hex-rays.com/blog/'; const response = await got.get(link); const $ = load(response.data); @@ -40,9 +62,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'Hex-Rays Blog', link, item: items, - }); -}; + }; +} diff --git a/lib/routes/hicairo/rss.ts b/lib/routes/hicairo/rss.ts index 1a2917f1f..baabb4ae8 100644 --- a/lib/routes/hicairo/rss.ts +++ b/lib/routes/hicairo/rss.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: '/', + radar: { + source: ['hicairo.com/'], + target: '', + }, + name: 'Unknown', + maintainers: ['cnkmmk'], + handler, +}; + +async function handler() { const currentUrl = 'https://www.hicairo.com'; const response = await got(`${currentUrl}/feed.php`); const $ = load(response.data, { xmlMode: true }); @@ -23,10 +35,10 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title: title_main, description: description_main, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/hinatazaka46/blog.ts b/lib/routes/hinatazaka46/blog.ts index 90bc5a0d3..ae9e41be2 100644 --- a/lib/routes/hinatazaka46/blog.ts +++ b/lib/routes/hinatazaka46/blog.ts @@ -1,10 +1,29 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; const rootUrl = 'https://www.hinatazaka46.com'; -export default async (ctx) => { +export const route: Route = { + path: '/blog/:id?/:page?', + categories: ['traditional-media'], + example: '/hinatazaka46/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: 'Hinatazaka46 Blog 日向坂 46 博客', + maintainers: [], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? 'all'; const page = ctx.req.param('page') ?? '0'; @@ -31,10 +50,10 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { allowEmpty: true, title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/hinatazaka46/news.ts b/lib/routes/hinatazaka46/news.ts index cef9ff0a5..d950e40b0 100644 --- a/lib/routes/hinatazaka46/news.ts +++ b/lib/routes/hinatazaka46/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: '/hinatazaka46/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['hinatazaka46.com/s/official/news/list', 'hinatazaka46.com/'], + }, + name: 'Hinatazaka46 News 日向坂 46 新闻', + maintainers: ['crispgm', 'akashigakki'], + handler, +}; + +async function handler(ctx) { const rootUrl = 'https://www.hinatazaka46.com'; const currentUrl = `${rootUrl}/s/official/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/hit/hitgs.ts b/lib/routes/hit/hitgs.ts index 0edb34c9a..65960064b 100644 --- a/lib/routes/hit/hitgs.ts +++ b/lib/routes/hit/hitgs.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: '/hitgs', + categories: ['forecast'], + example: '/hit/hitgs', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['hitgs.hit.edu.cn/*'], + }, + name: '研究生院通知公告', + maintainers: ['hlmu'], + handler, +}; + +async function handler() { const host = 'https://hitgs.hit.edu.cn'; const response = await got(host + '/tzgg/list.htm', { @@ -38,10 +60,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '哈工大研究生院通知公告', link: host + '/tzgg/list.htm', description: '哈尔滨工业大学研究生院通知公告', item: out, - }); -}; + }; +} diff --git a/lib/routes/hit/jwc.ts b/lib/routes/hit/jwc.ts index 502fe48bd..5c9c98a20 100644 --- a/lib/routes/hit/jwc.ts +++ b/lib/routes/hit/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,28 @@ import { parseDate } from '@/utils/parse-date'; const baseUrl = 'https://jwc.hit.edu.cn'; const type = (filename) => filename.split('.').pop(); -export default async (ctx) => { +export const route: Route = { + path: '/jwc', + categories: ['forecast'], + example: '/hit/jwc', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['jwc.hit.edu.cn/*'], + }, + name: '教务处通知公告', + maintainers: ['lty96117'], + handler, +}; + +async function handler() { const response = await got(`${baseUrl}/2591/list.htm`); const { data } = response; @@ -53,9 +75,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '哈尔滨工业大学教务处通知公告', link: `${baseUrl}/2591/list.htm`, item: items, - }); -}; + }; +} diff --git a/lib/routes/hit/today.ts b/lib/routes/hit/today.ts index 9cc40b916..d9337f2dd 100644 --- a/lib/routes/hit/today.ts +++ b/lib/routes/hit/today.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: '/today/:category', + categories: ['forecast'], + example: '/hit/today/10', + parameters: { category: '分类编号,`10`为公告公示,`11`为新闻快讯,同时支持详细分类,使用方法见下' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['today.hit.edu.cn/category/:category'], + }, + name: '今日哈工大', + maintainers: ['ranpox'], + handler, +}; + +async function handler(ctx) { const host = 'https://today.hit.edu.cn'; const category = ctx.req.param('category'); @@ -52,9 +74,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text().trim(), link: host + '/category/' + category, item: out, - }); -}; + }; +} diff --git a/lib/routes/hitsz/article.ts b/lib/routes/hitsz/article.ts index e6faaac0e..c28444885 100644 --- a/lib/routes/hitsz/article.ts +++ b/lib/routes/hitsz/article.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: '/article/:category?', + categories: ['forecast'], + example: '/hitsz/article/id-74', + parameters: { category: '分类名,默认为校园动态' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '新闻中心', + maintainers: ['xandery-geek'], + handler, +}; + +async function handler(ctx) { const host = 'https://www.hitsz.edu.cn'; const category = ctx.req.param('category') ?? 'id-77'; const link = `${host}/article/${category}.html`; @@ -33,10 +52,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '哈尔滨工业大学(深圳)-' + category_name, link, description: '哈尔滨工业大学(深圳)-' + category_name, item: items, - }); -}; + }; +} diff --git a/lib/routes/hitwh/today.ts b/lib/routes/hitwh/today.ts index 80359b3c5..38dff4b42 100644 --- a/lib/routes/hitwh/today.ts +++ b/lib/routes/hitwh/today.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 baseUrl = 'https://today.hitwh.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/today', + categories: ['forecast'], + example: '/hitwh/today', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['hitwh.edu.cn/1024/list.htm', 'hitwh.edu.cn/'], + }, + name: '今日工大 - 通知公告', + maintainers: ['raptazure'], + handler, +}; + +async function handler() { const response = await got(`${baseUrl}/1024/list.htm`, { https: { rejectUnauthorized: false, @@ -22,7 +44,7 @@ export default async (ctx) => { })) .get(); - ctx.set('data', { + return { title: '哈尔滨工业大学(威海)通知公告', link: `${baseUrl}/1024/list.htm`, item: await Promise.all( @@ -51,5 +73,5 @@ export default async (ctx) => { }) ) ), - }); -}; + }; +} diff --git a/lib/routes/hizu/index.ts b/lib/routes/hizu/index.ts index 41031b807..b87a65b49 100644 --- a/lib/routes/hizu/index.ts +++ b/lib/routes/hizu/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -30,7 +31,28 @@ const titles = { '607d37ade4b05c59ac2f3d40': '高新', }; -export default async (ctx) => { +export const route: Route = { + path: '/:column?', + categories: ['traditional-media'], + example: '/hizu', + parameters: { column: '栏目,见下表,默认为热点' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['hizh.cn/'], + }, + name: '栏目', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const column = ctx.req.param('column') ?? '5dd92265e4b0bf88dd8c1175'; const rootUrl = 'https://www.hizh.cn'; @@ -69,9 +91,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${titles[column]} - 珠海网`, link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/hk01/channel.ts b/lib/routes/hk01/channel.ts index 0deb0f2da..dbe98c17e 100644 --- a/lib/routes/hk01/channel.ts +++ b/lib/routes/hk01/channel.ts @@ -1,8 +1,19 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { rootUrl, apiRootUrl, ProcessItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/channel/:id?', + radar: { + source: ['hk01.com/channel/:id', 'hk01.com/'], + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? '1'; const currentUrl = `${rootUrl}/channel/${id}`; @@ -15,10 +26,10 @@ export default async (ctx) => { const items = await ProcessItems(response.data.items, ctx.req.query('limit'), cache.tryGet); - ctx.set('data', { + return { title: response.data.category ? `${response.data.category.publishName} | 香港01` : `Channel: ${id} | 香港01`, link: currentUrl, item: items, image: response.data.category ? response.data.category.icon : null, - }); -}; + }; +} diff --git a/lib/routes/hk01/hot.ts b/lib/routes/hk01/hot.ts index ee2b5d227..50512b364 100644 --- a/lib/routes/hk01/hot.ts +++ b/lib/routes/hk01/hot.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { rootUrl, apiRootUrl, ProcessItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/hot', + categories: ['traditional-media'], + example: '/hk01/hot', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['hk01.com/hot', 'hk01.com/'], + }, + name: '热门', + maintainers: ['hoilc', 'Fatpandac', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const currentUrl = `${rootUrl}/hot`; const apiUrl = `${apiRootUrl}/v2/feed/hot`; @@ -13,9 +35,9 @@ export default async (ctx) => { const items = await ProcessItems(response.data.items, ctx.req.query('limit'), cache.tryGet); - ctx.set('data', { + return { title: '熱門新聞、全城熱話及社會時事 | 香港01', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/hk01/issue.ts b/lib/routes/hk01/issue.ts index 568cf8fd0..f2751bc57 100644 --- a/lib/routes/hk01/issue.ts +++ b/lib/routes/hk01/issue.ts @@ -1,8 +1,19 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { rootUrl, apiRootUrl, ProcessItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/issue/:id?', + radar: { + source: ['hk01.com/issue/:id', 'hk01.com/'], + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? '649'; const currentUrl = `${rootUrl}/issue/${id}`; @@ -15,9 +26,9 @@ export default async (ctx) => { const items = await ProcessItems(response.data.blocks[0].articles, ctx.req.query('limit'), cache.tryGet); - ctx.set('data', { + return { title: `${response.data.title} | 香港01`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/hk01/latest.ts b/lib/routes/hk01/latest.ts index 7ab2ae060..049550b5d 100644 --- a/lib/routes/hk01/latest.ts +++ b/lib/routes/hk01/latest.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { rootUrl, apiRootUrl, ProcessItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/latest', + categories: ['traditional-media'], + example: '/hk01/latest', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['hk01.com/latest', 'hk01.com/'], + }, + name: '即時', + maintainers: ['5upernova-heng'], + handler, +}; + +async function handler(ctx) { const currentUrl = `${rootUrl}/latest`; const apiUrl = `${apiRootUrl}/v2/page/latest`; @@ -13,9 +35,9 @@ export default async (ctx) => { const items = await ProcessItems(response.data.items, ctx.req.query('limit'), cache.tryGet); - ctx.set('data', { + return { title: '即時 | 香港01', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/hk01/tag.ts b/lib/routes/hk01/tag.ts index 844d6a287..aa5a6ffe1 100644 --- a/lib/routes/hk01/tag.ts +++ b/lib/routes/hk01/tag.ts @@ -1,8 +1,19 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { rootUrl, apiRootUrl, ProcessItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/tag/:id?', + radar: { + source: ['hk01.com/tag/:id', 'hk01.com/'], + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? '1'; const currentUrl = `${rootUrl}/tag/${id}`; @@ -15,9 +26,9 @@ export default async (ctx) => { const items = await ProcessItems(response.data.items, ctx.req.query('limit'), cache.tryGet); - ctx.set('data', { + return { title: `${id} | 香港01`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/hk01/zone.ts b/lib/routes/hk01/zone.ts index 49f267f9f..b1843b097 100644 --- a/lib/routes/hk01/zone.ts +++ b/lib/routes/hk01/zone.ts @@ -1,8 +1,19 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { rootUrl, apiRootUrl, ProcessItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/zone/:id?', + radar: { + source: ['hk01.com/zone/:id', 'hk01.com/'], + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? '1'; const currentUrl = `${rootUrl}/zone/${id}`; @@ -15,10 +26,10 @@ export default async (ctx) => { const items = await ProcessItems(response.data.items, ctx.req.query('limit'), cache.tryGet); - ctx.set('data', { + return { title: `${response.data.category.publishName} | 香港01`, link: currentUrl, item: items, image: response.data.category.icon, - }); -}; + }; +} diff --git a/lib/routes/hkej/index.ts b/lib/routes/hkej/index.ts index cb102e4e1..7517e2b68 100644 --- a/lib/routes/hkej/index.ts +++ b/lib/routes/hkej/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -57,7 +58,28 @@ const categories = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + categories: ['bbs'], + example: '/hkej/index', + parameters: { category: '分类,默认为全部新闻' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['hkej.com/'], + }, + name: '即时新闻', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'index'; const cat = categories[category]; const baseUrl = 'https://www2.hkej.com'; @@ -139,13 +161,13 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `信報網站 - ${cat.title} - 信報網站 hkej.com`, link: baseUrl + cat.link, description: `信報網站(www.hkej.com)即時新聞${cat.name},提供${cat.description}。`, item: items, language: 'zh-hk', - }); + }; ctx.set('json', { title: `信報網站 - ${cat.title} - 信報網站 hkej.com`, @@ -155,4 +177,4 @@ export default async (ctx) => { language: 'zh-hk', cookieJar, }); -}; +} diff --git a/lib/routes/hkepc/index.ts b/lib/routes/hkepc/index.ts index 2408219f4..6aee0ee3b 100644 --- a/lib/routes/hkepc/index.ts +++ b/lib/routes/hkepc/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 timezone from '@/utils/timezone'; import { baseUrl, categoryMap } from './data'; -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + categories: ['traditional-media'], + example: '/hkepc/news', + parameters: { category: '分类,见下表,默认为最新消息' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['hkepc.com/'], + target: '', + }, + name: 'HKEPC 电脑领域', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? ''; const { data: response } = await got(categoryMap[category].url, { @@ -88,11 +111,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `電腦領域 HKEPC${categoryMap[category].feedSuffix}`, link: `https://www.hkepc.com/${category}`, description: '電腦領域 HKEPC Hardware - 全港 No.1 PC網站', language: 'zh-hk', item: items, - }); -}; + }; +} diff --git a/lib/routes/hket/index.ts b/lib/routes/hket/index.ts index b8efbe087..9014f0495 100644 --- a/lib/routes/hket/index.ts +++ b/lib/routes/hket/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -24,7 +25,29 @@ const urlMap = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + categories: ['bbs'], + example: '/hket/sran001', + parameters: { category: '分类,默认为全部新闻,可在 URL 中找到,部分见下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.hket.com/'], + target: '', + }, + name: '新闻', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { category = 'sran001' } = ctx.req.param(); const baseUrl = urlMap[category.substring(0, 4)].baseUrl; @@ -123,13 +146,13 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head meta[name=title]').attr('content').trim(), link: baseUrl + '/' + category, description: $('head meta[name=description]').attr('content').trim(), item: items, language: 'zh-hk', - }); + }; ctx.set('json', { title: $('head meta[name=title]').attr('content').trim(), @@ -138,4 +161,4 @@ export default async (ctx) => { item: items, language: 'zh-hk', }); -}; +} diff --git a/lib/routes/hkjunkcall/index.ts b/lib/routes/hkjunkcall/index.ts index 8be8dc20c..74e1c88a0 100644 --- a/lib/routes/hkjunkcall/index.ts +++ b/lib/routes/hkjunkcall/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: ['hkjunkcall.com/'], + target: '', + }, + name: 'Unknown', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://hkjunkcall.com'; const currentUrl = `${rootUrl}/cdpushdash.asp`; @@ -46,9 +58,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/hljucm/yjsy.ts b/lib/routes/hljucm/yjsy.ts index f040859ef..97a293723 100644 --- a/lib/routes/hljucm/yjsy.ts +++ b/lib/routes/hljucm/yjsy.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: '/yjsy/:category?', + categories: ['forecast'], + example: '/hljucm/yjsy', + 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') ?? 'xwdt'; const rootUrl = 'https://yjsy.hljucm.net'; @@ -49,9 +68,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/hnrb/index.ts b/lib/routes/hnrb/index.ts index fe345f00d..74295ae08 100644 --- a/lib/routes/hnrb/index.ts +++ b/lib/routes/hnrb/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: ['bbs'], + example: '/hnrb', + parameters: { id: '编号,见下表,默认为全部' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['voc.com.cn/'], + target: '/:id', + }, + name: '电子刊物', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const rootUrl = 'https://hnrb.voc.com.cn'; @@ -78,9 +101,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `湖南日报${id ? ` - ${$('strong').first().parent().text()}` : ''}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/hnu/careers.ts b/lib/routes/hnu/careers.ts index d6bbd217c..43dc74dee 100644 --- a/lib/routes/hnu/careers.ts +++ b/lib/routes/hnu/careers.ts @@ -1,6 +1,28 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/careers', + categories: ['forecast'], + example: '/hnu/careers', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['scc.hnu.edu.cnundefined'], + }, + name: '校园招聘', + maintainers: ['ningyougan'], + handler, +}; + +async function handler() { const rootUrl = 'http://scc.hnu.edu.cn/'; // 校园招聘的路由为/module/getcareers,但该页面默认仅加载整体框架,具体的招聘信息在页面加载完成后再交易获取。 // 具体的交易可以通过f12,网络栏进行查看,项目中仅保留了必须携带的参数。 @@ -13,7 +35,7 @@ export default async (ctx) => { }); const company_list = response.data.data; - ctx.set('data', { + return { title: '校园招聘', link: currentRoute, item: company_list.map((company_info) => { @@ -29,5 +51,5 @@ export default async (ctx) => { }; return ret; }), - }); -}; + }; +} diff --git a/lib/routes/hongkong/chp.ts b/lib/routes/hongkong/chp.ts index c291c0fae..29830358e 100644 --- a/lib/routes/hongkong/chp.ts +++ b/lib/routes/hongkong/chp.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,17 @@ const titles = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/chp/:category?/:language?', + radar: { + source: ['dh.gov.hk/'], + }, + name: 'Unknown', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const languageCodes = { en: 'en', zh_cn: 'sc', @@ -96,9 +107,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${titles[category][language]} - ${titles.title[language]}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/hongkong/dh.ts b/lib/routes/hongkong/dh.ts index f7dcc2e8a..e4a496d17 100644 --- a/lib/routes/hongkong/dh.ts +++ b/lib/routes/hongkong/dh.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: '/dh/:language?', + categories: ['study'], + example: '/hongkong/dh', + parameters: { language: 'Language, see below, tc_chi by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['dh.gov.hk/'], + }, + name: 'Press Release', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const language = ctx.req.param('language') ?? 'tc_chi'; const rootUrl = 'https://www.dh.gov.hk'; @@ -46,9 +68,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/hostmonit/cloudflareyes.ts b/lib/routes/hostmonit/cloudflareyes.ts index cdf8b9f50..2e833ae95 100644 --- a/lib/routes/hostmonit/cloudflareyes.ts +++ b/lib/routes/hostmonit/cloudflareyes.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 lines = { CT: '中国电信', }; -export default async (ctx) => { +export const route: Route = { + path: '/cloudflareyes/:type?', + categories: ['other'], + example: '/hostmonit/cloudflareyes', + parameters: { type: '类型,见下表,默认为 v4' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'CloudFlareYes', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const { type = 'v4' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; @@ -79,7 +98,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().replace(/- .*$/, `- ${title}`), link: currentUrl, @@ -90,5 +109,5 @@ export default async (ctx) => { subtitle: title, author: $('title').text().split(/\s-/)[0], allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/hostmonit/cloudflareyesv6.ts b/lib/routes/hostmonit/cloudflareyesv6.ts index dbb60c079..a52287cfb 100644 --- a/lib/routes/hostmonit/cloudflareyesv6.ts +++ b/lib/routes/hostmonit/cloudflareyesv6.ts @@ -1,3 +1,11 @@ -export default (ctx) => { - ctx.redirect('/hostmonit/cloudflareyes/v6'); +import { Route } from '@/types'; +export const route: Route = { + path: '/cloudflareyesv6', + name: 'Unknown', + maintainers: [], + handler, }; + +function handler(ctx) { + ctx.redirect('/hostmonit/cloudflareyes/v6'); +} diff --git a/lib/routes/hotukdeals/hottest.ts b/lib/routes/hotukdeals/hottest.ts index 34d5d359f..5ee050e79 100644 --- a/lib/routes/hotukdeals/hottest.ts +++ b/lib/routes/hotukdeals/hottest.ts @@ -1,7 +1,29 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { JSDOM } from 'jsdom'; -export default async (ctx) => { +export const route: Route = { + path: '/hottest', + categories: ['game'], + example: '/hotukdeals/hottest', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.hotukdeals.com/'], + }, + name: 'hottest', + maintainers: ['DIYgod'], + handler, +}; + +async function handler() { const data = await got.get(`https://www.hotukdeals.com/`, { headers: { Referer: `https://www.hotukdeals.com/`, @@ -13,7 +35,7 @@ export default async (ctx) => { }); const threads = dom.window.__INITIAL_STATE__.widgets.hottestWidget.threads; - ctx.set('data', { + return { title: `hotukdeals hottest`, link: `https://www.hotukdeals.com/`, item: threads.map((item) => ({ @@ -21,5 +43,5 @@ export default async (ctx) => { description: `
    ${item.temperature}° ${item.title}
    ${item.displayPrice}`, link: item.url, })), - }); -}; + }; +} diff --git a/lib/routes/hotukdeals/index.ts b/lib/routes/hotukdeals/index.ts index 49a5b2d1c..ceb9eddac 100644 --- a/lib/routes/hotukdeals/index.ts +++ b/lib/routes/hotukdeals/index.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: '/:type', + categories: ['game'], + example: '/hotukdeals/hot', + parameters: { type: 'should be one of highlights, hot, new, discussed' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'thread', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { let type = ctx.req.param('type'); if (type === 'highlights') { type = ''; @@ -16,7 +35,7 @@ export default async (ctx) => { const list = $('article.thread'); - ctx.set('data', { + return { title: `hotukdeals ${type}`, link: `https://www.hotukdeals.com/${type}`, item: list @@ -30,5 +49,5 @@ export default async (ctx) => { }) .get() .reverse(), - }); -}; + }; +} diff --git a/lib/routes/houxu/events.ts b/lib/routes/houxu/events.ts index bd45bce92..deb59cbfe 100644 --- a/lib/routes/houxu/events.ts +++ b/lib/routes/houxu/events.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: '/events', + categories: ['traditional-media'], + example: '/houxu/events', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['houxu.app/events', 'houxu.app/'], + }, + name: '专栏', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const rootUrl = 'https://houxu.app'; const apiUrl = `${rootUrl}/api/1/events?limit=${ctx.req.query('limit') ?? 50}`; const currentUrl = `${rootUrl}/events`; @@ -32,9 +54,9 @@ export default async (ctx) => { }), })); - ctx.set('data', { + return { title: '后续 - 专栏', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/houxu/index.ts b/lib/routes/houxu/index.ts index 8a0707f3c..e5347fbae 100644 --- a/lib/routes/houxu/index.ts +++ b/lib/routes/houxu/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: ['/featured', '/index', '/'], + radar: { + source: ['houxu.app/'], + target: '', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const rootUrl = 'https://houxu.app'; const apiUrl = `${rootUrl}/api/1/records/index?limit=${ctx.req.query('limit') ?? 50}`; @@ -32,9 +44,9 @@ export default async (ctx) => { }), })); - ctx.set('data', { + return { title: '后续 - 热点', link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/houxu/lives.ts b/lib/routes/houxu/lives.ts index 1e6a34273..a9022587f 100644 --- a/lib/routes/houxu/lives.ts +++ b/lib/routes/houxu/lives.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: '/lives/:id', + categories: ['traditional-media'], + example: '/houxu/lives/33899', + parameters: { id: '编号,可在对应 Live 页面的 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['houxu.app/lives/:id', 'houxu.app/'], + }, + name: 'Live', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const rootUrl = 'https://houxu.app'; @@ -26,10 +48,10 @@ export default async (ctx) => { description: item.link.description, })); - ctx.set('data', { + return { title: `后续 - ${pageResponse.data.title}`, link: currentUrl, item: items, description: pageResponse.data.summary, - }); -}; + }; +} diff --git a/lib/routes/houxu/memory.ts b/lib/routes/houxu/memory.ts index 52a00e164..19a271d47 100644 --- a/lib/routes/houxu/memory.ts +++ b/lib/routes/houxu/memory.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: '/memory', + categories: ['traditional-media'], + example: '/houxu/memory', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['houxu.app/memory', 'houxu.app/'], + }, + name: '跟踪', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const rootUrl = 'https://houxu.app'; const apiUrl = `${rootUrl}/api/1/lives/updated?limit=${ctx.req.query('limit') ?? 50}`; const currentUrl = `${rootUrl}/memory`; @@ -32,9 +54,9 @@ export default async (ctx) => { }), })); - ctx.set('data', { + return { title: '后续 - 跟踪', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/howtoforge/rss.ts b/lib/routes/howtoforge/rss.ts index 7709dee75..fdd05f2df 100644 --- a/lib/routes/howtoforge/rss.ts +++ b/lib/routes/howtoforge/rss.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: '/', + radar: { + source: ['howtoforge.com/'], + target: '', + }, + name: 'Unknown', + maintainers: ['cnkmmk'], + handler, +}; + +async function handler() { const currentUrl = 'https://www.howtoforge.com'; const response = await got(`${currentUrl}/feed.rss`); const $ = load(response.data, { xmlMode: true }); @@ -23,10 +35,10 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title: titleMain, description: descriptionMain, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/hoyolab/news.ts b/lib/routes/hoyolab/news.ts index 419c10710..12fc43b3f 100644 --- a/lib/routes/hoyolab/news.ts +++ b/lib/routes/hoyolab/news.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -73,7 +74,25 @@ const getPostContent = (list, { language }) => }) ); -export default async (ctx) => { +export const route: Route = { + path: '/news/:language/:gids/:type', + categories: ['reading'], + example: '/hoyolab/news/zh-cn/2/2', + parameters: { language: 'Language', gids: 'Game ID', type: 'Announcement type' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Official Announcement', + maintainers: ['ZenoTian'], + handler, +}; + +async function handler(ctx) { try { const { type, gids, language } = ctx.req.param(); const params = { @@ -86,15 +105,15 @@ export default async (ctx) => { const typeInfo = await getI18nType(language, cache.tryGet); const list = await getEventList(params); const items = await getPostContent(list, params); - ctx.set('data', { + return { title: `HoYoLAB-${gameInfo.name}-${typeInfo[type].title}`, link: `${LINK}/circles/${gids}/${OFFICIAL_PAGE_TYPE[gids]}/official?page_type=${OFFICIAL_PAGE_TYPE[gids]}&page_sort=${typeInfo[type].sort}`, item: items, image: gameInfo.icon, icon: gameInfo.icon, logo: gameInfo.icon, - }); + }; } catch (error) { logger.error(error); } -}; +} diff --git a/lib/routes/hpoi/all.ts b/lib/routes/hpoi/all.ts index 514a2b6cd..46bc865c6 100644 --- a/lib/routes/hpoi/all.ts +++ b/lib/routes/hpoi/all.ts @@ -1,3 +1,28 @@ +import { Route } from '@/types'; import { ProcessFeed } from './utils'; -export default async (ctx) => ctx.set('data', await ProcessFeed('all', 0, ctx.req.param('order'))); +export const route: Route = { + path: '/items/all/:order?', + categories: ['program-update'], + example: '/hpoi/items/all', + parameters: { order: '排序, 见下表,默认为 add' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.hpoi.net/hobby/all'], + target: '/items/all', + }, + name: '所有周边', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { + ctx.set('data', await ProcessFeed('all', 0, ctx.req.param('order'))); +} diff --git a/lib/routes/hpoi/banner-item.ts b/lib/routes/hpoi/banner-item.ts index b753e6a30..3ff500ab9 100644 --- a/lib/routes/hpoi/banner-item.ts +++ b/lib/routes/hpoi/banner-item.ts @@ -1,14 +1,36 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/bannerItem', + categories: ['program-update'], + example: '/hpoi/bannerItem', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.hpoi.net/bannerItem/list'], + }, + name: '热门推荐', + maintainers: ['DIYgod'], + handler, +}; + +async function handler() { const link = 'https://www.hpoi.net/bannerItem/list?categoryId=0&bannerItemType=0&subType=0&page=1'; const response = await got({ method: 'get', url: link, }); const $ = load(response.data); - ctx.set('data', { + return { title: `Hpoi 手办维基 - 热门推荐`, link, item: $('#content .item') @@ -22,5 +44,5 @@ export default async (ctx) => { }; }) .get(), - }); -}; + }; +} diff --git a/lib/routes/hpoi/character.ts b/lib/routes/hpoi/character.ts index 750af0c2b..4551de9be 100644 --- a/lib/routes/hpoi/character.ts +++ b/lib/routes/hpoi/character.ts @@ -1,3 +1,24 @@ +import { Route } from '@/types'; import { ProcessFeed } from './utils'; -export default async (ctx) => ctx.set('data', await ProcessFeed('character', ctx.req.param('id'), ctx.req.param('order'))); +export const route: Route = { + path: '/items/character/:id/:order?', + categories: ['program-update'], + example: '/hpoi/items/character/1035374', + parameters: { id: '角色 ID', order: '排序, 见下表,默认为 add' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '角色周边', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { + ctx.set('data', await ProcessFeed('character', ctx.req.param('id'), ctx.req.param('order'))); +} diff --git a/lib/routes/hpoi/info.ts b/lib/routes/hpoi/info.ts index 3dfb592f7..8da768c3a 100644 --- a/lib/routes/hpoi/info.ts +++ b/lib/routes/hpoi/info.ts @@ -1,8 +1,27 @@ +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: '/info/:type?', + categories: ['program-update'], + example: '/hpoi/info/all', + parameters: { type: '分类, 见下表, 默认为`all`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '情报', + maintainers: ['sanmmm DIYgod'], + handler, +}; + +async function handler(ctx) { const { type = 'all' } = ctx.req.param(); const baseUrl = 'https://www.hpoi.net'; const reqUrl = `${baseUrl}/user/home/ajax`; @@ -42,10 +61,10 @@ export default async (ctx) => { model: '模型', }; const title = `手办维基 - 情报 - ${typeToLabel[type]}`; - ctx.set('data', { + return { title, link: `${baseUrl}/user/home?type=info&catType=${type}`, description: title, item: items, - }); -}; + }; +} diff --git a/lib/routes/hpoi/user.ts b/lib/routes/hpoi/user.ts index 536283a73..32b270101 100644 --- a/lib/routes/hpoi/user.ts +++ b/lib/routes/hpoi/user.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -11,7 +12,25 @@ const titleMap = { resell: '有过', }; -export default async (ctx) => { +export const route: Route = { + path: '/user/:user_id/:caty', + categories: ['program-update'], + example: '/hpoi/user/116297/buy', + parameters: { user_id: '用户ID', caty: '类别, 见下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '用户动态', + maintainers: ['DIYgod', 'luyuhuang'], + handler, +}; + +async function handler(ctx) { const { user_id, caty } = ctx.req.param(); const url = `${root_url}/user/${user_id}/hobby?order=actionDate&view=2&favState=${caty}`; @@ -34,10 +53,10 @@ export default async (ctx) => { const title = $('.hpoi-collect-head .info p').eq(0).text() + '的手办 - ' + titleMap[caty]; - ctx.set('data', { + return { title, link: url, item: list, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/hpoi/work.ts b/lib/routes/hpoi/work.ts index 8a57246ca..992b7e61e 100644 --- a/lib/routes/hpoi/work.ts +++ b/lib/routes/hpoi/work.ts @@ -1,3 +1,24 @@ +import { Route } from '@/types'; import { ProcessFeed } from './utils'; -export default async (ctx) => ctx.set('data', await ProcessFeed('work', ctx.req.param('id'), ctx.req.param('order'))); +export const route: Route = { + path: '/items/work/:id/:order?', + categories: ['program-update'], + example: '/hpoi/items/work/4117491', + parameters: { id: '作品 ID', order: '排序, 见下表,默认为 add' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '作品周边', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { + ctx.set('data', await ProcessFeed('work', ctx.req.param('id'), ctx.req.param('order'))); +} diff --git a/lib/routes/hrbeu/gx/card.ts b/lib/routes/hrbeu/gx/card.ts index 0b9c8b4da..a02b6cfea 100644 --- a/lib/routes/hrbeu/gx/card.ts +++ b/lib/routes/hrbeu/gx/card.ts @@ -1,9 +1,17 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; const rootUrl = 'http://news.hrbeu.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/gx/card/:column/:id?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const column = ctx.req.param('column'); const id = ctx.req.param('id') || ''; const toUrl = id === '' ? `${rootUrl}/${column}.htm` : `${rootUrl}/${column}/${id}.htm`; @@ -29,9 +37,9 @@ export default async (ctx) => { })) .get(); - ctx.set('data', { + return { title: '工学-' + bigTitle, link: toUrl, item: card, - }); -}; + }; +} diff --git a/lib/routes/hrbeu/gx/list.ts b/lib/routes/hrbeu/gx/list.ts index 1e6c4835a..b8539ed4a 100644 --- a/lib/routes/hrbeu/gx/list.ts +++ b/lib/routes/hrbeu/gx/list.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 rootUrl = 'http://news.hrbeu.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/gx/list/:column/:id?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const column = ctx.req.param('column'); const id = ctx.req.param('id') || ''; const toUrl = id === '' ? `${rootUrl}/${column}.htm` : `${rootUrl}/${column}/${id}.htm`; @@ -53,9 +61,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '工学-' + bigTitle, link: toUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/hrbeu/job/bigemploy.ts b/lib/routes/hrbeu/job/bigemploy.ts index 777369c51..412e547fd 100644 --- a/lib/routes/hrbeu/job/bigemploy.ts +++ b/lib/routes/hrbeu/job/bigemploy.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: '/job/bigemploy', + categories: ['forecast'], + example: '/hrbeu/job/bigemploy', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['job.hrbeu.edu.cn/*'], + }, + name: 'Unknown', + maintainers: ['Derekmini'], + handler, +}; + +async function handler() { const response = await got('http://job.hrbeu.edu.cn/HrbeuJY/web'); const $ = load(response.data); @@ -16,10 +38,10 @@ export default async (ctx) => { })) .get(); - ctx.set('data', { + return { title: '大型招聘会', link: 'http://job.hrbeu.edu.cn/HrbeuJY/web', item: list, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/hrbeu/job/calendar.ts b/lib/routes/hrbeu/job/calendar.ts index 1327a051d..fdaf77ab1 100644 --- a/lib/routes/hrbeu/job/calendar.ts +++ b/lib/routes/hrbeu/job/calendar.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; const rootUrl = 'http://job.hrbeu.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/job/calendar', + categories: ['forecast'], + example: '/hrbeu/job/calendar', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['job.hrbeu.edu.cn/*'], + }, + name: '就业服务平台', + maintainers: ['Derekmini'], + handler, +}; + +async function handler() { const date = new Date(); const year = date.getFullYear(); const month = date.getMonth() + 1; @@ -37,10 +59,10 @@ export default async (ctx) => { })) .get(); - ctx.set('data', { + return { title: '今日招聘会', link: 'http://job.hrbeu.edu.cn/HrbeuJY/web', item: list, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/hrbeu/job/list.ts b/lib/routes/hrbeu/job/list.ts index a0a65bb67..00e8dc616 100644 --- a/lib/routes/hrbeu/job/list.ts +++ b/lib/routes/hrbeu/job/list.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 idMap = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/job/list/:id', + categories: ['forecast'], + example: '/hrbeu/job/list/tzgg', + parameters: { id: '栏目,如下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Unknown', + maintainers: ['Derekmini'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const response = await got(idMap[id].url, { @@ -58,9 +77,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '就业服务平台-' + idMap[id].name, link: idMap[id].url, item: items, - }); -}; + }; +} diff --git a/lib/routes/hrbeu/uae/news.ts b/lib/routes/hrbeu/uae/news.ts index 3df7c8db5..8ba263550 100644 --- a/lib/routes/hrbeu/uae/news.ts +++ b/lib/routes/hrbeu/uae/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 { parseDate } from '@/utils/parse-date'; import { finishArticleItem } from '@/utils/wechat-mp'; -export default async (ctx) => { +export const route: Route = { + path: '/uae/:id', + categories: ['forecast'], + example: '/hrbeu/uae/xwdt', + parameters: { id: '栏目编号,在 `URL` 中获取,如果有多级编号,将 `/` 替换为 `-`。' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['uae.hrbeu.edu.cn/:id.htm'], + }, + name: '水声工程学院', + maintainers: [], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id').replaceAll('-', '/'); const host = 'http://uae.hrbeu.edu.cn'; const url = `${host}/${id}.htm`; @@ -59,9 +81,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `水声工程学院 - ${title}`, link: url, item, - }); -}; + }; +} diff --git a/lib/routes/hrbeu/ugs/news.ts b/lib/routes/hrbeu/ugs/news.ts index 341016898..5ddae5a48 100644 --- a/lib/routes/hrbeu/ugs/news.ts +++ b/lib/routes/hrbeu/ugs/news.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -53,7 +54,29 @@ const authorMap = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/ugs/news/:author?/:category?', + categories: ['forecast'], + example: '/hrbeu/ugs/news/jwc/jxap', + parameters: { author: '发布部门,默认为 `gztz`', category: '分类,默认为 `all`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ugs.hrbeu.edu.cn/:author/list.htm'], + target: '/ugs/news/:author', + }, + name: '本科生院工作通知', + maintainers: ['XYenon'], + handler, +}; + +async function handler(ctx) { const author = ctx.req.param('author') || 'gztz'; const category = ctx.req.param('category') || 'all'; const link = baseUrl + authorMap[author][category] + '/list.htm'; @@ -91,9 +114,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '哈尔滨工程大学本科生院工作通知', link, item: out, - }); -}; + }; +} diff --git a/lib/routes/hrbeu/yjsy/list.ts b/lib/routes/hrbeu/yjsy/list.ts index bfdab7f0a..aa8622f21 100644 --- a/lib/routes/hrbeu/yjsy/list.ts +++ b/lib/routes/hrbeu/yjsy/list.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 rootUrl = 'http://yjsy.hrbeu.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/yjsy/list/:id', + categories: ['forecast'], + example: '/hrbeu/yjsy/list/2981', + parameters: { id: '栏目编号,由 `URL` 中获取。' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['yjsy.hrbeu.edu.cn/:id/list.htm'], + }, + name: '研究生院', + maintainers: ['Derekmini'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const response = await got(`${rootUrl}/${id}/list.htm`, { @@ -50,9 +72,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '研究生院-' + bigTitle, link: rootUrl.concat('/', id, '/list.htm'), item: items, - }); -}; + }; +} diff --git a/lib/routes/hrbust/jwzx.ts b/lib/routes/hrbust/jwzx.ts index 87d06e58b..41b9fcbf3 100644 --- a/lib/routes/hrbust/jwzx.ts +++ b/lib/routes/hrbust/jwzx.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import utils from './utils'; @@ -17,7 +18,25 @@ const typeMap = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/jwzx/:type?/:page?', + categories: ['forecast'], + example: '/hrbust/jwzx', + parameters: { type: '分类名,默认为教务公告', page: '文章数,默认为12' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '教务处', + maintainers: ['LenaNouzen'], + handler, +}; + +async function handler(ctx) { const page = ctx.req.param('page') || '12'; const base = utils.columnIdBase(ctx.req.param('type')) + '&pagingNumberPer=' + page; const res = await got(base); @@ -29,9 +48,9 @@ export default async (ctx) => { // info, // }; - ctx.set('data', { + return { title: '哈理工教务处' + typeMap[ctx.req.param('type') || 354].name, link: base, item: details, - }); -}; + }; +} diff --git a/lib/routes/huanqiu/index.ts b/lib/routes/huanqiu/index.ts index 17c5ee70f..a2205df8b 100644 --- a/lib/routes/huanqiu/index.ts +++ b/lib/routes/huanqiu/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,28 @@ function getKeysRecursive(dic, key, attr, array) { return array; } -export default async (ctx) => { +export const route: Route = { + path: '/news/:category?', + categories: ['bbs'], + example: '/huanqiu/news/china', + parameters: { category: '类别,可以使用二级域名作为参数,默认为:china' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['huanqiu.com/'], + }, + name: '分类', + maintainers: ['yuxinliu-alex'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'china'; if (!isValidHost(category)) { throw new Error('Invalid category'); @@ -63,11 +85,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${name} - 环球网`, link: host, description: '环球网', language: 'zh-cn', item: items, - }); -}; + }; +} diff --git a/lib/routes/huggingface/blog-zh.ts b/lib/routes/huggingface/blog-zh.ts index 3a770fef7..682202f70 100644 --- a/lib/routes/huggingface/blog-zh.ts +++ b/lib/routes/huggingface/blog-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: '/blog-zh', + categories: ['design'], + example: '/huggingface/blog-zh', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['huggingface.co/blog/zh', 'huggingface.co/'], + }, + name: '中文博客', + maintainers: ['zcf0508'], + handler, +}; + +async function handler() { const { body: response } = await got('https://huggingface.co/blog/zh'); const $ = load(response); @@ -26,9 +48,9 @@ export default async (ctx) => { author: item.blog.author, })); - ctx.set('data', { + return { title: 'Huggingface 中文博客', link: 'https://huggingface.co/blog/zh', item: items, - }); -}; + }; +} diff --git a/lib/routes/huggingface/daily-papers.ts b/lib/routes/huggingface/daily-papers.ts index 200d6d225..ad6350a58 100644 --- a/lib/routes/huggingface/daily-papers.ts +++ b/lib/routes/huggingface/daily-papers.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: '/daily-papers', + categories: ['design'], + example: '/huggingface/daily-papers', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['huggingface.co/papers', 'huggingface.co/'], + }, + name: 'Daily Papers', + maintainers: ['zeyugao'], + handler, +}; + +async function handler() { const { body: response } = await got('https://huggingface.co/papers'); const $ = load(response); const papers = $('main > div[data-target="DailyPapers"]').data('props'); @@ -18,9 +40,9 @@ export default async (ctx) => { })) .sort((a, b) => b.upvotes - a.upvotes); - ctx.set('data', { + return { title: 'Huggingface Daily Papers', link: 'https://huggingface.co/papers', item: items, - }); -}; + }; +} diff --git a/lib/routes/hunanpea/rsks.ts b/lib/routes/hunanpea/rsks.ts index a7ee231d0..45c11e9b8 100644 --- a/lib/routes/hunanpea/rsks.ts +++ b/lib/routes/hunanpea/rsks.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: '/rsks/:guid', + categories: ['journal'], + example: '/hunanpea/rsks/2f1a6239-b4dc-491b-92af-7d95e0f0543e', + parameters: { guid: '分类 id,可在 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['rsks.hunanpea.com/Category/:guid/ArticlesByCategory.do'], + }, + name: '公告', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'http://rsks.hunanpea.com'; const guid = ctx.req.param('guid'); const link = `${baseUrl}/Category/${guid}/ArticlesByCategory.do?PageIndex=1`; @@ -35,9 +57,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('.sitemap h2').text()} - ${$('head title').text()}`, link, item: items, - }); -}; + }; +} diff --git a/lib/routes/hunau/gfxy/index.ts b/lib/routes/hunau/gfxy/index.ts index 2465b91c9..63c7efb30 100644 --- a/lib/routes/hunau/gfxy/index.ts +++ b/lib/routes/hunau/gfxy/index.ts @@ -1,6 +1,29 @@ +import { Route } from '@/types'; import getContent from '../utils/common'; -export default async (ctx) => { +export const route: Route = { + path: '/gfxy/:category?/:page?', + categories: ['forecast'], + example: '/hunau/gfxy', + parameters: { category: '页面分类,默认为 `tzgg`', page: '页码,默认为 `1`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['xky.hunau.edu.cn/', 'xky.hunau.edu.cntzgg_8472', 'xky.hunau.edu.cn/:category'], + target: '/:category', + }, + name: '公共管理与法学学院', + maintainers: [], + handler, +}; + +async function handler(ctx) { await getContent(ctx, { baseHost: 'https://gfxy.hunau.edu.cn', baseCategory: 'tzgg', // 默认:通知公告 @@ -8,4 +31,4 @@ export default async (ctx) => { baseDescription: '湖南农业大学公共管理与法学学院', baseDeparment: 'gfxy', }); -}; +} diff --git a/lib/routes/hunau/ied.ts b/lib/routes/hunau/ied.ts index 49dd4bbeb..e2d6f03fa 100644 --- a/lib/routes/hunau/ied.ts +++ b/lib/routes/hunau/ied.ts @@ -1,6 +1,29 @@ +import { Route } from '@/types'; import getContent from './utils/common'; -export default async (ctx) => { +export const route: Route = { + path: '/ied/:type?/:category?/:page?', + categories: ['forecast'], + example: '/hunau/ied', + parameters: { type: '页面归属,默认为 `xwzx`', category: '页面分类,默认为 `ggtz`', page: '页码,默认为 `1`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['xky.hunau.edu.cn/', 'xky.hunau.edu.cntzgg_8472', 'xky.hunau.edu.cn/:category'], + target: '/:category', + }, + name: '国际交流与合作处、国际教育学院、港澳台事务办公室', + maintainers: ['lcandy2'], + handler, +}; + +async function handler(ctx) { await getContent(ctx, { baseHost: 'https://ied.hunau.edu.cn', baseCategory: 'ggtz', // 默认:公告通知 @@ -9,4 +32,4 @@ export default async (ctx) => { baseDescription: '湖南农业大学国际交流与合作处、国际教育学院、港澳台事务办公室', baseDeparment: 'ied', }); -}; +} diff --git a/lib/routes/hunau/jwc.ts b/lib/routes/hunau/jwc.ts index 6f1c99b38..a51864469 100644 --- a/lib/routes/hunau/jwc.ts +++ b/lib/routes/hunau/jwc.ts @@ -1,10 +1,33 @@ +import { Route } from '@/types'; import getContent from './utils/common'; -export default async (ctx) => { +export const route: Route = { + path: '/jwc/:category?/:page?', + categories: ['forecast'], + example: '/hunau/jwc', + parameters: { category: '页面分类,默认为 `tzgg`', page: '页码,默认为 `1`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['xky.hunau.edu.cn/', 'xky.hunau.edu.cntzgg_8472', 'xky.hunau.edu.cn/:category'], + target: '/:category', + }, + name: '教务处', + maintainers: [], + handler, +}; + +async function handler(ctx) { await getContent(ctx, { baseHost: 'https://jwc.hunau.edu.cn', baseCategory: 'tzgg', // 默认:通知公告 baseTitle: '湖南农业大学教务处', baseDeparment: 'jwc', }); -}; +} diff --git a/lib/routes/hunau/utils/common.ts b/lib/routes/hunau/utils/common.ts index 41895c35c..b75af647c 100644 --- a/lib/routes/hunau/utils/common.ts +++ b/lib/routes/hunau/utils/common.ts @@ -48,7 +48,7 @@ async function getContent(ctx, { baseHost, baseCategory, baseType, baseTitle, ba ) ); - ctx.set('data', { + return { // 源标题 title, description, @@ -56,7 +56,7 @@ async function getContent(ctx, { baseHost, baseCategory, baseType, baseTitle, ba link: url, // 源文章 item: items, - }); + }; } export default getContent; diff --git a/lib/routes/hunau/xky/index.ts b/lib/routes/hunau/xky/index.ts index 6291dcdbb..a3796c314 100644 --- a/lib/routes/hunau/xky/index.ts +++ b/lib/routes/hunau/xky/index.ts @@ -1,6 +1,29 @@ +import { Route } from '@/types'; import getContent from '../utils/common'; -export default async (ctx) => { +export const route: Route = { + path: '/xky/:category?/:page?', + categories: ['forecast'], + example: '/hunau/xky', + parameters: { category: '页面分类,默认为 `tzgg_8472`', page: '页码,默认为 `1`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['xky.hunau.edu.cn/', 'xky.hunau.edu.cntzgg_8472', 'xky.hunau.edu.cn/:category'], + target: '/:category', + }, + name: '信息与智能科学学院', + maintainers: [], + handler, +}; + +async function handler(ctx) { await getContent(ctx, { baseHost: 'https://xky.hunau.edu.cn', baseCategory: 'tzgg_8472', // 默认:通知公告 @@ -9,4 +32,4 @@ export default async (ctx) => { baseDeparment: 'xky', baseClass: 'div.right_list ul li:has(a)', }); -}; +} diff --git a/lib/routes/huoxian/zone.ts b/lib/routes/huoxian/zone.ts index 5e7501a79..935a69f04 100644 --- a/lib/routes/huoxian/zone.ts +++ b/lib/routes/huoxian/zone.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: '/zone', + categories: ['blog'], + example: '/huoxian/zone', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Zone', + maintainers: ['p7e4'], + handler, +}; + +async function handler() { const { data } = await got('https://zone.huoxian.cn/api/discussions?sort=-createdAt'); const items = data.data.map((item) => ({ title: item.attributes.title, @@ -11,9 +30,9 @@ export default async (ctx) => { author: data.included.find((i) => i.id === item.relationships.user.data.id).attributes.displayName, category: data.included.filter((i) => item.relationships.tags.data.map((t) => t.id).includes(i.id) && i.type === 'tags').map((i) => i.attributes.name), })); - ctx.set('data', { + return { title: '火线 Zone-安全攻防社区', link: 'https://zone.huoxian.cn/', item: items, - }); -}; + }; +} diff --git a/lib/routes/hupu/all.ts b/lib/routes/hupu/all.ts index 448ca9b79..61262ef77 100644 --- a/lib/routes/hupu/all.ts +++ b/lib/routes/hupu/all.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 { parseDate, parseRelativeDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/all/:id?', + categories: ['blog'], + example: '/hupu/all/topic-daily', + parameters: { id: '编号,可在对应热帖版面 URL 中找到,默认为步行街每日话题' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['m.hupu.com/:category', 'm.hupu.com/'], + target: '/:category', + }, + name: '热帖', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? 'topic-daily'; const rootUrl = 'https://bbs.hupu.com'; @@ -69,9 +92,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `虎扑社区 - ${$('.middle-title, .bbs-sl-web-intro-detail-title').text()}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/hupu/bbs.ts b/lib/routes/hupu/bbs.ts index a91d07b8c..7ce517175 100644 --- a/lib/routes/hupu/bbs.ts +++ b/lib/routes/hupu/bbs.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 { 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: ['/bbs/:id?/:order?', '/bxj/:id?/:order?'], + categories: ['blog'], + example: '/hupu/bbs/topic-daily', + parameters: { id: '编号,可在对应社区 URL 中找到,默认为#步行街主干道', order: '排序方式,可选 `0` 即 最新回复 或 `1` 即 最新发布,默认为最新回复' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['m.hupu.com/:category', 'm.hupu.com/'], + target: '/:category', + }, + name: '社区', + maintainers: ['LogicJake', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? '34'; const order = ctx.req.param('order') ?? '1'; @@ -80,10 +103,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `虎扑社区 - ${$('.bbs-sl-web-intro-detail-title').text()}`, link: currentUrl, item: items, description: $('.bbs-sl-web-intro-detail-desc-text').first().text(), - }); -}; + }; +} diff --git a/lib/routes/hupu/index.ts b/lib/routes/hupu/index.ts index f2ce8a3cb..ece049917 100644 --- a/lib/routes/hupu/index.ts +++ b/lib/routes/hupu/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,18 @@ const categories = { }, }; -export default async (ctx) => { +export const route: Route = { + path: ['/dept/:category?', '/:category?'], + radar: { + source: ['m.hupu.com/:category', 'm.hupu.com/'], + target: '/:category', + }, + name: 'Unknown', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'soccer'; const rootUrl = 'https://m.hupu.com'; @@ -73,9 +85,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `虎扑 - ${categories[category].title}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/hust/aia/news.ts b/lib/routes/hust/aia/news.ts index 10996518c..547e3186b 100755 --- a/lib/routes/hust/aia/news.ts +++ b/lib/routes/hust/aia/news.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: ['/aia/news', '/auto/news'], + categories: ['forecast'], + example: '/hust/aia/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['aia.hust.edu.cn/xyxw.htm', 'aia.hust.edu.cn/'], + }, + name: '人工智能和自动化学院新闻', + maintainers: ['budui'], + handler, +}; + +async function handler() { const link = 'https://aia.hust.edu.cn/xyxw.htm'; const response = await got(link); const $ = load(response.data); const list = $('.list li'); - ctx.set('data', { + return { title: '华科人工智能和自动化学院新闻', link, item: @@ -22,5 +44,5 @@ export default async (ctx) => { link: new URL(item.find('a').attr('href'), link).href, }; }), - }); -}; + }; +} diff --git a/lib/routes/hust/aia/notice.ts b/lib/routes/hust/aia/notice.ts index c4e3d96d4..c9e00917d 100755 --- a/lib/routes/hust/aia/notice.ts +++ b/lib/routes/hust/aia/notice.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: ['/aia/notice/:type?', '/auto/notice/:type?'], + categories: ['forecast'], + example: '/hust/aia/notice', + parameters: { type: '分区,默认为最新通知,可在网页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '人工智能和自动化学院通知', + maintainers: ['budui'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); const baseUrl = 'https://aia.hust.edu.cn'; const link = `${baseUrl}/tzgg${type ? `/${type}` : ''}.htm`; @@ -11,7 +30,7 @@ export default async (ctx) => { const list = $('.list li'); const title = $('title').text(); - ctx.set('data', { + return { title, link, item: @@ -25,5 +44,5 @@ export default async (ctx) => { link: new URL(item.find('a').attr('href'), link).href, }; }), - }); -}; + }; +} diff --git a/lib/routes/hust/yjs.ts b/lib/routes/hust/yjs.ts index f3e071cfb..23755db5c 100644 --- a/lib/routes/hust/yjs.ts +++ b/lib/routes/hust/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: '/hust/yjs', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gszs.hust.edu.cn/zsxx/ggtz.htm', 'gszs.hust.edu.cn/'], + }, + name: '研究生院通知公告', + maintainers: ['shengmaosu'], + handler, +}; + +async function handler() { const link = 'https://gszs.hust.edu.cn/zsxx/ggtz.htm'; const response = await got(link); const $ = load(response.data); const list = $('.main_conRCb 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/huxiu/brief-column.ts b/lib/routes/huxiu/brief-column.ts index 05e0ba346..14e4920b7 100644 --- a/lib/routes/huxiu/brief-column.ts +++ b/lib/routes/huxiu/brief-column.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { apiBriefRootUrl, processItems, fetchBriefColumnData } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/briefcolumn/:id', + categories: ['traditional-media'], + example: '/huxiu/briefcolumn/1', + parameters: { id: '简报 id,可在对应简报页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: true, + supportPodcast: true, + supportScihub: false, + }, + name: '简报', + maintainers: ['Fatpandac', '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) : 20; @@ -23,8 +42,8 @@ export default async (ctx) => { const data = await fetchBriefColumnData(id); - ctx.set('data', { + return { item: items, ...data, - }); -}; + }; +} diff --git a/lib/routes/huxiu/channel.ts b/lib/routes/huxiu/channel.ts index 14c33d6f9..3d60a0043 100644 --- a/lib/routes/huxiu/channel.ts +++ b/lib/routes/huxiu/channel.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { rootUrl, apiArticleRootUrl, processItems, fetchData } from './util'; -export default async (ctx) => { +export const route: Route = { + path: ['/article', '/channel/:id?'], + categories: ['traditional-media'], + example: '/huxiu/article', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: true, + supportPodcast: true, + supportScihub: false, + }, + radar: { + source: ['huxiu.com/article'], + }, + 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) : 20; @@ -22,8 +44,8 @@ export default async (ctx) => { const data = await fetchData(currentUrl); - ctx.set('data', { + return { item: items, ...data, - }); -}; + }; +} diff --git a/lib/routes/huxiu/club.ts b/lib/routes/huxiu/club.ts index 97f39667b..3714685fa 100644 --- a/lib/routes/huxiu/club.ts +++ b/lib/routes/huxiu/club.ts @@ -1,9 +1,17 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { apiBriefRootUrl, processItems, fetchClubData } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/club/:id', + name: 'Unknown', + 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) : 20; @@ -24,8 +32,8 @@ export default async (ctx) => { const items = await processItems(response.data.datalist, limit, cache.tryGet); - ctx.set('data', { + return { item: items, ...data, - }); -}; + }; +} diff --git a/lib/routes/huxiu/collection.ts b/lib/routes/huxiu/collection.ts index 440a76fcf..55c9355d4 100644 --- a/lib/routes/huxiu/collection.ts +++ b/lib/routes/huxiu/collection.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { rootUrl, apiArticleRootUrl, processItems, fetchData } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/collection/:id', + categories: ['traditional-media'], + example: '/huxiu/collection/212', + parameters: { id: '文集 id,可在对应文集页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: true, + supportPodcast: true, + supportScihub: false, + }, + name: '文集', + maintainers: ['AlexdanerZe', '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) : 10; @@ -21,8 +40,8 @@ export default async (ctx) => { const data = await fetchData(currentUrl); - ctx.set('data', { + return { item: items, ...data, - }); -}; + }; +} diff --git a/lib/routes/huxiu/member.ts b/lib/routes/huxiu/member.ts index dc08e1cd3..595438215 100644 --- a/lib/routes/huxiu/member.ts +++ b/lib/routes/huxiu/member.ts @@ -1,9 +1,17 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { rootUrl, apiMemberRootUrl, processItems, fetchData } from './util'; -export default async (ctx) => { +export const route: Route = { + path: ['/author/:id/:type?', '/member/:id/:type?'], + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { id, type = 'article' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 10; @@ -21,8 +29,8 @@ export default async (ctx) => { const data = await fetchData(currentUrl); - ctx.set('data', { + return { item: items, ...data, - }); -}; + }; +} diff --git a/lib/routes/huxiu/moment.ts b/lib/routes/huxiu/moment.ts index dfc48076e..aed13a6b9 100644 --- a/lib/routes/huxiu/moment.ts +++ b/lib/routes/huxiu/moment.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { rootUrl, apiMomentRootUrl, processItems, fetchData } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/moment', + categories: ['traditional-media'], + example: '/huxiu/moment', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: true, + supportPodcast: true, + supportScihub: false, + }, + radar: { + source: ['huxiu.com/moment'], + }, + name: '24 小时', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 20; const apiUrl = new URL('web-v2/moment/feed', apiMomentRootUrl).href; @@ -19,8 +41,8 @@ export default async (ctx) => { const data = await fetchData(currentUrl); - ctx.set('data', { + return { item: items, ...data, - }); -}; + }; +} diff --git a/lib/routes/huxiu/search.ts b/lib/routes/huxiu/search.ts index ecc20edfe..bf3992359 100644 --- a/lib/routes/huxiu/search.ts +++ b/lib/routes/huxiu/search.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { rootUrl, apiSearchRootUrl, generateSignature, processItems, fetchData } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/search/:keyword', + categories: ['traditional-media'], + example: '/huxiu/search/生活', + parameters: { keyword: '关键字' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: true, + supportPodcast: true, + supportScihub: false, + }, + radar: { + source: ['huxiu.com/'], + }, + name: '搜索', + maintainers: ['xyqfer', 'HenryQW', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const keyword = ctx.req.param('keyword'); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 20; @@ -29,8 +51,8 @@ export default async (ctx) => { ctx.set('json', response.data.datalist); - ctx.set('data', { + return { item: items, ...data, - }); -}; + }; +} diff --git a/lib/routes/huxiu/tag.ts b/lib/routes/huxiu/tag.ts index 1e7a9f233..f3ae8cb97 100644 --- a/lib/routes/huxiu/tag.ts +++ b/lib/routes/huxiu/tag.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { rootUrl, processItems, fetchData } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/tag/:id', + categories: ['traditional-media'], + example: '/huxiu/tag/291', + parameters: { id: '标签 id,可在对应标签页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: true, + supportPodcast: true, + supportScihub: false, + }, + name: '标签', + maintainers: ['xyqfer', '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) : 10; @@ -20,8 +39,8 @@ export default async (ctx) => { const data = await fetchData(currentUrl); - ctx.set('data', { + return { item: items, ...data, - }); -}; + }; +} diff --git a/lib/routes/ianspriggs/index.ts b/lib/routes/ianspriggs/index.ts index c58f0fe08..2d5c63529 100644 --- a/lib/routes/ianspriggs/index.ts +++ b/lib/routes/ianspriggs/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: ['programming'], + example: '/ianspriggs/portraits', + parameters: { category: 'Category, see below, 3D PORTRAITS 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 = 'portraits' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; @@ -80,7 +99,7 @@ export default async (ctx) => { const icon = new URL('favicon.ico', rootUrl).href; - ctx.set('data', { + return { item: items, title: $('title').text(), link: currentUrl, @@ -90,5 +109,5 @@ export default async (ctx) => { logo: icon, subtitle: $('a[aria-current="page"] span.menu-title-text').text(), author, - }); -}; + }; +} diff --git a/lib/routes/icac/news.ts b/lib/routes/icac/news.ts index a2e797e00..18db18775 100644 --- a/lib/routes/icac/news.ts +++ b/lib/routes/icac/news.ts @@ -1,10 +1,33 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import utils from './utils'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/news/:lang?', + categories: ['study'], + example: '/icac/news/sc', + parameters: { lang: 'Language, default to `sc`. Supprot `en`(English), `sc`(Simplified Chinese) and `tc`(Traditional Chinese)' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['icac.org.hk/:lang/press/index.html'], + target: '/news/:lang', + }, + name: 'Press Releases', + maintainers: ['linbuxiao, TonyRL'], + handler, +}; + +async function handler(ctx) { const BASE_WITH_LANG = utils.langBase(ctx.req.param('lang')); const res = await got.get(`${BASE_WITH_LANG}/press/index.html`); const $ = load(res.data); @@ -37,11 +60,11 @@ export default async (ctx) => { }) ) ); - ctx.set('data', { + return { title: 'ICAC 新闻公布', link: `${BASE_WITH_LANG}/press/index.html`, description: 'ICAC 新闻公布', language: ctx.req.param('lang') ? utils.LANG_TYPE[ctx.req.param('lang')] : utils.LANG_TYPE.sc, item: items, - }); -}; + }; +} diff --git a/lib/routes/icbc/whpj.ts b/lib/routes/icbc/whpj.ts index 69d65707a..16b0265a1 100644 --- a/lib/routes/icbc/whpj.ts +++ b/lib/routes/icbc/whpj.ts @@ -1,12 +1,35 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/whpj/:format?', + categories: ['other'], + example: '/icbc/whpj/zs?filter_title=%E8%8B%B1%E9%95%91', + parameters: { format: '输出的标题格式,默认为标题 + 所有价格。短格式仅包含货币名称。' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['icbc.com.cn/column/1438058341489590354.html'], + target: '/whpj', + }, + name: '外汇牌价', + maintainers: ['leoleoasd'], + handler, +}; + +async function handler(ctx) { // yes, insecure // their openssl version isn't safe anyway (https://stackoverflow.com/questions/75763525/curl-35-error0a000152ssl-routinesunsafe-legacy-renegotiation-disabled) const { data } = await got('http://papi.icbc.com.cn/exchanges/ns/getLatest'); const format = ctx.req.param('format'); - ctx.set('data', { + return { title: '中国工商银行外汇牌价', link: 'https://www.icbc.com.cn/column/1438058341489590354.html', item: data.data.map((item) => { @@ -53,5 +76,5 @@ export default async (ctx) => { guid: `${name} ${item.publishDate} ${item.publishTime}`, }; }), - }); -}; + }; +} diff --git a/lib/routes/idaily/index.ts b/lib/routes/idaily/index.ts index a881ebe27..e7225e72a 100644 --- a/lib/routes/idaily/index.ts +++ b/lib/routes/idaily/index.ts @@ -1,10 +1,18 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; 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: ['/today/:language?', '/:language?'], + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { language = 'zh-hans' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 100; @@ -51,7 +59,7 @@ export default async (ctx) => { const title = $('title').text(); const image = new URL('img/idaily/logo_2x.png', currentUrl).href; - ctx.set('data', { + return { item: items, title, link: currentUrl, @@ -61,5 +69,5 @@ export default async (ctx) => { subtitle: $('meta[name="keywords"]').prop('content'), author: title.split(/\s/)[0], allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/idolypride/news.ts b/lib/routes/idolypride/news.ts index ff6325cf2..28f8e84eb 100644 --- a/lib/routes/idolypride/news.ts +++ b/lib/routes/idolypride/news.ts @@ -1,15 +1,37 @@ +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: '/news', + categories: ['program-update'], + example: '/idolypride/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['idolypride.jp/news'], + }, + name: 'News', + maintainers: ['Mingxia1'], + handler, +}; + +async function handler() { const response = await got({ method: 'get', url: 'https://idolypride.jp/wp-json/wp/v2/news', }); const list = response.data; - ctx.set('data', { + return { title: '偶像荣耀-新闻', link: 'https://idolypride.jp/news', item: list.map((item) => { @@ -25,5 +47,5 @@ export default async (ctx) => { description: rendered, }; }), - }); -}; + }; +} diff --git a/lib/routes/ieee-security/sp.ts b/lib/routes/ieee-security/sp.ts index 388bd5446..4a071f38e 100644 --- a/lib/routes/ieee-security/sp.ts +++ b/lib/routes/ieee-security/sp.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 url = 'https://www.ieee-security.org/'; // https://www.ieee-security.org/TC/SP2023/program-papers.html -export default async (ctx) => { +export const route: Route = { + path: '/security-privacy', + categories: ['finance'], + example: '/ieee-security/security-privacy', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ieee-security.org/TC/SP-Index.html', 'ieee-security.org/'], + }, + name: 'IEEE Symposium on Security and Privacy', + maintainers: ['ZeddYu'], + handler, +}; + +async function handler() { const last = new Date().getFullYear() + 1; const urlList = Array.from({ length: last - 2020 }, (_, v) => `${url}TC/SP${v + 2020}/program-papers.html`); const responses = await got.all(urlList.map((url) => got(url))); @@ -26,11 +48,11 @@ export default async (ctx) => { }); }); - ctx.set('data', { + return { title: 'S&P', link: `${url}TC/SP-Index.html`, description: 'IEEE Symposium on Security and Privacy Accepted Papers', allowEmpty: true, item: items.flat(), - }); -}; + }; +} diff --git a/lib/routes/ieee/earlyaccess.ts b/lib/routes/ieee/earlyaccess.ts index ca9367a75..ae603183e 100644 --- a/lib/routes/ieee/earlyaccess.ts +++ b/lib/routes/ieee/earlyaccess.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 { CookieJar } from 'tough-cookie'; const cookieJar = new CookieJar(); -export default async (ctx) => { +export const route: Route = { + path: '/journal/:journal/earlyaccess/:sortType?', + categories: ['finance'], + example: '/ieee/journal/5306045/earlyaccess', + parameters: { journal: 'Issue code, the number of the `isnumber` in the URL', sortType: 'Sort Type, default: `vol-only-seq`, the part of the URL after `sortType`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Early Access Journal', + maintainers: ['5upernova-heng'], + handler, +}; + +async function handler(ctx) { const isnumber = ctx.req.param('journal'); const sortType = ctx.req.param('sortType') ?? 'vol-only-seq'; const host = 'https://ieeexplore.ieee.org'; @@ -73,9 +92,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: jrnlName, link: jrnlUrl, item: list, - }); -}; + }; +} diff --git a/lib/routes/ieee/journal.ts b/lib/routes/ieee/journal.ts index 7a3f7224e..a90a5533b 100644 --- a/lib/routes/ieee/journal.ts +++ b/lib/routes/ieee/journal.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 { art } from '@/utils/render'; import { CookieJar } from 'tough-cookie'; const cookieJar = new CookieJar(); -export default async (ctx) => { +export const route: Route = { + path: ['/:journal/latest/vol/:sortType?', '/journal/:journal/:sortType?'], + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const punumber = ctx.req.param('journal'); const sortType = ctx.req.param('sortType') ?? 'vol-only-seq'; const host = 'https://ieeexplore.ieee.org'; @@ -74,9 +82,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: jrnlName, link: jrnlUrl, item: list, - }); -}; + }; +} diff --git a/lib/routes/ieee/recent.ts b/lib/routes/ieee/recent.ts index f907af287..0b848ea92 100644 --- a/lib/routes/ieee/recent.ts +++ b/lib/routes/ieee/recent.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 { art } from '@/utils/render'; import { CookieJar } from 'tough-cookie'; const cookieJar = new CookieJar(); -export default async (ctx) => { +export const route: Route = { + path: ['/:journal/latest/date/:sortType?', '/journal/:journal/recent/:sortType?'], + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const punumber = ctx.req.param('journal'); const sortType = ctx.req.param('sortType') ?? 'vol-only-seq'; const host = 'https://ieeexplore.ieee.org'; @@ -91,9 +99,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${jrnlName} - Recent`, link: jrnlUrl, item: list, - }); -}; + }; +} diff --git a/lib/routes/ielts/index.ts b/lib/routes/ielts/index.ts index 6ccb03b76..860ed2536 100644 --- a/lib/routes/ielts/index.ts +++ b/lib/routes/ielts/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { load } from 'cheerio'; import got from '@/utils/got'; @@ -7,7 +8,18 @@ const targetUrl = 'https://ielts.neea.cn/allnews?locale=zh_CN'; import { config } from '@/config'; import puppeteer from '@/utils/puppeteer'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['ielts.neea.cn/allnews'], + target: '', + }, + name: 'Unknown', + maintainers: ['zenxds'], + handler, +}; + +async function handler() { const html = await cache.tryGet( targetUrl, async () => { @@ -58,9 +70,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'IELTS雅思最新消息', link: targetUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/ifeng/feng.ts b/lib/routes/ifeng/feng.ts index 4f3ca676c..f48e758a7 100644 --- a/lib/routes/ifeng/feng.ts +++ b/lib/routes/ifeng/feng.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 { extractDoc, renderVideo } from './utils'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -export default async (ctx) => { +export const route: Route = { + path: '/feng/:id/:type', + categories: ['traditional-media'], + example: '/ifeng/feng/2583/doc', + parameters: { id: '对应 id,可在 大风号作者页面 找到', type: '类型,见下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '大风号', + maintainers: ['Jamch'], + handler, +}; + +async function handler(ctx) { const { id, type } = ctx.req.param(); const { data: userResponse } = await got(`https://ishare.ifeng.com/mediaShare/home/${id}/media`, { headers: { @@ -60,11 +79,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `大风号-${mediaName}-${type === 'doc' ? '文章' : '视频'}`, description: `${honorDesc} ${description}`, image: `https:${logo}`, link: `https://ishare.ifeng.com/mediaShare/home/${id}/media`, item: items, - }); -}; + }; +} diff --git a/lib/routes/ifeng/news.ts b/lib/routes/ifeng/news.ts index d2fa0942a..b69703d35 100644 --- a/lib/routes/ifeng/news.ts +++ b/lib/routes/ifeng/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 { 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/*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 20; const rootUrl = 'https://news.ifeng.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/ifi-audio/download.ts b/lib/routes/ifi-audio/download.ts index a62a3f5e1..8f461ddab 100644 --- a/lib/routes/ifi-audio/download.ts +++ b/lib/routes/ifi-audio/download.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; const host = 'https://ifi-audio.com'; -export default async (ctx) => { +export const route: Route = { + path: '/download/:val/:id', + categories: ['university'], + example: '/ifi-audio/download/1503007035/44472', + parameters: { val: 'product val', id: 'product id' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Download Hub', + maintainers: ['EthanWng97'], + handler, +}; + +async function handler(ctx) { const { val, id } = ctx.req.param(); const url = host + '/wp-admin/admin-ajax.php'; @@ -19,7 +38,7 @@ export default async (ctx) => { const $ = load(markup); const latestTitle = $('li[data-category=firmware]:first h4').text(); const latestDownloadLink = $('li[data-category=firmware]:first a').attr('href'); - ctx.set('data', { + return { title: 'iFi audio Download Hub', link: 'https://ifi-audio.com/download-hub/', description: 'iFi audio Download Hub', @@ -30,5 +49,5 @@ export default async (ctx) => { link: latestDownloadLink, }, ], - }); -}; + }; +} diff --git a/lib/routes/iguoguo/index.ts b/lib/routes/iguoguo/index.ts index ce10d89ad..7ec238542 100644 --- a/lib/routes/iguoguo/index.ts +++ b/lib/routes/iguoguo/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 getComments = (a, node) => { } }; -export default async (ctx) => { +export const route: Route = { + path: '/html5', + categories: ['live'], + example: '/iguoguo/html5', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '最新 H5', + maintainers: ['yuxinliu-alex'], + handler, +}; + +async function handler() { const currentUrl = rootUrl.concat('/html5'); const response = await got({ method: 'get', @@ -66,11 +85,11 @@ export default async (ctx) => { }) ) ); - ctx.set('data', { + return { title: '爱果果', link: currentUrl, description: '爱果果iguoguo是一个优秀酷站、h5、UI素材资源的发布分享平台,是设计师的灵感聚合地和素材下载源。', language: 'zh-cn', item: items, - }); -}; + }; +} diff --git a/lib/routes/iiilab/index.ts b/lib/routes/iiilab/index.ts index 9ea0a4522..f241e8fff 100644 --- a/lib/routes/iiilab/index.ts +++ b/lib/routes/iiilab/index.ts @@ -1,7 +1,19 @@ +import { Route } from '@/types'; import buildData from '@/utils/common-config'; const baseUrl = 'https://www.iiilab.com/'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['www.iiilab.com/'], + target: '', + }, + name: 'Unknown', + maintainers: ['Joey'], + handler, +}; + +async function handler(ctx) { const link = baseUrl; ctx.set( 'data', @@ -24,4 +36,4 @@ export default async (ctx) => { }, }) ); -}; +} diff --git a/lib/routes/ikea/cn/family-offers.ts b/lib/routes/ikea/cn/family-offers.ts index fa77af05d..8492c24e9 100644 --- a/lib/routes/ikea/cn/family-offers.ts +++ b/lib/routes/ikea/cn/family-offers.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { generateProductItem } from './utils'; @@ -32,7 +33,28 @@ const productRequest = async (productIds) => { return response.data; }; -export default async (ctx) => { +export const route: Route = { + path: '/cn/family_offers', + categories: ['game'], + example: '/ikea/cn/family_offers', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ikea.cn/cn/zh/offers/family-offers', 'ikea.cn/'], + }, + name: '中国 - 会员特惠', + maintainers: ['jzhangdev'], + handler, +}; + +async function handler() { const familyPriceProductIds = []; const productRequests = []; @@ -61,10 +83,10 @@ export default async (ctx) => { const productResponses = await Promise.all(productRequests); const products = productResponses.flat(); - ctx.set('data', { + return { title: 'IKEA 宜家 - 会员特惠', link: 'https://www.ikea.cn/cn/zh/offers/family-offers/', description: '会员特惠', item: products.map((element) => generateProductItem(element)), - }); -}; + }; +} diff --git a/lib/routes/ikea/cn/low-price.ts b/lib/routes/ikea/cn/low-price.ts index aa89b2b43..8341f02d4 100644 --- a/lib/routes/ikea/cn/low-price.ts +++ b/lib/routes/ikea/cn/low-price.ts @@ -1,7 +1,29 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { generateRequestHeaders, generateProductItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/cn/low_price', + categories: ['game'], + example: '/ikea/cn/low_price', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ikea.cn/cn/zh/campaigns/wo3-men2-de-chao1-zhi2-di1-jia4-pub8b08af40', 'ikea.cn/'], + }, + name: '中国 - 低价优选', + maintainers: ['jzhangdev'], + handler, +}; + +async function handler() { const response = await got({ url: 'https://srv.app.ikea.cn/content/recommendation/v2/product-group/products', headers: generateRequestHeaders(), @@ -13,10 +35,10 @@ export default async (ctx) => { }, }); - ctx.set('data', { + return { title: 'IKEA 宜家 - 低价优选', link: 'https://www.ikea.cn/cn/zh/campaigns/wo3-men2-de-chao1-zhi2-di1-jia4-pub8b08af40', description: '低价优选', item: response.data.products.map((element) => generateProductItem(element)), - }); -}; + }; +} diff --git a/lib/routes/ikea/cn/new.ts b/lib/routes/ikea/cn/new.ts index ffaf7d143..b3497bf36 100644 --- a/lib/routes/ikea/cn/new.ts +++ b/lib/routes/ikea/cn/new.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { generateRequestHeaders, generateProductItem } from './utils'; @@ -13,7 +14,28 @@ const request = ({ moreToken = '' }) => json: {}, }); -export default async (ctx) => { +export const route: Route = { + path: '/cn/new', + categories: ['game'], + example: '/ikea/cn/new', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ikea.cn/cn/zh/new/', 'ikea.cn/'], + }, + name: '中国 - 当季新品推荐', + maintainers: ['jzhangdev'], + handler, +}; + +async function handler() { const allProductSummaries = []; const loadMoreRequest = async ({ moreToken }) => { @@ -30,10 +52,10 @@ export default async (ctx) => { const products = allProductSummaries.flat(); - ctx.set('data', { + return { title: 'IKEA 宜家 - 当季新品推荐', link: 'https://www.ikea.cn/cn/zh/new/', description: '当季新品推荐', item: products.map((element) => generateProductItem(element)), - }); -}; + }; +} diff --git a/lib/routes/ikea/gb/new.ts b/lib/routes/ikea/gb/new.ts index 0b0f432d3..07aec7b61 100644 --- a/lib/routes/ikea/gb/new.ts +++ b/lib/routes/ikea/gb/new.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -5,7 +6,28 @@ 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: '/gb/new', + categories: ['game'], + example: '/ikea/gb/new', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ikea.com/gb/en/new/new-products/', 'ikea.com/'], + }, + name: 'UK - New Product Release', + maintainers: ['HenryQW'], + handler, +}; + +async function handler() { const link = 'https://www.ikea.com/gb/en/new/new-products/'; const { @@ -48,10 +70,10 @@ export default async (ctx) => { category: p.categoryPath.map((c) => c.name), })); - ctx.set('data', { + return { title: 'New Products - Browse All New Furniture & Home Decor - IKEA', link, description: 'New products released by IKEA UK.', item: items, - }); -}; + }; +} diff --git a/lib/routes/ikea/gb/offer.ts b/lib/routes/ikea/gb/offer.ts index 276853ac3..14e40045d 100644 --- a/lib/routes/ikea/gb/offer.ts +++ b/lib/routes/ikea/gb/offer.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 { load } from 'cheerio'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/gb/offer', + categories: ['game'], + example: '/ikea/gb/offer', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ikea.com/gb/en/offers', 'ikea.com/'], + }, + name: 'UK - Offers', + maintainers: ['HenryQW'], + handler, +}; + +async function handler() { const link = 'https://www.ikea.com/gb/en/offers/'; const response = await got(link); @@ -63,10 +85,10 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: 'IKEA UK - Offers', link, description: 'Offers by IKEA UK.', item: [...carousel, ...banner], - }); -}; + }; +} diff --git a/lib/routes/imagemagick/changelog.ts b/lib/routes/imagemagick/changelog.ts index a0e6ce4fe..2629887b4 100644 --- a/lib/routes/imagemagick/changelog.ts +++ b/lib/routes/imagemagick/changelog.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; @@ -6,7 +7,28 @@ const md = MarkdownIt({ html: true, }); -export default async (ctx) => { +export const route: Route = { + path: '/changelog', + categories: ['university'], + example: '/imagemagick/changelog', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['imagemagick.org/script/download.php', 'imagemagick.org/script', 'imagemagick.org/'], + }, + name: 'Changelog', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://imagemagick.org'; const currentUrl = `${rootUrl}/script/download.php`; const logUrl = 'https://github.com/ImageMagick/Website/blob/main/ChangeLog.md'; @@ -39,9 +61,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: 'ImageMagick - ChangeLog', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/imiker/jinghua.ts b/lib/routes/imiker/jinghua.ts index 9346afdb0..5b521fe6e 100644 --- a/lib/routes/imiker/jinghua.ts +++ b/lib/routes/imiker/jinghua.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: '/ask/jinghua', + categories: ['traditional-media'], + example: '/imiker/ask/jinghua', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['imiker.com/explore/find'], + }, + name: '米课圈精华', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 50; const rootUrl = 'https://ask.imiker.com'; @@ -76,7 +98,7 @@ export default async (ctx) => { const description = '精华'; const icon = new URL('favicon.ico', rootUrl).href; - ctx.set('data', { + return { item: items, title: `${author} - ${description}`, link: currentUrl, @@ -87,5 +109,5 @@ export default async (ctx) => { subtitle: description, author, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/indiansinkuwait/latest.ts b/lib/routes/indiansinkuwait/latest.ts index 55a2d8b80..d72061ba6 100644 --- a/lib/routes/indiansinkuwait/latest.ts +++ b/lib/routes/indiansinkuwait/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,7 +6,28 @@ import { parseDate } from '@/utils/parse-date'; const baseUrl = 'https://www.indiansinkuwait.com'; -export default async (ctx) => { +export const route: Route = { + path: '/latest', + categories: ['traditional-media'], + example: '/indiansinkuwait/latest', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['indiansinkuwait.com/latest-news', 'indiansinkuwait.com/'], + }, + name: 'News', + maintainers: ['TonyRL'], + handler, +}; + +async function handler() { const { data: response } = await got(`${baseUrl}/latest-news`); const $ = load(response); @@ -34,11 +56,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), description: $('head meta[name="description"]').attr('content'), image: 'https://www.indiansinkuwait.com/apple-touch-icon-152x152-precomposed.png', link: `${baseUrl}/latest-news`, item: items, - }); -}; + }; +} diff --git a/lib/routes/indienova/article.ts b/lib/routes/indienova/article.ts index cbc113812..283916422 100644 --- a/lib/routes/indienova/article.ts +++ b/lib/routes/indienova/article.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseList, parseItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/:type', + categories: ['reading'], + example: '/indienova/article', + parameters: { type: '类型: `article` 文章,`development` 开发' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Unknown', + maintainers: ['GensouSakuya', 'kt286'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); let link = 'https://indienova.com/indie-game-news/'; if (type === 'development') { @@ -16,10 +35,10 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => cache.tryGet(item.link, () => parseItem(item)))); - ctx.set('data', { + return { title: $('head title').text(), link, description: '独立游戏资讯 | indienova 独立游戏', item: items, - }); -}; + }; +} diff --git a/lib/routes/indienova/column.ts b/lib/routes/indienova/column.ts index a6fc7de38..4a63d7c05 100644 --- a/lib/routes/indienova/column.ts +++ b/lib/routes/indienova/column.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, parseList, parseItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/column/:columnId', + categories: ['reading'], + example: '/indienova/column/52', + parameters: { columnId: '专题 ID,可在 URL中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['indienova.com/column/:columnId'], + }, + name: '专题', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const columnId = ctx.req.param('columnId'); const { data: response, url: link } = await got(`${baseUrl}/column/${columnId}`); @@ -13,9 +35,9 @@ export default async (ctx) => { const items = await Promise.all(list.map((item) => cache.tryGet(item.link, () => parseItem(item)))); - ctx.set('data', { + return { title: $('head title').text(), link, item: items, - }); -}; + }; +} diff --git a/lib/routes/indienova/gamedb.ts b/lib/routes/indienova/gamedb.ts index 2d3745039..e1a155100 100644 --- a/lib/routes/indienova/gamedb.ts +++ b/lib/routes/indienova/gamedb.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'; import timezone from '@/utils/timezone'; -export default async (ctx) => { +export const route: Route = { + path: '/gamedb/recent', + name: 'Unknown', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { platform = 'all' } = ctx.req.param(); const baseUrl = 'https://indienova.com'; @@ -48,9 +56,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), link, item: items, - }); -}; + }; +} diff --git a/lib/routes/indienova/usergames.ts b/lib/routes/indienova/usergames.ts index b794ee51d..7e3412434 100644 --- a/lib/routes/indienova/usergames.ts +++ b/lib/routes/indienova/usergames.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: '/usergames', + categories: ['reading'], + example: '/indienova/usergames', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['indienova.com/usergames', 'indienova.com/'], + }, + name: '会员开发游戏库', + maintainers: ['TonyRL'], + handler, +}; + +async function handler() { const baseUrl = 'https://indienova.com'; const { data: response, url: link } = await got(`${baseUrl}/usergames`); @@ -35,9 +57,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), link, item: items, - }); -}; + }; +} diff --git a/lib/routes/inewsweek/index.ts b/lib/routes/inewsweek/index.ts index de3819560..be4170a09 100644 --- a/lib/routes/inewsweek/index.ts +++ b/lib/routes/inewsweek/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,28 @@ import iconv from 'iconv-lite'; const rootUrl = 'http://news.inewsweek.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/:channel', + categories: ['bbs'], + example: '/inewsweek/survey', + parameters: { channel: '栏目' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['inewsweek.cn/:channel', 'inewsweek.cn/'], + }, + name: '栏目', + maintainers: ['changren-wcr'], + handler, +}; + +async function handler(ctx) { const channel = ctx.req.param('channel'); const url = `${rootUrl}/${channel}`; const response = await got(url, { @@ -46,9 +68,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: $('title').text(), link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/infoq/recommend.ts b/lib/routes/infoq/recommend.ts index 5da671861..58ce2bc99 100644 --- a/lib/routes/infoq/recommend.ts +++ b/lib/routes/infoq/recommend.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/recommend', + categories: ['traditional-media'], + example: '/infoq/recommend', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['infoq.cn/'], + }, + name: '推荐', + maintainers: ['brilon'], + handler, +}; + +async function handler(ctx) { const apiUrl = 'https://www.infoq.cn/public/v1/my/recommond'; const pageUrl = 'https://www.infoq.cn'; @@ -18,9 +40,9 @@ export default async (ctx) => { const data = resp.data.data; const items = await utils.ProcessFeed(data, cache); - ctx.set('data', { + return { title: 'InfoQ 推荐', link: pageUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/infoq/topic.ts b/lib/routes/infoq/topic.ts index 4505c7b7a..0c7efefb8 100644 --- a/lib/routes/infoq/topic.ts +++ b/lib/routes/infoq/topic.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/topic/:id', + categories: ['traditional-media'], + example: '/infoq/topic/1', + parameters: { id: '话题id,可在 [InfoQ全部话题](https://www.infoq.cn/topics) 页面找到URL里的话题id' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['infoq.cn/topic/:id'], + }, + name: '话题', + maintainers: ['brilon'], + handler, +}; + +async function handler(ctx) { const paramId = ctx.req.param('id'); const apiUrl = 'https://www.infoq.cn/public/v1/article/getList'; const infoUrl = 'https://www.infoq.cn/public/v1/topic/getInfo'; @@ -33,11 +55,11 @@ export default async (ctx) => { const data = resp.data.data; const items = await utils.ProcessFeed(data, cache); - ctx.set('data', { + return { title: `InfoQ 话题 - ${topicName}`, description: info.data.data.desc, image: info.data.data.cover, link: pageUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/informs/index.ts b/lib/routes/informs/index.ts index 7128e3bc2..095489c53 100644 --- a/lib/routes/informs/index.ts +++ b/lib/routes/informs/index.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 { parseDate } from '@/utils/parse-date'; const rootUrl = 'https://pubsonline.informs.org'; -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + categories: ['finance'], + example: '/informs/mnsc', + parameters: { category: 'Category, can be found in the url of the page, `orsc` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Category', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'orsc'; const cateUrl = `${rootUrl}/toc/${category}/0/0`; @@ -70,9 +89,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `INFORMS - ${category}`, link: cateUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/inoreader/index.ts b/lib/routes/inoreader/index.ts index 3967b5136..3f5669149 100644 --- a/lib/routes/inoreader/index.ts +++ b/lib/routes/inoreader/index.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: '/html_clip/:user/:tag', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const type = 'html'; const user = ctx.req.param('user'); const tag = ctx.req.param('tag'); @@ -18,7 +26,7 @@ export default async (ctx) => { const $ = load(data); const entries = $('#snip_body>.article_content'); - ctx.set('data', { + return { title: $('.header_text').text().trim(), link: currentUrl, item: entries @@ -41,5 +49,5 @@ export default async (ctx) => { }) .get(), allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/inoreader/rss.ts b/lib/routes/inoreader/rss.ts index a945b34e9..366496117 100644 --- a/lib/routes/inoreader/rss.ts +++ b/lib/routes/inoreader/rss.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import parser from '@/utils/rss-parser'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/rss/:user/:tag', + categories: ['government'], + example: '/inoreader/rss/1005137674/user-favorites', + parameters: { user: 'user id, the interger after user/ in the example URL', tag: 'tag, the string after tag/ in the example URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'RSS', + maintainers: ['EthanWng97'], + handler, +}; + +async function handler(ctx) { const user = ctx.req.param('user'); const tag = ctx.req.param('tag'); const rootUrl = 'https://www.inoreader.com/stream'; @@ -36,12 +55,12 @@ export default async (ctx) => { category: item?.categories ?? [], }; }); - ctx.set('data', { + return { title: feed.title, itunes_author: 'Inoreader', image: 'https://www.inoreader.com/brand/img/ino_app_icon.png', link: feed.link, description: feed.description, item: feed.items, - }); -}; + }; +} diff --git a/lib/routes/instagram/private-api/index.ts b/lib/routes/instagram/private-api/index.ts index d789e6cee..e729ebc3c 100644 --- a/lib/routes/instagram/private-api/index.ts +++ b/lib/routes/instagram/private-api/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { ig, login } from './utils'; import logger from '@/utils/logger'; @@ -54,7 +55,25 @@ async function loadContent(category, nameOrId, tryGet) { }; } -export default async (ctx) => { +export const route: Route = { + path: '/:category/:key', + categories: ['new-media'], + example: '/instagram/user/stefaniejoosten', + parameters: { category: 'Feed category, see table above', key: 'Username / Hashtag name' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'User Profile / Hashtag - Private API', + maintainers: ['oppilate', 'DIYgod'], + handler, +}; + +async function handler(ctx) { // https://github.com/dilame/instagram-private-api#feeds // const availableCategories = ["accountFollowers", "accountFollowing", "news", // "discover", "pendingFriendships", "blockedUsers", "directInbox", "directPending", @@ -82,7 +101,7 @@ export default async (ctx) => { throw error; } - ctx.set('data', { + return { title: data.feedTitle, link: data.feedLink, description: data.feedDescription, @@ -91,5 +110,5 @@ export default async (ctx) => { logo: data.feedLogo, image: data.feedLogo, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/instagram/web-api/index.ts b/lib/routes/instagram/web-api/index.ts index 0c24f99ef..554473a73 100644 --- a/lib/routes/instagram/web-api/index.ts +++ b/lib/routes/instagram/web-api/index.ts @@ -1,10 +1,29 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { CookieJar } from 'tough-cookie'; import { config } from '@/config'; import { renderItems } from '../common-utils'; import { baseUrl, COOKIE_URL, checkLogin, getUserInfo, getUserFeedItems, getTagsFeedItems, getLoggedOutTagsFeedItems, renderGuestItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/2/:category/:key', + categories: ['new-media'], + example: '/instagram/2/user/stefaniejoosten', + parameters: { category: 'Feed category, see table below', key: 'Username / Hashtag name' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'User Profile / Hashtag', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { // if (!config.instagram || !config.instagram.cookie) { // throw Error('Instagram RSS is disabled due to the lack of relevant config'); // } @@ -76,7 +95,7 @@ export default async (ctx) => { await cache.set('instagram:cookieJar', cookieJar.toJSON(), 31_536_000); - ctx.set('data', { + return { title: feedTitle, link: feedLink, description: feedDescription, @@ -85,5 +104,5 @@ export default async (ctx) => { logo: feedLogo, image: feedLogo, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/instructables/projects.ts b/lib/routes/instructables/projects.ts index 36b811c72..a9178dacc 100644 --- a/lib/routes/instructables/projects.ts +++ b/lib/routes/instructables/projects.ts @@ -1,6 +1,29 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/projects/:category?', + categories: ['other'], + example: '/instructables/projects/circuits', + parameters: { category: 'Category, empty by default, can be found in URL or see the table below' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['instructables.com/projects'], + target: '/projects', + }, + name: 'Projects', + maintainers: ['wolfg1969'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'all'; const siteDomain = 'www.instructables.com'; @@ -39,7 +62,7 @@ export default async (ctx) => { const data = response.data; - ctx.set('data', { + return { title: 'Instructables Projects', // 项目的标题 link, // 指向项目的链接 description: 'Instructables Projects', // 描述项目 @@ -52,5 +75,5 @@ export default async (ctx) => { pubDate: new Date(item.document.publishDate).toUTCString(), category: item.document.primaryClassification, })), - }); -}; + }; +} diff --git a/lib/routes/iqilu/program.ts b/lib/routes/iqilu/program.ts index 0738edd41..cf8c16e9d 100644 --- a/lib/routes/iqilu/program.ts +++ b/lib/routes/iqilu/program.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: '/v/:category{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { category = 'sdws/sdxwlb' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; @@ -84,7 +92,7 @@ export default async (ctx) => { .map((a) => $(a).text()) .join('/'); - ctx.set('data', { + return { item: items, title: $('title').text(), link: currentUrl, @@ -98,5 +106,5 @@ export default async (ctx) => { itunes_author: author, itunes_category: 'News', allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/iqiyi/album.ts b/lib/routes/iqiyi/album.ts index 2b3ae1814..c610bb2e4 100644 --- a/lib/routes/iqiyi/album.ts +++ b/lib/routes/iqiyi/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 { 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: '/album/:id', + categories: ['picture'], + example: '/iqiyi/album/神武天尊-2020-1b4lufwxd7h', + parameters: { id: '剧集 id, 可在该主页 URL 中找到' }, + 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 { data: response } = await got(`https://www.iq.com/album/${id}`); @@ -54,12 +73,12 @@ export default async (ctx) => { pubDate: parseDate(item.initIssueTime), })); - ctx.set('data', { + return { title: baseInfo.name, description: baseInfo.description, image: album.videoAlbumInfo.albumFocus1024, link: `https://www.iq.com/album/${album.videoAlbumInfo.albumLocSuffix}`, item: items, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/iqiyi/video.ts b/lib/routes/iqiyi/video.ts index eaed7032a..f7e5bc1f1 100644 --- a/lib/routes/iqiyi/video.ts +++ b/lib/routes/iqiyi/video.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { load } from 'cheerio'; import { config } from '@/config'; @@ -7,7 +8,28 @@ import puppeteer from '@/utils/puppeteer'; // /iqiyi/user/video/:uid // http://localhost:1200/iqiyi/user/video/2289191062 -export default async (ctx) => { +export const route: Route = { + path: '/user/video/:uid', + categories: ['picture'], + example: '/iqiyi/user/video/2289191062', + parameters: { uid: '用户名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['iqiyi.com/u/:uid/*'], + }, + name: '用户视频', + maintainers: ['talengu', 'JimenezLi'], + handler, +}; + +async function handler(ctx) { const uid = ctx.req.param('uid'); const link = `https://www.iqiyi.com/u/${uid}/videos`; @@ -52,4 +74,4 @@ export default async (ctx) => { browser.close(); ctx.set('data', data); -}; +} diff --git a/lib/routes/iqnew/latest.ts b/lib/routes/iqnew/latest.ts index 17f217dc8..676608736 100644 --- a/lib/routes/iqnew/latest.ts +++ b/lib/routes/iqnew/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,7 +6,28 @@ import iconv from 'iconv-lite'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -export default async (ctx) => { +export const route: Route = { + path: '/latest', + categories: ['other'], + example: '/iqnew/latest', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['iqnew.com/post/new_100/', 'iqnew.com/'], + }, + name: '最近更新', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const rootUrl = 'https://www.iqnew.com'; const url = rootUrl + '/post/new_100/'; const response = await got({ @@ -50,9 +72,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '爱Q生活网 - 最近更新', link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/iresearch/report.ts b/lib/routes/iresearch/report.ts index 36648bdaf..381634aaa 100644 --- a/lib/routes/iresearch/report.ts +++ b/lib/routes/iresearch/report.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -31,7 +32,28 @@ function processReport(item) { return description; } -export default async (ctx) => { +export const route: Route = { + path: '/report', + categories: ['other'], + example: '/iresearch/report', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.iresearch.com.cn/report.shtml'], + }, + name: '产业研究报告', + maintainers: ['brilon', 'Fatpandac'], + handler, +}; + +async function handler(ctx) { const limit = isNaN(Number.parseInt(ctx.req.query('limit'))) ? 20 : Number.parseInt(ctx.req.query('limit')); const apiUrl = `https://www.iresearch.com.cn/api/products/GetReportList?fee=0&date=&lastId=&pageSize=${limit}`; const pageUrl = 'https://www.iresearch.com.cn/m/report.shtml'; @@ -55,10 +77,10 @@ export default async (ctx) => { })) ); - ctx.set('data', { + return { title: '艾瑞产业研究报告', link: pageUrl, description: '艾瑞产业研究报告', item: items, - }); -}; + }; +} diff --git a/lib/routes/iresearch/weekly.ts b/lib/routes/iresearch/weekly.ts index 98272266d..92f3f1773 100644 --- a/lib/routes/iresearch/weekly.ts +++ b/lib/routes/iresearch/weekly.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: '/weekly/:category?', + categories: ['other'], + example: '/iresearch/weekly', + 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 = 'https://www.iresearch.com.cn'; @@ -34,9 +53,9 @@ export default async (ctx) => { }), })); - ctx.set('data', { + return { title: '艾瑞咨询 - 周度市场观察', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/issuehunt/funded.ts b/lib/routes/issuehunt/funded.ts index 88b12f8b6..e66d6708a 100644 --- a/lib/routes/issuehunt/funded.ts +++ b/lib/routes/issuehunt/funded.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import MarkdownIt from 'markdown-it'; -export default async (ctx) => { +export const route: Route = { + path: '/funded/:username/:repo', + categories: ['design'], + example: '/issuehunt/funded/DIYgod/RSSHub', + parameters: { username: 'Github user/org', repo: 'Repository name' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Project Funded', + maintainers: ['running-grass'], + handler, +}; + +async function handler(ctx) { const { username, repo } = ctx.req.param(); const response = await got(`https://issuehunt.io/apis/pages/repos/show?repositoryOwnerName=${username}&repositoryName=${repo}`); @@ -13,7 +32,7 @@ export default async (ctx) => { const md = MarkdownIt({ html: true, }); - ctx.set('data', { + return { title: `Issue Hunt 的悬赏 -- ${username}/${repo}`, link: `https://issuehunt.io/r/${username}/${repo}`, description: ``, @@ -24,5 +43,5 @@ export default async (ctx) => { link: `https://issuehunt.io/r/${username}/${repo}/issues/${item.number}`, author: item.userName, })), - }); -}; + }; +} diff --git a/lib/routes/itch/devlog.ts b/lib/routes/itch/devlog.ts index d12b4ab5d..8681cdd96 100644 --- a/lib/routes/itch/devlog.ts +++ b/lib/routes/itch/devlog.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 { isValidHost } from '@/utils/valid-host'; -export default async (ctx) => { +export const route: Route = { + path: '/devlog/:user/:id', + categories: ['reading'], + example: '/itch/devlog/teamterrible/the-baby-in-yellow', + parameters: { user: 'User id, can be found in URL', id: 'Item id, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Developer Logs', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const user = ctx.req.param('user') ?? ''; const id = ctx.req.param('id') ?? ''; if (!isValidHost(user)) { @@ -62,9 +81,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/itch/index.ts b/lib/routes/itch/index.ts index 649abecca..ba40ceb41 100644 --- a/lib/routes/itch/index.ts +++ b/lib/routes/itch/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 { load } from 'cheerio'; 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://itch.io'; const currentUrl = `${rootUrl}${getSubPath(ctx)}`; @@ -53,9 +61,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/itch/posts.ts b/lib/routes/itch/posts.ts index a1eb78ac2..f8676d498 100644 --- a/lib/routes/itch/posts.ts +++ b/lib/routes/itch/posts.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: '/posts/:topic/:id', + categories: ['reading'], + example: '/itch/posts/9539/introduce-yourself', + parameters: { topic: 'Topic id, can be found in URL', id: 'Topic name, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['itch.io/t/:topic/:id'], + }, + name: 'Posts', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const topic = ctx.req.param('topic'); const id = ctx.req.param('id'); @@ -33,9 +55,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/ithome/index.ts b/lib/routes/ithome/index.ts index 9ee728e52..3e8d713b7 100644 --- a/lib/routes/ithome/index.ts +++ b/lib/routes/ithome/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -34,7 +35,25 @@ const config = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/:caty', + categories: ['traditional-media'], + example: '/ithome/it', + parameters: { caty: '类别' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '分类资讯', + maintainers: ['luyuhuang'], + handler, +}; + +async function handler(ctx) { const cfg = config[ctx.req.param('caty')]; if (!cfg) { throw new Error('Bad category. See https://docs.rsshub.app/routes/new-media#it-zhi-jia'); @@ -77,10 +96,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'IT 之家 - ' + cfg.title, link: current_url, image: 'https://img.ithome.com/m/images/logo.png', item: items, - }); -}; + }; +} diff --git a/lib/routes/ithome/ranking.ts b/lib/routes/ithome/ranking.ts index 25c9c2e9a..6e128f607 100644 --- a/lib/routes/ithome/ranking.ts +++ b/lib/routes/ithome/ranking.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: '/ranking/:type', + categories: ['traditional-media'], + example: '/ithome/ranking/24h', + parameters: { type: '类别' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '热榜', + maintainers: ['immmortal', 'luyuhuang'], + handler, +}; + +async function handler(ctx) { const option = ctx.req.param('type'); const response = await got({ @@ -62,9 +81,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `IT之家-${title}`, link: 'https://www.ithome.com', item: items, - }); -}; + }; +} diff --git a/lib/routes/ithome/tag.ts b/lib/routes/ithome/tag.ts index de6203069..416ad64b3 100644 --- a/lib/routes/ithome/tag.ts +++ b/lib/routes/ithome/tag.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 rootUrl = 'https://www.ithome.com/'; -export default async (ctx) => { +export const route: Route = { + path: '/tag/:name', + categories: ['traditional-media'], + example: '/ithome/tag/win11', + parameters: { name: '标签名称,可从网址链接中获取' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ithome.com/tag/:name'], + }, + name: '标签', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const name = ctx.req.param('name'); const url = `${rootUrl}tag/${name}`; @@ -52,9 +74,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `IT之家 - ${name}标签`, link: url, item: items.filter(Boolean), - }); -}; + }; +} diff --git a/lib/routes/ithome/tw/feeds.ts b/lib/routes/ithome/tw/feeds.ts index fae8f19fe..5e7e0ada4 100644 --- a/lib/routes/ithome/tw/feeds.ts +++ b/lib/routes/ithome/tw/feeds.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: '/tw/feeds/:category', + categories: ['traditional-media'], + example: '/ithome/tw/feeds/news', + parameters: { category: '類別' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.ithome.com.tw/:category', 'www.ithome.com.tw/:category/feeds'], + }, + name: 'Feeds', + maintainers: ['miles170'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://www.ithome.com.tw'; const currentUrl = `${baseUrl}/${ctx.req.param('category')}/feeds`; const response = await got(currentUrl); @@ -30,10 +52,10 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `${name} | iThome`, link: currentUrl, description: 'iThome Online 是臺灣第一個網路原生報,提供IT產業即時新聞、企業IT產品報導與測試、技術專題、IT應用報導、IT書訊,以及面向豐富的名家專欄。', item: items, - }); -}; + }; +} diff --git a/lib/routes/ithome/zt.ts b/lib/routes/ithome/zt.ts index e74caaace..7556f4bca 100644 --- a/lib/routes/ithome/zt.ts +++ b/lib/routes/ithome/zt.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: '/zt/:id', + categories: ['traditional-media'], + example: '/ithome/zt/xijiayi', + parameters: { id: '专题 id' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ithome.com/zt/:id'], + }, + name: '专题', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const rootUrl = 'https://www.ithome.com'; @@ -55,9 +77,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('title').text()} - IT之家`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/iwara/index.ts b/lib/routes/iwara/index.ts index 9495645f7..22a0a3531 100644 --- a/lib/routes/iwara/index.ts +++ b/lib/routes/iwara/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'; @@ -35,7 +36,14 @@ const parseThumbnail = (type, item) => { return ''; }; -export default async (ctx) => { +export const route: Route = { + path: '/users/:username?/:type?', + name: 'Unknown', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const username = ctx.req.param('username'); const id = await cache.tryGet(`${apiRootUrl}/profile/${username}`, async () => (await got(`${apiRootUrl}/profile/${username}`)).data.user.id); const type = ctx.req.param('type') ?? 'video'; @@ -48,9 +56,9 @@ export default async (ctx) => { pubDate: parseDate(item.createdAt), })); - ctx.set('data', { + return { title: `${username}'s iwara - ${typeMap[type]}`, link: `${rootUrl}/users/${username}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/iwara/subscriptions.ts b/lib/routes/iwara/subscriptions.ts index dca284618..d73b767cb 100644 --- a/lib/routes/iwara/subscriptions.ts +++ b/lib/routes/iwara/subscriptions.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: '/subscriptions', + categories: ['program-update'], + example: '/iwara/subscriptions', + parameters: {}, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ecchi.iwara.tv/'], + }, + name: 'User Subscriptions', + maintainers: ['FeCCC'], + handler, +}; + +async function handler() { if (!config.iwara || !config.iwara.username || !config.iwara.password) { throw new Error('Iwara subscription RSS is disabled due to the lack of relevant config'); } @@ -140,9 +162,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `Iwara Subscription`, link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/ixigua/user-video.ts b/lib/routes/ixigua/user-video.ts index 60843969d..79dae2f7c 100644 --- a/lib/routes/ixigua/user-video.ts +++ b/lib/routes/ixigua/user-video.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 { parseDate } from '@/utils/parse-date'; const host = 'https://www.ixigua.com'; -export default async (ctx) => { +export const route: Route = { + path: '/user/video/:uid/:disableEmbed?', + categories: ['picture'], + example: '/ixigua/user/video/4234740937', + parameters: { uid: '用户 id, 可在用户主页中找到', disableEmbed: '默认为开启内嵌视频, 任意值为关闭' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ixigua.com/home/:uid'], + target: '/user/video/:uid', + }, + name: '用户视频投稿', + maintainers: [], + handler, +}; + +async function handler(ctx) { const uid = ctx.req.param('uid'); const disableEmbed = ctx.req.param('disableEmbed'); const url = `${host}/home/${uid}/?wid_try=1`; @@ -22,7 +45,7 @@ export default async (ctx) => { const videoInfos = data.AuthorVideoList.videoList; const userInfo = data.AuthorDetailInfo; - ctx.set('data', { + return { title: `${userInfo.name} 的西瓜视频`, link: url, description: userInfo.introduce, @@ -36,5 +59,5 @@ export default async (ctx) => { pubDate: parseDate(i.publishTime * 1000), author: userInfo.name, })), - }); -}; + }; +} diff --git a/lib/routes/jandan/index.ts b/lib/routes/jandan/index.ts index 4cc0fd443..b3da07a82 100644 --- a/lib/routes/jandan/index.ts +++ b/lib/routes/jandan/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 parser from '@/utils/rss-parser'; -export default async (ctx) => { +export const route: Route = { + path: '/', + name: 'Unknown', + maintainers: ['nczitzk', 'bigfei'], + handler, +}; + +async function handler() { const rootUrl = 'http://i.jandan.net'; const feed = await parser.parseURL(`${rootUrl}/feed/`); const items = await Promise.all( @@ -24,9 +32,9 @@ export default async (ctx) => { }) ) ); - ctx.set('data', { + return { title: '煎蛋', link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/jandan/section.ts b/lib/routes/jandan/section.ts index 7e8803ba6..3fa2ef623 100644 --- a/lib/routes/jandan/section.ts +++ b/lib/routes/jandan/section.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: '/:category', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'top'; const rootUrl = 'http://i.jandan.net'; @@ -40,9 +48,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `${$('title').text()} - 煎蛋`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/japanpost/track.ts b/lib/routes/japanpost/track.ts index c4707c92b..363c64945 100644 --- a/lib/routes/japanpost/track.ts +++ b/lib/routes/japanpost/track.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 utils from './utils'; let baseTitle = '日本郵便'; const baseUrl = 'https://trackings.post.japanpost.jp/services/srv/search/direct?'; -export default async (ctx) => { +export const route: Route = { + path: '/track/:reqCode/:locale?', + categories: ['other'], + example: '/japanpost/track/EJ123456789JP/en', + parameters: { reqCode: 'Package Number', locale: 'Language, default to japanese `ja`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Track & Trace Service 郵便追跡サービス', + maintainers: ['tuzi3040'], + handler, +}; + +async function handler(ctx) { const reqCode = ctx.req.param('reqCode'); const reqReqCode = 'reqCodeNo1=' + reqCode; @@ -64,7 +83,7 @@ export default async (ctx) => { let lastItemTimeStamp; let tz; - ctx.set('data', { + return { title: `${baseTitle} ${reqCode} ${packageType}`, link, description: `${baseTitle} ${reqCode} ${packageType}`, @@ -111,5 +130,5 @@ export default async (ctx) => { }; }) .get(), - }); -}; + }; +} diff --git a/lib/routes/javbus/index.ts b/lib/routes/javbus/index.ts index a44f7e228..653885276 100644 --- a/lib/routes/javbus/index.ts +++ b/lib/routes/javbus/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -17,7 +18,18 @@ const toSize = (raw) => { const allowDomain = new Set(['javbus.com', 'javbus.org', 'javsee.icu', 'javsee.one']); -export default async (ctx) => { +export const route: Route = { + path: '*', + radar: { + source: ['www.seejav.pw/'], + target: '', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const isWestern = /^\/western/.test(getSubPath(ctx)); const domain = ctx.req.query('domain') ?? 'javbus.com'; const westernDomain = ctx.req.query('western_domain') ?? 'javbus.org'; @@ -176,10 +188,10 @@ export default async (ctx) => { const title = $('head title').text(); - ctx.set('data', { + return { title: `${title.startsWith('JavBus') ? '' : 'JavBus - '}${title.replace(/ - AV磁力連結分享/, '')}`, link: currentUrl, item: items, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/javdb/actors.ts b/lib/routes/javdb/actors.ts index ab00a8d9f..7103006e3 100644 --- a/lib/routes/javdb/actors.ts +++ b/lib/routes/javdb/actors.ts @@ -1,6 +1,29 @@ +import { Route } from '@/types'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/actors/:id/:filter?', + categories: ['picture'], + example: '/javdb/actors/R2Vg', + parameters: { id: '编号,可在演员页 URL 中找到', filter: '过滤,见下表,默认为 `全部`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['javdb.com/'], + target: '', + }, + name: '演員', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const filter = ctx.req.param('filter') ?? ''; @@ -17,4 +40,4 @@ export default async (ctx) => { const title = `JavDB${filters[filter] === '' ? '' : ` - ${filters[filter]}`} `; ctx.set('data', await utils.ProcessItems(ctx, currentUrl, title)); -}; +} diff --git a/lib/routes/javdb/index.ts b/lib/routes/javdb/index.ts index 18f2964bd..046daeac1 100644 --- a/lib/routes/javdb/index.ts +++ b/lib/routes/javdb/index.ts @@ -1,6 +1,18 @@ +import { Route } from '@/types'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: ['/home/:category?/:sort?/:filter?', '/:category?/:sort?/:filter?'], + radar: { + source: ['javdb.com/'], + target: '', + }, + name: 'Unknown', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'censored'; const sort = ctx.req.param('sort') ?? '2'; const filter = ctx.req.param('filter') ?? '1'; @@ -29,4 +41,4 @@ export default async (ctx) => { const title = `${categories[category]} - JavDB - ${filters[filter] === '' ? '|' : `${filters[filter]} | `}${sorts[sort]}`; ctx.set('data', await utils.ProcessItems(ctx, currentUrl, title)); -}; +} diff --git a/lib/routes/javdb/lists.ts b/lib/routes/javdb/lists.ts index bfb91337f..296c33fae 100644 --- a/lib/routes/javdb/lists.ts +++ b/lib/routes/javdb/lists.ts @@ -1,6 +1,18 @@ +import { Route } from '@/types'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/lists/:id/:filter?/:sort?', + radar: { + source: ['javdb.com/'], + target: '', + }, + name: 'Unknown', + maintainers: ['dddepg'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const filter = ctx.req.param('filter') ?? ''; const sort = ctx.req.param('sort') ?? '0'; @@ -25,4 +37,4 @@ export default async (ctx) => { const title = `JavDB${filters[filter] === '' ? '' : ` - ${filters[filter]}`} ${sortOptions[sort]}`; ctx.set('data', await utils.ProcessItems(ctx, currentUrl, title)); -}; +} diff --git a/lib/routes/javdb/makers.ts b/lib/routes/javdb/makers.ts index 647bf142c..b67438a37 100644 --- a/lib/routes/javdb/makers.ts +++ b/lib/routes/javdb/makers.ts @@ -1,6 +1,29 @@ +import { Route } from '@/types'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/makers/:id/:filter?', + categories: ['picture'], + example: '/javdb/makers/7R', + parameters: { id: '编号,可在片商页 URL 中找到', filter: '过滤,见下表,默认为 `全部`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['javdb.com/'], + target: '', + }, + name: '片商', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const filter = ctx.req.param('filter') ?? ''; @@ -18,4 +41,4 @@ export default async (ctx) => { const title = `JavDB${filters[filter] === '' ? '' : ` - ${filters[filter]}`} `; ctx.set('data', await utils.ProcessItems(ctx, currentUrl, title)); -}; +} diff --git a/lib/routes/javdb/rankings.ts b/lib/routes/javdb/rankings.ts index 81b8a8b6e..d70ef5a3c 100644 --- a/lib/routes/javdb/rankings.ts +++ b/lib/routes/javdb/rankings.ts @@ -1,6 +1,29 @@ +import { Route } from '@/types'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/rankings/:category?/:time?', + categories: ['picture'], + example: '/javdb/rankings', + parameters: { category: '分类,见下表,默认为 `有碼`', time: '时间,见下表,默认为 `日榜`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['javdb.com/'], + target: '', + }, + name: '排行榜', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'censored'; const time = ctx.req.param('time') ?? 'daily'; @@ -9,4 +32,4 @@ export default async (ctx) => { const title = 'JavDB'; ctx.set('data', await utils.ProcessItems(ctx, currentUrl, title)); -}; +} diff --git a/lib/routes/javdb/search.ts b/lib/routes/javdb/search.ts index c78878653..002ffd322 100644 --- a/lib/routes/javdb/search.ts +++ b/lib/routes/javdb/search.ts @@ -1,6 +1,29 @@ +import { Route } from '@/types'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/search/:keyword?/:filter?/:sort?', + categories: ['picture'], + example: '/javdb/search/巨乳', + parameters: { keyword: '关键字,默认为空', filter: '过滤,见下表,默认为 `可播放`', sort: '排序,见下表,默认为 `按相关度排序`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['javdb.com/'], + target: '', + }, + name: '搜索', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const filter = ctx.req.param('filter') ?? ''; const keyword = ctx.req.param('keyword') ?? ''; const sort = ctx.req.param('sort') ?? '0'; @@ -30,4 +53,4 @@ export default async (ctx) => { const title = `關鍵字 ${keyword} ${filters[filter] === '' ? '' : `+ ${filters[filter]}`} ${sorts[sort]} 搜索結果 - JavDB`; ctx.set('data', await utils.ProcessItems(ctx, currentUrl, title)); -}; +} diff --git a/lib/routes/javdb/series.ts b/lib/routes/javdb/series.ts index 33f0ccc9b..3cf2867c6 100644 --- a/lib/routes/javdb/series.ts +++ b/lib/routes/javdb/series.ts @@ -1,6 +1,29 @@ +import { Route } from '@/types'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/series/:id/:filter?', + categories: ['picture'], + example: '/javdb/series/1NW', + parameters: { id: '编号,可在系列页 URL 中找到', filter: '过滤,见下表,默认为 `全部`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['javdb.com/'], + target: '', + }, + name: '系列', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const filter = ctx.req.param('filter') ?? ''; @@ -18,4 +41,4 @@ export default async (ctx) => { const title = `JavDB${filters[filter] === '' ? '' : ` - ${filter[filter]}`} `; ctx.set('data', await utils.ProcessItems(ctx, currentUrl, title)); -}; +} diff --git a/lib/routes/javdb/tags.ts b/lib/routes/javdb/tags.ts index 60260d011..b65d67f81 100644 --- a/lib/routes/javdb/tags.ts +++ b/lib/routes/javdb/tags.ts @@ -1,6 +1,29 @@ +import { Route } from '@/types'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/tags/:query?/:category?', + categories: ['picture'], + example: '/javdb/tags/c2=5&c10=1', + parameters: { query: '筛选,默认为 `c10=1`', category: '分类,见下表,默认为 `有碼`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['javdb.com/'], + target: '', + }, + name: '分類', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'censored'; const query = ctx.req.param('query') ?? ''; @@ -9,4 +32,4 @@ export default async (ctx) => { const title = `JavDB${query === '' ? '' : ` - ${query}`} `; ctx.set('data', await utils.ProcessItems(ctx, currentUrl, title)); -}; +} diff --git a/lib/routes/javlibrary/bestrated.ts b/lib/routes/javlibrary/bestrated.ts index aa044cb0e..1998367f6 100644 --- a/lib/routes/javlibrary/bestrated.ts +++ b/lib/routes/javlibrary/bestrated.ts @@ -1,10 +1,18 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { defaultMode, defaultLanguage, rootUrl, ProcessItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: ['/videos/bestrated/:language?/:mode?', '/bestrated/:language?/:mode?'], + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const mode = ctx.req.param('mode') ?? defaultMode; const language = ctx.req.param('language') ?? defaultLanguage; const currentUrl = `${rootUrl}/${language}/vl_bestrated.php?list&mode=${mode}`; ctx.set('data', await ProcessItems(language, currentUrl, cache.tryGet)); -}; +} diff --git a/lib/routes/javlibrary/bestreviews.ts b/lib/routes/javlibrary/bestreviews.ts index e5997460e..8b4be162c 100644 --- a/lib/routes/javlibrary/bestreviews.ts +++ b/lib/routes/javlibrary/bestreviews.ts @@ -1,10 +1,29 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { defaultMode, defaultLanguage, rootUrl, ProcessItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/bestreviews/:language?/:mode?', + categories: ['picture'], + example: '/javlibrary/bestreviews/en', + parameters: { language: 'Language, see below, Japanese by default, as `ja`', mode: 'Mode, see below, Last Month by default, as `1`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Best Reviews', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const mode = ctx.req.param('mode') ?? defaultMode; const language = ctx.req.param('language') ?? defaultLanguage; const currentUrl = `${rootUrl}/${language}/tl_bestreviews.php?list&mode=${mode}`; ctx.set('data', await ProcessItems(language, currentUrl, cache.tryGet)); -}; +} diff --git a/lib/routes/javlibrary/genre.ts b/lib/routes/javlibrary/genre.ts index 35b0891cc..5291230f9 100644 --- a/lib/routes/javlibrary/genre.ts +++ b/lib/routes/javlibrary/genre.ts @@ -1,11 +1,19 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { defaultMode, defaultGenre, defaultLanguage, rootUrl, ProcessItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: ['/videos/genre/:genre?/:language?/:mode?', '/genre/:genre?/:language?/:mode?'], + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const mode = ctx.req.param('mode') ?? defaultMode; const genre = ctx.req.param('genre') ?? defaultGenre; const language = ctx.req.param('language') ?? defaultLanguage; const currentUrl = `${rootUrl}/${language}/vl_genre.php?list&g=${genre}&mode=${mode}`; ctx.set('data', await ProcessItems(language, currentUrl, cache.tryGet)); -}; +} diff --git a/lib/routes/javlibrary/maker.ts b/lib/routes/javlibrary/maker.ts index 999eb05d8..35c7fc3a6 100644 --- a/lib/routes/javlibrary/maker.ts +++ b/lib/routes/javlibrary/maker.ts @@ -1,11 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { defaultMode, defaultLanguage, defaultMaker, rootUrl, ProcessItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/videos/maker/:maker?/:language?/:mode?', + categories: ['picture'], + example: '/javlibrary/videos/maker/arlq/cn', + parameters: { maker: 'Maker, S1 NO.1 STYLE by default, as `arlq`', language: 'Language, see below, Japanese by default, as `ja`', mode: 'Mode, see below, videos with comments (by date) by default, as `1`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Videos by makers', + maintainers: [], + handler, +}; + +async function handler(ctx) { const mode = ctx.req.param('mode') ?? defaultMode; const maker = ctx.req.param('maker') ?? defaultMaker; const language = ctx.req.param('language') ?? defaultLanguage; const currentUrl = `${rootUrl}/${language}/vl_maker.php?list&m=${maker}&mode=${mode}`; ctx.set('data', await ProcessItems(language, currentUrl, cache.tryGet)); -}; +} diff --git a/lib/routes/javlibrary/mostwanted.ts b/lib/routes/javlibrary/mostwanted.ts index 4060361ab..ad0a4e224 100644 --- a/lib/routes/javlibrary/mostwanted.ts +++ b/lib/routes/javlibrary/mostwanted.ts @@ -1,10 +1,18 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { defaultMode, defaultLanguage, rootUrl, ProcessItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: ['/videos/mostwanted/:language?/:mode?', '/mostwanted/:language?/:mode?'], + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const mode = ctx.req.param('mode') ?? defaultMode; const language = ctx.req.param('language') ?? defaultLanguage; const currentUrl = `${rootUrl}/${language}/vl_mostwanted.php?list&mode=${mode}`; ctx.set('data', await ProcessItems(language, currentUrl, cache.tryGet)); -}; +} diff --git a/lib/routes/javlibrary/newentries.ts b/lib/routes/javlibrary/newentries.ts index 38f570d8e..2cfbab97d 100644 --- a/lib/routes/javlibrary/newentries.ts +++ b/lib/routes/javlibrary/newentries.ts @@ -1,9 +1,17 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { defaultLanguage, rootUrl, ProcessItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: ['/videos/newentries/:language?', '/newentries/:language?'], + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const language = ctx.req.param('language') ?? defaultLanguage; const currentUrl = `${rootUrl}/${language}/vl_newentries.php?list`; ctx.set('data', await ProcessItems(language, currentUrl, cache.tryGet)); -}; +} diff --git a/lib/routes/javlibrary/newrelease.ts b/lib/routes/javlibrary/newrelease.ts index 573825fc0..12ec2ec36 100644 --- a/lib/routes/javlibrary/newrelease.ts +++ b/lib/routes/javlibrary/newrelease.ts @@ -1,10 +1,18 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { defaultMode, defaultLanguage, rootUrl, ProcessItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: ['/videos/newrelease/:language?/:mode?', '/newrelease/:language?/:mode?'], + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const mode = ctx.req.param('mode') ?? defaultMode; const language = ctx.req.param('language') ?? defaultLanguage; const currentUrl = `${rootUrl}/${language}/vl_newrelease.php?list&mode=${mode}`; ctx.set('data', await ProcessItems(language, currentUrl, cache.tryGet)); -}; +} diff --git a/lib/routes/javlibrary/star.ts b/lib/routes/javlibrary/star.ts index 009185f6a..514781a19 100644 --- a/lib/routes/javlibrary/star.ts +++ b/lib/routes/javlibrary/star.ts @@ -1,11 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { defaultMode, defaultLanguage, rootUrl, ProcessItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/star/:id/:language?/:mode?', + categories: ['picture'], + example: '/javlibrary/star/abbds/en', + parameters: { id: 'Star id, can be found in URL', language: 'Language, see below, Japanese by default, as `ja`', mode: 'Mode, see below, videos with comments (by date) by default, as `1`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Videos by star', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const mode = ctx.req.param('mode') ?? defaultMode; const language = ctx.req.param('language') ?? defaultLanguage; const currentUrl = `${rootUrl}/${language}/vl_star.php?list&s=${id}&mode=${mode}`; ctx.set('data', await ProcessItems(language, currentUrl, cache.tryGet)); -}; +} diff --git a/lib/routes/javlibrary/update.ts b/lib/routes/javlibrary/update.ts index cd4877052..f16004430 100644 --- a/lib/routes/javlibrary/update.ts +++ b/lib/routes/javlibrary/update.ts @@ -1,9 +1,17 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { defaultLanguage, rootUrl, ProcessItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: ['/videos/update/:language?', '/update/:language?'], + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const language = ctx.req.param('language') ?? defaultLanguage; const currentUrl = `${rootUrl}/${language}/vl_update.php?list`; ctx.set('data', await ProcessItems(language, currentUrl, cache.tryGet)); -}; +} diff --git a/lib/routes/javlibrary/user.ts b/lib/routes/javlibrary/user.ts index 3d1890889..79ce6ebc0 100644 --- a/lib/routes/javlibrary/user.ts +++ b/lib/routes/javlibrary/user.ts @@ -1,11 +1,19 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { defaultLanguage, rootUrl, ProcessItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: ['/users/:id/:type/:language?', '/:type/:id/:language?'], + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const type = ctx.req.param('type'); const language = ctx.req.param('language') ?? defaultLanguage; const currentUrl = `${rootUrl}/${language}/${type}.php?list&u=${id}`; ctx.set('data', await ProcessItems(language, currentUrl, cache.tryGet)); -}; +} diff --git a/lib/routes/jd/price.ts b/lib/routes/jd/price.ts index 65cb15002..11457492c 100644 --- a/lib/routes/jd/price.ts +++ b/lib/routes/jd/price.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: '/price/:id', + categories: ['game'], + example: '/jd/price/526835', + 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 rootUrl = 'https://item.jd.com'; @@ -26,7 +45,7 @@ export default async (ctx) => { const title = response.data.match(/name: '(.*?)'/)[1]; - ctx.set('data', { + return { title: `京东商品价格 - ${title}`, link: currentUrl, item: [ @@ -41,5 +60,5 @@ export default async (ctx) => { }), }, ], - }); -}; + }; +} diff --git a/lib/routes/jewishmuseum/exhibitions.ts b/lib/routes/jewishmuseum/exhibitions.ts index 57c059f1e..dd1846fab 100644 --- a/lib/routes/jewishmuseum/exhibitions.ts +++ b/lib/routes/jewishmuseum/exhibitions.ts @@ -1,6 +1,25 @@ +import { Route } from '@/types'; import buildData from '@/utils/common-config'; -export default async (ctx) => { +export const route: Route = { + path: '/exhibitions', + categories: ['shopping'], + example: '/jewishmuseum/exhibitions', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Exhibitions', + maintainers: ['chazeon'], + handler, +}; + +async function handler(ctx) { const link = 'https://thejewishmuseum.org/exhibitions'; ctx.set( @@ -16,4 +35,4 @@ export default async (ctx) => { }, }) ); -}; +} diff --git a/lib/routes/jianshu/collection.ts b/lib/routes/jianshu/collection.ts index 836bba07e..2e1c07d52 100644 --- a/lib/routes/jianshu/collection.ts +++ b/lib/routes/jianshu/collection.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import util from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/collection/:id', + categories: ['new-media'], + example: '/jianshu/collection/xYuZYD', + parameters: { id: '专题 id, 可在专题页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.jianshu.com/c/:id'], + }, + name: '专题', + maintainers: ['DIYgod', 'HenryQW', 'JimenezLi'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const response = await got({ @@ -21,10 +43,10 @@ export default async (ctx) => { const result = await util.ProcessFeed(list, cache); - ctx.set('data', { + return { title: $('title').text(), link: `https://www.jianshu.com/c/${id}`, description: $('meta[name="description"]').attr('content') || $('title').text(), item: result, - }); -}; + }; +} diff --git a/lib/routes/jianshu/home.ts b/lib/routes/jianshu/home.ts index 3c66d6899..db8f99018 100644 --- a/lib/routes/jianshu/home.ts +++ b/lib/routes/jianshu/home.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import util from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/home', + categories: ['new-media'], + example: '/jianshu/home', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.jianshu.com/'], + }, + name: '首页', + maintainers: ['DIYgod', 'HenryQW', 'JimenezLi'], + handler, +}; + +async function handler() { const response = await got({ method: 'get', url: 'https://www.jianshu.com', @@ -19,10 +41,10 @@ export default async (ctx) => { const result = await util.ProcessFeed(list, cache); - ctx.set('data', { + return { title: '简书首页', link: 'https://www.jianshu.com', description: $('meta[name="description"]').attr('content'), item: result, - }); -}; + }; +} diff --git a/lib/routes/jianshu/user.ts b/lib/routes/jianshu/user.ts index b514bc9a7..56be8c1ba 100644 --- a/lib/routes/jianshu/user.ts +++ b/lib/routes/jianshu/user.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import util from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/user/:id', + categories: ['new-media'], + example: '/jianshu/user/yZq3ZV', + parameters: { id: '作者 id, 可在作者主页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.jianshu.com/u/:id'], + }, + name: '作者', + maintainers: ['DIYgod', 'HenryQW', 'JimenezLi'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const response = await got({ @@ -21,10 +43,10 @@ export default async (ctx) => { const result = await util.ProcessFeed(list, cache); - ctx.set('data', { + return { title: $('title').text(), link: `https://www.jianshu.com/u/${id}`, description: $('meta[name="description"]').attr('content') || $('title').text(), item: result, - }); -}; + }; +} diff --git a/lib/routes/jiaoliudao/index.ts b/lib/routes/jiaoliudao/index.ts index 27b970254..662204afd 100644 --- a/lib/routes/jiaoliudao/index.ts +++ b/lib/routes/jiaoliudao/index.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: ['jiaoliudao.com/'], + target: '', + }, + name: 'Unknown', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://www.jiaoliudao.com'; const { data } = await got(`${baseUrl}/wp-json/wp/v2/posts`, { searchParams: { @@ -17,10 +29,10 @@ export default async (ctx) => { link: item.link, })); - ctx.set('data', { + return { title: '交流岛资源网-专注网络资源收集', image: `${baseUrl}/favicon.ico`, link: baseUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/jiemian/list.ts b/lib/routes/jiemian/list.ts index 2643565da..ea316e701 100644 --- a/lib/routes/jiemian/list.ts +++ b/lib/routes/jiemian/list.ts @@ -1,6 +1,14 @@ -export default (ctx) => { +import { Route } from '@/types'; +export const route: Route = { + path: '/list/:id', + name: 'Unknown', + maintainers: [], + handler, +}; + +function handler(ctx) { const id = ctx.req.param('id'); const redirectTo = `/jiemian${id ? `/lists/${id}` : ''}`; ctx.redirect(redirectTo); -}; +} diff --git a/lib/routes/jiemian/lists.ts b/lib/routes/jiemian/lists.ts index 1b792769d..0e7861fc7 100644 --- a/lib/routes/jiemian/lists.ts +++ b/lib/routes/jiemian/lists.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: '/:category{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { category = '' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 50; @@ -89,7 +97,7 @@ export default async (ctx) => { const image = $('div.logo img').prop('src'); const icon = new URL($('link[rel="icon"]').prop('href'), rootUrl).href; - ctx.set('data', { + return { item: items, title, link: currentUrl, @@ -100,5 +108,5 @@ export default async (ctx) => { logo: icon, subtitle: titleSplits[0], author: titleSplits.pop(), - }); -}; + }; +} diff --git a/lib/routes/jike/topic-text.ts b/lib/routes/jike/topic-text.ts index fbb20392e..6707636ce 100644 --- a/lib/routes/jike/topic-text.ts +++ b/lib/routes/jike/topic-text.ts @@ -1,7 +1,29 @@ +import { Route } from '@/types'; import dayjs from 'dayjs'; import { constructTopicEntry } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/topic/text/:id', + categories: ['new-media'], + example: '/jike/topic/text/553870e8e4b0cafb0a1bef68', + parameters: { id: '圈子 id, 可在即刻 web 端圈子页或 APP 分享出来的圈子页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['web.okjike.com/topic/:id'], + }, + name: '圈子 - 纯文字', + maintainers: ['HenryQW'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const topicUrl = `https://m.okjike.com/topics/${id}`; @@ -20,4 +42,4 @@ export default async (ctx) => { }); ctx.set('data', result); } -}; +} diff --git a/lib/routes/jike/topic.ts b/lib/routes/jike/topic.ts index ab3f6f55c..7cd2312d7 100644 --- a/lib/routes/jike/topic.ts +++ b/lib/routes/jike/topic.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { topicDataHanding, constructTopicEntry } from './utils'; @@ -6,7 +7,29 @@ import dayjs from 'dayjs'; const urlRegex = /(https?:\/\/[^\s"'<>]+)/g; -export default async (ctx) => { +export const route: Route = { + path: '/topic/:id/:showUid?', + categories: ['new-media'], + example: '/jike/topic/556688fae4b00c57d9dd46ee', + parameters: { id: '圈子 id, 可在即刻 web 端圈子页或 APP 分享出来的圈子页 URL 中找到', showUid: '是否在内容中显示用户信息,设置为 1 则开启' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['web.okjike.com/topic/:id'], + target: '/topic/:id', + }, + name: '圈子', + maintainers: ['DIYgod', 'prnake'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const topicUrl = `https://m.okjike.com/topics/${id}`; @@ -44,4 +67,4 @@ export default async (ctx) => { } ctx.set('data', result); } -}; +} diff --git a/lib/routes/jike/user.ts b/lib/routes/jike/user.ts index 4021c58cd..2ab956816 100644 --- a/lib/routes/jike/user.ts +++ b/lib/routes/jike/user.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: '/user/:id', + categories: ['new-media'], + example: '/jike/user/3EE02BC9-C5B3-4209-8750-4ED1EE0F67BB', + parameters: { id: '用户 id, 可在即刻分享出来的单条动态页点击用户头像进入个人主页,然后在个人主页的 URL 中找到,或者在单条动态页使用 RSSHub Radar 插件' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['web.okjike.com/u/:uid'], + target: '/user/:uid', + }, + name: '用户动态', + maintainers: ['DIYgod', 'prnake'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const response = await got({ @@ -148,11 +171,11 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `${data.user.screenName}的即刻动态`, description: data.user.bio, link: `https://m.okjike.com/users/${id}`, image: data.user.avatarImage.picUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/jike/utils.ts b/lib/routes/jike/utils.ts index 756e1ba5d..91ba6685b 100644 --- a/lib/routes/jike/utils.ts +++ b/lib/routes/jike/utils.ts @@ -175,21 +175,21 @@ const constructTopicEntry = async (ctx, url) => { ); if (data.length === 0) { - ctx.set('data', { + return { title: '主题 ID 不存在,或该主题暂无内容', - }); + }; return null; } const topic = data.topic; - ctx.set('data', { + return { title: `${topic.content} - 即刻圈子`, link: url, description: topic.briefIntro, image: topic.squarePicture.picUrl || topic.squarePicture.middlePicUrl || topic.squarePicture.thumbnailUrl, // item: topicDataHanding(data), - }); + }; return data; }; diff --git a/lib/routes/jin10/index.ts b/lib/routes/jin10/index.ts index dd7ec8f94..4e0376355 100644 --- a/lib/routes/jin10/index.ts +++ b/lib/routes/jin10/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 { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/:important?', + categories: ['other'], + example: '/jin10', + parameters: { important: '只看重要,任意值开启,留空关闭' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['jin10.com/'], + target: '', + }, + name: '市场快讯', + maintainers: ['laampui'], + handler, +}; + +async function handler(ctx) { const { important = false } = ctx.req.param(); const data = await cache.tryGet( 'jin10:index', @@ -54,9 +77,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: '金十数据', link: 'https://www.jin10.com/', item: important ? item.filter((item) => item.important) : item, - }); -}; + }; +} diff --git a/lib/routes/jin10/topic.ts b/lib/routes/jin10/topic.ts index 423de4409..1c3451084 100644 --- a/lib/routes/jin10/topic.ts +++ b/lib/routes/jin10/topic.ts @@ -1,10 +1,32 @@ +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 { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/topic/:id', + categories: ['other'], + example: '/jin10/topic/396', + parameters: { id: 'N' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['xnews.jin10.com/topic/:id'], + }, + name: '主题文章', + maintainers: ['miles170'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const data = await cache.tryGet( `jin10:topic:${id}`, @@ -42,10 +64,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: data.title, link: `https://xnews.jin10.com/topic/${id}`, description: data.introduction, item: items, - }); -}; + }; +} diff --git a/lib/routes/jinse/catalogue.ts b/lib/routes/jinse/catalogue.ts index e0c95dcf2..3dd2132d6 100644 --- a/lib/routes/jinse/catalogue.ts +++ b/lib/routes/jinse/catalogue.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -21,7 +22,25 @@ const categories = { capitalmarket: '研报', }; -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + categories: ['other'], + example: '/jinse/zhengce', + 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 = 'zhengce' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 50; @@ -89,7 +108,7 @@ export default async (ctx) => { const image = $('a.js-logoBox img').prop('src'); const icon = new URL($('link[rel="favicon"]').prop('href'), rootUrl).href; - ctx.set('data', { + return { item: items, title: `${author} - ${Object.hasOwn(categories, category) ? categories[category] : category}`, link: currentUrl, @@ -101,5 +120,5 @@ export default async (ctx) => { subtitle: $('meta[name="keywords"]').prop('content'), author, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/jinse/lives.ts b/lib/routes/jinse/lives.ts index 609098622..66246cfd4 100644 --- a/lib/routes/jinse/lives.ts +++ b/lib/routes/jinse/lives.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -16,7 +17,25 @@ const categories = { 5: '项目', }; -export default async (ctx) => { +export const route: Route = { + path: '/lives/:category?', + categories: ['other'], + example: '/jinse/lives', + 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 = '0' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 100; @@ -74,7 +93,7 @@ export default async (ctx) => { const image = $('a.js-logoBox img').prop('src'); const icon = new URL($('link[rel="favicon"]').prop('href'), rootUrl).href; - ctx.set('data', { + return { item: items, title: `${author} - ${Object.hasOwn(categories, category) ? categories[category] : category}`, link: currentUrl, @@ -86,5 +105,5 @@ export default async (ctx) => { subtitle: $('meta[name="keywords"]').prop('content'), author, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/jinse/timeline.ts b/lib/routes/jinse/timeline.ts index d9fa60759..0a6ee0283 100644 --- a/lib/routes/jinse/timeline.ts +++ b/lib/routes/jinse/timeline.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: '/timeline/:category?', + categories: ['other'], + example: '/jinse/timeline', + 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(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 50; @@ -84,7 +103,7 @@ export default async (ctx) => { const image = $('a.js-logoBox img').prop('src'); const icon = new URL($('link[rel="favicon"]').prop('href'), rootUrl).href; - ctx.set('data', { + return { item: items, title: `${author} - ${category}`, link: currentUrl, @@ -96,5 +115,5 @@ export default async (ctx) => { subtitle: $('meta[name="keywords"]').prop('content'), author, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/jisilu/index.ts b/lib/routes/jisilu/index.ts index 6504c45b1..b47a417c4 100644 --- a/lib/routes/jisilu/index.ts +++ b/lib/routes/jisilu/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?/:sort?/:day?', + categories: ['blog'], + example: '/jisilu', + parameters: { category: '分类,见下表,默认为全部,可在 URL 中找到', sort: '排序,见下表,默认为最新,可在 URL 中找到', day: '几天内,见下表,默认为30天,本参数仅在排序参数设定为 `热门` 后才可生效' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['jisilu.cn/home/explore', 'jisilu.cn/explore', 'jisilu.cn/'], + }, + name: '广场', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? ''; const sort = ctx.req.param('sort') ?? ''; const day = ctx.req.param('day') ?? ''; @@ -83,9 +105,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${name ? `${name}的${category === 'topic' ? '主题' : '回复'}` : '广场'} - 集思录`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/jjwxc/author.ts b/lib/routes/jjwxc/author.ts index 463a9e5e7..c670a6e12 100644 --- a/lib/routes/jjwxc/author.ts +++ b/lib/routes/jjwxc/author.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: '/author/:id?', + categories: ['government'], + example: '/jjwxc/author/4364484', + 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'); const rootUrl = 'https://www.jjwxc.net'; @@ -56,7 +75,7 @@ export default async (ctx) => { const image = `https:${logoEl.prop('src')}`; const icon = new URL('favicon.ico', rootUrl).href; - ctx.set('data', { + return { item: items, title: `${logoEl.prop('alt').replace(/logo/, '')} | ${author} - 最近更新`, link: currentUrl, @@ -67,5 +86,5 @@ export default async (ctx) => { logo: icon, subtitle: $('meta[name="Description"]').prop('content'), author, - }); -}; + }; +} diff --git a/lib/routes/jjwxc/book.ts b/lib/routes/jjwxc/book.ts index 39d3bd028..8200450e2 100644 --- a/lib/routes/jjwxc/book.ts +++ b/lib/routes/jjwxc/book.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: '/book/:id?', + categories: ['government'], + example: '/jjwxc/book/7013024', + 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'); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 100; @@ -95,7 +114,7 @@ export default async (ctx) => { const image = `https:${logoEl.prop('src')}`; const icon = new URL('favicon.ico', rootUrl).href; - ctx.set('data', { + return { item: items, title: `${logoEl.prop('alt').replace(/logo/, '')} | ${author}${keywords[0]}`, link: currentUrl, @@ -106,5 +125,5 @@ export default async (ctx) => { logo: icon, subtitle: $('meta[name="Description"]').prop('content'), author: $('meta[name="Author"]').prop('content'), - }); -}; + }; +} diff --git a/lib/routes/jornada/index.ts b/lib/routes/jornada/index.ts index 0d8d6697d..bb6e4b2f5 100644 --- a/lib/routes/jornada/index.ts +++ b/lib/routes/jornada/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; @@ -23,7 +24,25 @@ const getDateForToday = () => { return today; }; -export default async (ctx) => { +export const route: Route = { + path: '/:date?/:category?', + categories: ['bbs'], + example: '/jornada/2022-10-12/capital', + parameters: { date: "Date string, must be in format of `YYYY-MM-DD`. You can get today's news using `today`", category: 'Category, refer to the table below' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'News', + maintainers: ['Thealf154'], + handler, +}; + +async function handler(ctx) { const date = ctx.req.param('date') === 'today' || ctx.req.param('date') === undefined ? getDateForToday() : ctx.req.param('date'); const category = ctx.req.param('category'); const url = `${rootUrl}/jsonstorage/articles_${date}_.json`; @@ -51,9 +70,9 @@ export default async (ctx) => { })); } - ctx.set('data', { + return { title: 'La Jornada', link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/jou/home.ts b/lib/routes/jou/home.ts index 9cfadf8e3..c9b3f4d86 100644 --- a/lib/routes/jou/home.ts +++ b/lib/routes/jou/home.ts @@ -1,18 +1,40 @@ +import { Route } from '@/types'; import { getItems } from './utils'; const url = 'https://www.jou.edu.cn/index/tzgg.htm'; const host = 'https://www.jou.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/tzgg', + categories: ['forecast'], + example: '/jou/tzgg', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.jou.edu.cn/index/tzgg.htm', 'www.jou.edu.cn/'], + }, + name: '官网通知公告', + maintainers: ['real-jiakai'], + handler, +}; + +async function handler(ctx) { const out = await getItems(ctx, url, host, 'winstyle106390', 'timestyle106390', 'titlestyle106402', 'timestyle106402'); // 生成RSS源 - ctx.set('data', { + return { // 项目标题 title: '江苏海洋大学 -- 通知公告', // 项目链接 link: url, // items的内容 item: out, - }); -}; + }; +} diff --git a/lib/routes/jou/yz.ts b/lib/routes/jou/yz.ts index e8812b29f..6ca81d504 100644 --- a/lib/routes/jou/yz.ts +++ b/lib/routes/jou/yz.ts @@ -1,18 +1,40 @@ +import { Route } from '@/types'; import { getItems } from './utils'; const url = 'https://yz.jou.edu.cn/index/zxgg.htm'; const host = 'https://yz.jou.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/yztzgg', + categories: ['forecast'], + example: '/jou/yztzgg', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['yz.jou.edu.cn/index/zxgg.htm', 'yz.jou.edu.cn/'], + }, + name: '研招网通知公告', + maintainers: ['real-jiakai'], + handler, +}; + +async function handler(ctx) { const out = await getItems(ctx, url, host, 'winstyle207638', 'timestyle207638', 'titlestyle207543', 'timestyle207543'); // 生成RSS源 - ctx.set('data', { + return { // 项目标题 title: '江苏海洋大学 -- 研招通知公告', // 项目链接 link: url, // items的内容 item: out, - }); -}; + }; +} diff --git a/lib/routes/jseea/news.ts b/lib/routes/jseea/news.ts index c66f035b0..dc3adf178 100644 --- a/lib/routes/jseea/news.ts +++ b/lib/routes/jseea/news.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,18 @@ async function loadContent(link) { return description; } -export default async (ctx) => { +export const route: Route = { + path: '/news/:type?', + radar: { + source: ['jseea.cn/webfile/news/:type'], + target: '/news/:type', + }, + name: 'Unknown', + maintainers: ['schen1024'], + handler, +}; + +async function handler(ctx) { // 默认 正常规定 然后获取列表页面 const type = ctx.req.param('type') ?? 'zkyw'; const listPageUrl = `${baseUrl}/webfile/news/${type}/`; @@ -57,9 +69,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text() + siteTitle, link: listPageUrl, item: result, - }); -}; + }; +} diff --git a/lib/routes/jsu/cxzx.ts b/lib/routes/jsu/cxzx.ts index 69b559022..a2a0dd240 100644 --- a/lib/routes/jsu/cxzx.ts +++ b/lib/routes/jsu/cxzx.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; // 导入必要的模组 import got from '@/utils/got'; // 自订的 got @@ -5,7 +6,14 @@ import { load } from 'cheerio'; // 可以使用类似 jQuery 的 API HTML 解析 import { parseDate } from '@/utils/parse-date'; import { getPageItemAndDate } from './utils/index'; -export default async (ctx) => { +export const route: Route = { + path: '/cxzx/:types?', + name: 'Unknown', + maintainers: ['wenjia03'], + handler, +}; + +async function handler(ctx) { // 在此处编写您的逻辑 const { types = 'xkjs' } = ctx.req.param(); const baseUrl = 'https://cxzx.jsu.edu.cn/'; @@ -66,11 +74,11 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { // 在此处输出您的 RSS title: `吉首大学创新中心 - ${urls[types].title}`, link: urls[types].url, description: '吉首大学创新中心', item: out, - }); -}; + }; +} diff --git a/lib/routes/jsu/jwc.ts b/lib/routes/jsu/jwc.ts index 740be9b3e..5d1c4affa 100644 --- a/lib/routes/jsu/jwc.ts +++ b/lib/routes/jsu/jwc.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; // 导入必要的模组 import got from '@/utils/got'; // 自订的 got @@ -5,7 +6,25 @@ import { load } from 'cheerio'; // 可以使用类似 jQuery 的 API HTML 解析 import { parseDate } from '@/utils/parse-date'; import { getPageItemAndDate } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/jwc/:types?', + categories: ['forecast'], + example: '/jsu/jwc/jwdt', + parameters: { types: '通知分类 默认为`jwtz`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '教务处', + maintainers: ['wenjia03'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://jwc.jsu.edu.cn/'; const { types = 'jwtz' } = ctx.req.param(); const selectors = { @@ -50,11 +69,11 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { // 在此处输出您的 RSS title: `吉首大学教务处 - ${selectors[types].category}`, link: selectors[types].url, description: `吉首大学教务处 - ${selectors[types].category}`, item: out, - }); -}; + }; +} diff --git a/lib/routes/jsu/math.ts b/lib/routes/jsu/math.ts index d55f87d62..2fea2452b 100644 --- a/lib/routes/jsu/math.ts +++ b/lib/routes/jsu/math.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; // 导入必要的模组 import got from '@/utils/got'; // 自订的 got @@ -5,7 +6,25 @@ import { load } from 'cheerio'; // 可以使用类似 jQuery 的 API HTML 解析 import { parseDate } from '@/utils/parse-date'; import { getPageItemAndDate } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/stxy', + categories: ['forecast'], + example: '/jsu/stxy', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '数学与统计学院 - 通知公告', + maintainers: ['wenjia03'], + handler, +}; + +async function handler() { const baseUrl = 'https://stxy.jsu.edu.cn/'; const response = await got({ @@ -33,10 +52,10 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: '吉首大学数学与统计学院 - 通知公告', link: 'https://stxy.jsu.edu.cn/index/tzgg1.htm', description: '吉首大学数学与统计学院 - 通知公告', item: out, - }); -}; + }; +} diff --git a/lib/routes/jsu/rjxy.ts b/lib/routes/jsu/rjxy.ts index 836222601..d669a1252 100644 --- a/lib/routes/jsu/rjxy.ts +++ b/lib/routes/jsu/rjxy.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; // 导入必要的模组 import got from '@/utils/got'; // 自订的 got @@ -5,7 +6,25 @@ import { load } from 'cheerio'; // 可以使用类似 jQuery 的 API HTML 解析 import { parseDate } from '@/utils/parse-date'; import { getPageItemAndDate } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/rjxy', + categories: ['forecast'], + example: '/jsu/rjxy', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '计算机科学与工程学院 - 通知公告', + maintainers: ['wenjia03'], + handler, +}; + +async function handler() { const baseUrl = 'https://rjxy.jsu.edu.cn/'; const response = await got({ @@ -34,11 +53,11 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { // 在此处输出您的 RSS title: '吉首大学计算机科学与工程学院 - 通知公告', link: 'https://rjxy.jsu.edu.cn/index/tzgg1.htm', description: '吉首大学计算机科学与工程学院 - 通知公告', item: out, - }); -}; + }; +} diff --git a/lib/routes/jsu/universityindex.ts b/lib/routes/jsu/universityindex.ts index 168fe1841..ec25bae8e 100644 --- a/lib/routes/jsu/universityindex.ts +++ b/lib/routes/jsu/universityindex.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; // 导入必要的模组 import got from '@/utils/got'; // 自订的 got @@ -5,7 +6,25 @@ import { load } from 'cheerio'; // 可以使用类似 jQuery 的 API HTML 解析 import { parseDate } from '@/utils/parse-date'; import { getPageItemAndDate } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/notice', + categories: ['forecast'], + example: '/jsu/notice', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '通知公告', + maintainers: ['wenjia03'], + handler, +}; + +async function handler() { const baseUrl = 'https://www.jsu.edu.cn/'; const response = await got({ @@ -39,11 +58,11 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { // 在此处输出您的 RSS title: '吉首大学 - 通知公告', link: 'https://www.jsu.edu.cn/index/tzgg.htm', description: '吉首大学 - 通知公告', item: out, - }); -}; + }; +} diff --git a/lib/routes/juejin/books.ts b/lib/routes/juejin/books.ts index 681d54aeb..f17aebae7 100644 --- a/lib/routes/juejin/books.ts +++ b/lib/routes/juejin/books.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: '/books', + categories: ['design'], + example: '/juejin/books', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['juejin.cn/books'], + }, + name: '小册', + maintainers: ['xyqfer'], + handler, +}; + +async function handler() { const response = await got({ method: 'post', url: 'https://api.juejin.cn/booklet_api/v1/booklet/listbycategory', @@ -22,9 +44,9 @@ export default async (ctx) => { guid: base_info.booklet_id, })); - ctx.set('data', { + return { title: '掘金小册', link: 'https://juejin.cn/books', item: items, - }); -}; + }; +} diff --git a/lib/routes/juejin/category.ts b/lib/routes/juejin/category.ts index 3d868e553..beb446b32 100644 --- a/lib/routes/juejin/category.ts +++ b/lib/routes/juejin/category.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import util from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/category/:category', + categories: ['design'], + example: '/juejin/category/frontend', + parameters: { category: '分类名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '分类', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category'); const idResponse = await got({ @@ -31,10 +50,10 @@ export default async (ctx) => { } const resultItems = await util.ProcessFeed(originalData, cache); - ctx.set('data', { + return { title: `掘金 ${cat.category_name}`, link: `https://juejin.cn/${category}`, description: `掘金 ${cat.category_name}`, item: resultItems, - }); -}; + }; +} diff --git a/lib/routes/juejin/collection.ts b/lib/routes/juejin/collection.ts index 3634edb0c..18612c95f 100644 --- a/lib/routes/juejin/collection.ts +++ b/lib/routes/juejin/collection.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import util from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/collection/:collectionId', + categories: ['design'], + example: '/juejin/collection/6845243180586123271', + parameters: { collectionId: '收藏夹唯一标志符, 在浏览器地址栏URL中能够找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['juejin.cn/collection/:collectionId'], + }, + name: '单个收藏夹', + maintainers: ['isQ'], + handler, +}; + +async function handler(ctx) { const collectionId = ctx.req.param('collectionId'); const collectPage = await got({ @@ -17,11 +39,11 @@ export default async (ctx) => { const result = await util.ProcessFeed(items, cache); - ctx.set('data', { + return { title: '掘金 - 单个收藏夹', link: `https://juejin.cn/collection/${collectionId}`, description: '掘金,用户单个收藏夹', item: result, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/juejin/column.ts b/lib/routes/juejin/column.ts index ccf9a6c98..ef08990f7 100644 --- a/lib/routes/juejin/column.ts +++ b/lib/routes/juejin/column.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import util from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/column/:id', + categories: ['design'], + example: '/juejin/column/6960559453037199391', + parameters: { id: '专栏 id, 可在专栏页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['juejin.cn/column/:id'], + }, + name: '专栏', + maintainers: ['xiangzy1'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const detail = await got({ method: 'get', @@ -23,10 +45,10 @@ export default async (ctx) => { const columnName = detailData && detailData.column_version && detailData.column_version.title; const resultItems = await util.ProcessFeed(data, cache); - ctx.set('data', { + return { title: `掘金专栏-${columnName}`, link: `https://juejin.cn/column/${id}`, description: `掘金专栏-${columnName}`, item: resultItems, - }); -}; + }; +} diff --git a/lib/routes/juejin/favorites.ts b/lib/routes/juejin/favorites.ts index 977b83757..454d57aea 100644 --- a/lib/routes/juejin/favorites.ts +++ b/lib/routes/juejin/favorites.ts @@ -1,8 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import util from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/collections/:userId', + categories: ['design'], + example: '/juejin/collections/1697301682482439', + parameters: { userId: '用户唯一标志符, 在浏览器地址栏URL中能够找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['juejin.cn/user/:id', 'juejin.cn/user/:id/collections'], + target: '/collections/:id', + }, + name: '收藏集', + maintainers: ['isQ'], + handler, +}; + +async function handler(ctx) { const userId = ctx.req.param('userId'); const response = await got({ method: 'get', @@ -30,11 +53,11 @@ export default async (ctx) => { const result = await util.ProcessFeed(posts, cache); - ctx.set('data', { + return { title: '掘金 - 收藏集', link: `https://juejin.im/user/${userId}/collections`, description: '掘金,指定用户整个收藏集', item: result, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/juejin/pins.ts b/lib/routes/juejin/pins.ts index 24adcc55a..f93642c90 100644 --- a/lib/routes/juejin/pins.ts +++ b/lib/routes/juejin/pins.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: '/pins/:type?', + categories: ['design'], + example: '/juejin/pins/6824710202487472141', + parameters: { type: '默认为 recommend,见下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '沸点', + maintainers: ['xyqfer', 'laampui'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') ?? 'recommend'; const title = { recommend: '推荐', @@ -59,9 +78,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `沸点 - ${title[type]}`, link: 'https://juejin.cn/pins/recommended', item: items, - }); -}; + }; +} diff --git a/lib/routes/juejin/posts.ts b/lib/routes/juejin/posts.ts index 5a30e6861..7b65edf27 100644 --- a/lib/routes/juejin/posts.ts +++ b/lib/routes/juejin/posts.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import util from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/posts/:id', + categories: ['design'], + example: '/juejin/posts/3051900006845944', + parameters: { id: '用户 id, 可在用户页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['juejin.cn/user/:id', 'juejin.cn/user/:id/posts'], + }, + name: '用户文章', + maintainers: ['Maecenas'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const response = await got({ @@ -17,10 +39,10 @@ export default async (ctx) => { const username = data[0] && data[0].author_user_info && data[0].author_user_info.user_name; const resultItems = await util.ProcessFeed(data, cache); - ctx.set('data', { + return { title: `掘金专栏-${username}`, link: `https://juejin.cn/user/${id}/posts`, description: `掘金专栏-${username}`, item: resultItems, - }); -}; + }; +} diff --git a/lib/routes/juejin/tag.ts b/lib/routes/juejin/tag.ts index 26330f92a..cd8e50f73 100644 --- a/lib/routes/juejin/tag.ts +++ b/lib/routes/juejin/tag.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import util from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/tag/:tag', + categories: ['design'], + example: '/juejin/tag/JavaScript', + parameters: { tag: '标签名,可在标签 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['juejin.cn/tag/:tag'], + }, + name: '标签', + maintainers: ['isheng5'], + handler, +}; + +async function handler(ctx) { const tag = ctx.req.param('tag'); const idResponse = await got({ @@ -32,10 +54,10 @@ export default async (ctx) => { } const resultItems = await util.ProcessFeed(originalData, cache); - ctx.set('data', { + return { title: `掘金 ${tag}`, link: `https://juejin.cn/tag/${encodeURIComponent(tag)}`, description: `掘金 ${tag}`, item: resultItems, - }); -}; + }; +} diff --git a/lib/routes/juejin/trending.ts b/lib/routes/juejin/trending.ts index f7e08c8ef..cbc58f60c 100644 --- a/lib/routes/juejin/trending.ts +++ b/lib/routes/juejin/trending.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import util from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/trending/:category/:type', + categories: ['design'], + example: '/juejin/trending/ios/monthly', + parameters: { category: '分类名', type: '类型' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '热门', + maintainers: ['moaix'], + handler, +}; + +async function handler(ctx) { const { category, type } = ctx.req.param(); let id = ''; @@ -72,9 +91,9 @@ export default async (ctx) => { const resultItems = await util.ProcessFeed(entrylist, cache); - ctx.set('data', { + return { title, link, item: resultItems, - }); -}; + }; +} diff --git a/lib/routes/jump/discount.ts b/lib/routes/jump/discount.ts index 329afeb0b..aebd22a1d 100644 --- a/lib/routes/jump/discount.ts +++ b/lib/routes/jump/discount.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 getAllPageDiscountItem = async (countries, platform, termsId, totalNum) => // }; // }; -export default async (ctx) => { +export const route: Route = { + path: '/discount/:platform/:filter?/:countries?', + categories: ['reading'], + example: '/jump/discount/ps5/all', + parameters: { platform: '平台:switch,ps4,ps5,xbox,steam,epic', filter: '过滤参数,all-全部,jx-精选,sd-史低,dl-独立,vip-会员', countries: '地区,具体支持较多,可自信查看地区简写' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '游戏折扣', + maintainers: ['zytomorrow'], + handler, +}; + +async function handler(ctx) { const platform = ctx.req.param('platform'); const filter = ctx.req.param('filter') || 'all'; const countries = ctx.req.param('countries') || ''; @@ -109,7 +128,7 @@ export default async (ctx) => { // await seqGetGameDetail(allDiscountItem, ctx); // } - ctx.set('data', { + return { title: `jump 折扣-${platform}-${filterName[filter]}${countries ? `-${countries}` : ''}`, link: 'https://jumpvg.com/', description: 'jump 发现游戏', @@ -119,5 +138,5 @@ export default async (ctx) => { link: item.banner, guid: `${platform}-${item.oldGameId}-${item.cutOff}`, // 平台-打折id-打折率 })), - }); -}; + }; +} diff --git a/lib/routes/kamen-rider-official/news.ts b/lib/routes/kamen-rider-official/news.ts index a9b326a4c..6e37f6448 100644 --- a/lib/routes/kamen-rider-official/news.ts +++ b/lib/routes/kamen-rider-official/news.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: '/news/:category?', + categories: ['traditional-media'], + example: '/kamen-rider-official/news', + parameters: { category: 'Category, see below, すべて by default' }, + 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 limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 50; @@ -98,7 +117,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().split(/ー/)[0]}${category ? ` - ${category}` : ''}`, link: currentUrl, @@ -110,5 +129,5 @@ export default async (ctx) => { subtitle: $('meta[property="keywords"]').prop('content'), author: $('meta[property="og:site_name"]').prop('content'), allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/kantarworldpanel/index.ts b/lib/routes/kantarworldpanel/index.ts index 618c7ca13..cd6bf7412 100644 --- a/lib/routes/kantarworldpanel/index.ts +++ b/lib/routes/kantarworldpanel/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: '/:region?/:category{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { region = 'cn-en', category = 'news' } = 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 icon = new URL('favicon.ico', rootUrl).href; - ctx.set('data', { + return { item: items, title: $('title').text(), link: currentUrl, @@ -96,5 +104,5 @@ export default async (ctx) => { logo: icon, subtitle: $('meta[name="keywords"]').prop('content'), allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/kbs/news.ts b/lib/routes/kbs/news.ts index 18e7046bb..965a72d85 100644 --- a/lib/routes/kbs/news.ts +++ b/lib/routes/kbs/news.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: '/news/:category?/:language?', + categories: ['traditional-media'], + example: '/kbs/news', + parameters: { category: 'Category, can be found in Url as `id`, all by default', language: 'Language, see below, e as English by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['world.kbs.co.kr/'], + target: '/news', + }, + name: 'News', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'all'; const language = ctx.req.param('language') ?? 'e'; @@ -60,9 +83,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('.active').text() || list[0].category} - KBS WORLD`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/kbs/today.ts b/lib/routes/kbs/today.ts index 8cefbbf66..dfac881c9 100644 --- a/lib/routes/kbs/today.ts +++ b/lib/routes/kbs/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/:language?', + categories: ['traditional-media'], + example: '/kbs/today', + parameters: { language: 'Language, see below, e as English by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['world.kbs.co.kr/'], + target: '/today', + }, + name: 'Today', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const language = ctx.req.param('language') ?? 'e'; const rootUrl = 'http://world.kbs.co.kr'; @@ -49,9 +72,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `Latest News | KBS WORLD`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/kcna/news.ts b/lib/routes/kcna/news.ts index 37cf25403..3fb9779f9 100644 --- a/lib/routes/kcna/news.ts +++ b/lib/routes/kcna/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 { parseJucheDate, fixDesc, fetchPhoto, fetchVideo } from './utils'; import * as path from 'node:path'; import sanitizeHtml from 'sanitize-html'; -export default async (ctx) => { +export const route: Route = { + path: '/:lang/:category?', + categories: ['bbs'], + example: '/kcna/en', + parameters: { lang: 'Language, refer to the table below', category: 'Category, refer to the table below' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.kcna.kp/:lang', 'www.kcna.kp/:lang/category/articles/q/1ee9bdb7186944f765208f34ecfb5407.kcmsf', 'www.kcna.kp/:lang/category/articles.kcmsf'], + target: '/:lang', + }, + name: 'News', + maintainers: ['Rongronggg9'], + handler, +}; + +async function handler(ctx) { const { lang, category = '1ee9bdb7186944f765208f34ecfb5407' } = ctx.req.param(); const rootUrl = 'http://www.kcna.kp'; @@ -76,9 +99,9 @@ export default async (ctx) => { items.push(item); } - ctx.set('data', { + return { title, link: pageUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/ke/results.ts b/lib/routes/ke/results.ts index aa03eed96..91f9057bd 100644 --- a/lib/routes/ke/results.ts +++ b/lib/routes/ke/results.ts @@ -1,7 +1,29 @@ +import { Route } from '@/types'; import { parseDate } from '@/utils/parse-date'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/researchResults', + categories: ['other'], + example: '/ke/researchResults', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.research.ke.com/researchResults'], + }, + name: '研究成果', + maintainers: ['shaomingbo'], + handler, +}; + +async function handler() { const response = await got({ method: 'post', url: 'https://research.ke.com/apis/consumer-access/index/contents/page', @@ -21,7 +43,7 @@ export default async (ctx) => { const { list } = data.data; - ctx.set('data', { + return { title: '房地产行业研究报告', link: 'https://research.ke.com/ResearchResults', description: '研究成果', @@ -32,5 +54,5 @@ export default async (ctx) => { description: item.guideReading, pubDate: parseDate(item.publishTime), })), - }); -}; + }; +} diff --git a/lib/routes/keep/user.ts b/lib/routes/keep/user.ts index 99bea4d56..6bd43c168 100644 --- a/lib/routes/keep/user.ts +++ b/lib/routes/keep/user.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -5,7 +6,28 @@ 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: '/user/:id', + categories: ['new-media'], + example: '/keep/user/556b02c1ab59390afea671ea', + parameters: { id: 'Keep 用户 id' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gotokeep.com/users/:id'], + }, + name: '运动日记', + maintainers: ['Dectinc', 'DIYgod'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const response = await got({ @@ -24,7 +46,7 @@ export default async (ctx) => { response.data.data.entries[0] = response.data.data.entries[0].entries; const data = response.data.data.entries.reduce((all, current) => [...all, ...current.entries]); - ctx.set('data', { + return { title: `${data[0].author.username} 的 Keep 动态`, link: `https://show.gotokeep.com/users/${id}`, language: 'zh-cn', @@ -56,5 +78,5 @@ export default async (ctx) => { }), }; }), - }); -}; + }; +} diff --git a/lib/routes/keepass/news.ts b/lib/routes/keepass/news.ts index 04060dd3e..6bd468d54 100644 --- a/lib/routes/keepass/news.ts +++ b/lib/routes/keepass/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: '/', + name: 'Unknown', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://keepass.info/news/news_all.html'; const { data: response } = await got(baseUrl); const $ = load(response); @@ -39,9 +47,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').attr('content'), link: baseUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/kemono/index.ts b/lib/routes/kemono/index.ts index 8616ad081..cd403753d 100644 --- a/lib/routes/kemono/index.ts +++ b/lib/routes/kemono/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: '/:source?/:id?', + categories: ['program-update'], + example: '/kemono', + parameters: { source: 'Source, see below, Posts by default', id: 'User id, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['kemono.su/:source/user/:id', 'kemono.su/'], + }, + name: 'Posts', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 25; const source = ctx.req.param('source') ?? ''; const id = ctx.req.param('id'); @@ -124,10 +146,10 @@ export default async (ctx) => { ); } - ctx.set('data', { + return { title, image, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/kepu/live.ts b/lib/routes/kepu/live.ts index 4b3f0b1f2..5515f883a 100644 --- a/lib/routes/kepu/live.ts +++ b/lib/routes/kepu/live.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: '/live', + categories: ['traditional-media'], + example: '/kepu/live', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: true, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['live.kepu.net.cn/replay/index'], + }, + name: '直播回看', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 50; const rootUrl = 'https://live.kepu.net.cn'; @@ -76,7 +98,7 @@ export default async (ctx) => { const author = '中国科普博览'; const subtitle = '直播回看'; - ctx.set('data', { + return { item: items, title: `${author} - ${subtitle}`, link: currentUrl, @@ -89,5 +111,5 @@ export default async (ctx) => { itunes_author: author, itunes_category: 'Science', allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/keylol/index.ts b/lib/routes/keylol/index.ts index fb2c5df68..2fff6f24c 100644 --- a/lib/routes/keylol/index.ts +++ b/lib/routes/keylol/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) { let thePath = getSubPath(ctx).replace(/^\//, ''); if (/^f\d+-\d+/.test(thePath)) { @@ -70,7 +78,7 @@ export default async (ctx) => { const icon = $('link[rel="apple-touch-icon"]').prop('href'); - ctx.set('data', { + return { item: items, title: $('title').text(), link: currentUrl, @@ -80,5 +88,5 @@ export default async (ctx) => { logo: icon, subtitle: $('meta[name="application-name"]').prop('content'), author: $('meta[name="author"]').prop('content'), - }); -}; + }; +} diff --git a/lib/routes/kimlaw/thesis.ts b/lib/routes/kimlaw/thesis.ts index 41a444618..62c51279a 100644 --- a/lib/routes/kimlaw/thesis.ts +++ b/lib/routes/kimlaw/thesis.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 baseUrl = 'https://www.kimlaw.or.kr'; -export default async (ctx) => { +export const route: Route = { + path: '/thesis', + categories: ['journal'], + example: '/kimlaw/thesis', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['kimlaw.or.kr/67', 'kimlaw.or.kr/'], + }, + name: 'Thesis', + maintainers: ['TonyRL'], + handler, +}; + +async function handler() { const link = `${baseUrl}/67`; const { data: response } = await got(link); @@ -36,10 +58,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('.widget_menu_title').text()} - ${$('head title').text()}`, link, image: 'https://cdn.imweb.me/upload/S20210819f9dd86d20e7d7/9aec17c4e98a5.ico', item: items, - }); -}; + }; +} diff --git a/lib/routes/konghq/blog-posts.ts b/lib/routes/konghq/blog-posts.ts index 12847871e..01f12afc4 100644 --- a/lib/routes/konghq/blog-posts.ts +++ b/lib/routes/konghq/blog-posts.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; // Get the lastest blog posts of https://konghq.com/ import got from '@/utils/got'; @@ -7,7 +8,28 @@ import { parseDate } from '@/utils/parse-date'; const BASE_URL = 'https://konghq.com'; const BLOG_POSTS_URL = `${BASE_URL}/blog`; -export default async (ctx) => { +export const route: Route = { + path: '/blog-posts', + categories: ['design'], + example: '/konghq/blog-posts', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['konghq.com/blog/*'], + }, + name: '博客最新文章', + maintainers: ['piglei'], + handler, +}; + +async function handler() { // Always get the posts on the first page. const url = `${BLOG_POSTS_URL}/page/1`; @@ -45,9 +67,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `Kong Inc(konghq.com) blog posts`, link: BLOG_POSTS_URL, item: items, - }); -}; + }; +} diff --git a/lib/routes/kuaidi100/index.ts b/lib/routes/kuaidi100/index.ts index 255ddc113..392882f7d 100644 --- a/lib/routes/kuaidi100/index.ts +++ b/lib/routes/kuaidi100/index.ts @@ -1,6 +1,25 @@ +import { Route } from '@/types'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/track/:number/:id/:phone?', + categories: ['other'], + example: '/kuaidi100/track/shunfeng/SF1007896781640/0383', + parameters: { number: '快递公司代号', id: '订单号', phone: '手机号后四位(仅顺丰)' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '快递订单追踪', + maintainers: ['NeverBehave'], + handler, +}; + +async function handler(ctx) { // number is shorthand for company // id is ticket number // phone for shunfeng :) @@ -31,7 +50,7 @@ export default async (ctx) => { // Maybe we can look into isCheck, condition, and state :) // But I just want to make it work for now. - ctx.set('data', { + return { title: `快递 ${company.name}-${id}`, link: 'https://www.kuaidi100.com', description: `快递 ${company.name}-${id}`, @@ -42,5 +61,5 @@ export default async (ctx) => { pubDate: new Date(item.time || item.ftime).toUTCString(), link: 'https://www.kuaidi100.com', })), - }); -}; + }; +} diff --git a/lib/routes/kuaidi100/supported-company.ts b/lib/routes/kuaidi100/supported-company.ts index 149e763a9..0a7457638 100644 --- a/lib/routes/kuaidi100/supported-company.ts +++ b/lib/routes/kuaidi100/supported-company.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/company', + categories: ['other'], + example: '/kuaidi100/company', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['kuaidi100.com/'], + }, + name: '支持的快递公司列表', + maintainers: ['NeverBehave'], + handler, +}; + +async function handler() { const ls = await utils.company(); - ctx.set('data', { + return { title: `快递100 快递列表`, link: 'https://www.kuaidi100.com', description: `快递100 所支持的快递列表及其查询名称`, @@ -12,5 +34,5 @@ export default async (ctx) => { category: item.comTypeName, link: item.siteUrl, })), - }); -}; + }; +} diff --git a/lib/routes/kunchengblog/essay.ts b/lib/routes/kunchengblog/essay.ts index dffb1e2f7..ed6ce60b7 100644 --- a/lib/routes/kunchengblog/essay.ts +++ b/lib/routes/kunchengblog/essay.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 { SourceMapConsumer } from 'source-map'; -export default async (ctx) => { +export const route: Route = { + path: '/essay', + categories: ['programming'], + example: '/kunchengblog/essay', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['kunchengblog.com/essay'], + }, + name: 'Essay', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 100; const rootUrl = 'https://www.kunchengblog.com'; @@ -50,7 +72,7 @@ export default async (ctx) => { const description = $('meta[name="description"]').prop('content'); const icon = new URL($('link[rel="icon"]').prop('href'), rootUrl).href; - ctx.set('data', { + return { item: items, title: `${title} - Essay`, link: currentUrl, @@ -62,5 +84,5 @@ export default async (ctx) => { subtitle: description, author: title, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/kuwaitlocal/index.ts b/lib/routes/kuwaitlocal/index.ts index 52c662253..b9179e430 100644 --- a/lib/routes/kuwaitlocal/index.ts +++ b/lib/routes/kuwaitlocal/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: '/kuwaitlocal/article', + parameters: { category: 'Category name, can be found in URL, `latest` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['kuwaitlocal.com/news/categories/:category'], + target: '/:category', + }, + name: 'Categorised News', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://kuwaitlocal.com'; const { category = 'latest' } = ctx.req.param(); const url = `${baseUrl}/news/${category === 'latest' ? category : `categories/${category}`}`; @@ -39,11 +62,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text().trim(), description: $('head meta[name="description"]').attr('content').trim(), link: url, item: items, language: 'en', - }); -}; + }; +} diff --git a/lib/routes/kyodonews/index.ts b/lib/routes/kyodonews/index.ts index e47e8a6a5..5a8628ea0 100644 --- a/lib/routes/kyodonews/index.ts +++ b/lib/routes/kyodonews/index.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 * as path from 'node:path'; const resolveRelativeLink = (link, baseUrl) => (link.startsWith('http') ? link : `${baseUrl}${link}`); -export default async (ctx) => { +export const route: Route = { + path: '/:language?/:keyword?', + categories: ['bbs'], + example: '/kyodonews', + parameters: { language: '语言: `china` = 简体中文 (默认), `tchina` = 繁體中文', keyword: '关键词' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '最新报道', + maintainers: ['Rongronggg9'], + handler, +}; + +async function handler(ctx) { const language = ctx.req.param('language') ?? 'china'; const keyword = ctx.req.param('keyword') === 'RSS' ? 'rss' : ctx.req.param('keyword') ?? ''; @@ -117,11 +136,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title, description, link: currentUrl, item: items, image, - }); -}; + }; +} diff --git a/lib/routes/laimanhua/index.ts b/lib/routes/laimanhua/index.ts index 41f29e3d1..26e360fd2 100644 --- a/lib/routes/laimanhua/index.ts +++ b/lib/routes/laimanhua/index.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import iconv from 'iconv-lite'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:id', + categories: ['program-update'], + example: '/laimanhua/tiandikangzhanjiVERSUS', + parameters: { id: '漫画 ID,可在 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.laimanhua8.com/kanmanhua/:id'], + }, + name: '漫画列表', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const baseUrl = `https://www.laimanhua8.com`; const link = `${baseUrl}/kanmanhua/${id}/`; @@ -31,11 +53,11 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `${$('head meta[property="og:novel:book_name"]').attr('content')} - 来漫画`, description: $('.introduction').text(), image: $('head meta[property="og:image"]').attr('content'), link, item: items, - }); -}; + }; +} diff --git a/lib/routes/lala/rss.ts b/lib/routes/lala/rss.ts index 666788273..2f166139b 100644 --- a/lib/routes/lala/rss.ts +++ b/lib/routes/lala/rss.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: '/', + radar: { + source: ['lala.im/'], + target: '', + }, + name: 'Unknown', + maintainers: ['cnkmmk'], + handler, +}; + +async function handler() { const currentUrl = 'https://lala.im'; const response = await got(`${currentUrl}/feed`); const $ = load(response.data, { xmlMode: true }); @@ -23,10 +35,10 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title: titleMain, description: descriptionMain, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/lang/room.ts b/lib/routes/lang/room.ts index 6e85d0542..c8123abc1 100644 --- a/lib/routes/lang/room.ts +++ b/lib/routes/lang/room.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -5,7 +6,28 @@ 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: '/live/room/:id', + categories: ['multimedia'], + example: '/lang/live/room/1352360', + parameters: { id: '直播间 id, 可在主播直播间页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['lang.live/room/:id'], + }, + name: 'Unknown', + maintainers: ['MittWillson'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const url = `https://www.lang.live/room/${id}`; @@ -33,11 +55,11 @@ export default async (ctx) => { ]; } - ctx.set('data', { + return { title: `${name} 的浪 Play 直播`, description: data.live_info.sign, link: url, item, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/lanqiao/author.ts b/lib/routes/lanqiao/author.ts index 3a11e4485..92a855aaa 100644 --- a/lib/routes/lanqiao/author.ts +++ b/lib/routes/lanqiao/author.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import utils from './utils'; @@ -16,7 +17,28 @@ async function getUserName(uid) { return response.data.name; } -export default async (ctx) => { +export const route: Route = { + path: '/author/:uid', + categories: ['design'], + example: '/lanqiao/author/1701267', + parameters: { uid: '作者 `uid` 可在作者主页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['lanqiao.cn/users/:uid'], + }, + name: '作者发布的课程', + maintainers: ['huhuhang'], + handler, +}; + +async function handler(ctx) { const uid = ctx.req.param('uid'); const userName = await getUserName(uid); // 发起 HTTP GET 请求 @@ -49,7 +71,7 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { // 源标题 title: `${userName} 发布的课程`, // 源链接 @@ -58,5 +80,5 @@ export default async (ctx) => { description: `${userName} 发布的课程`, // 遍历此前获取的数据 item: items, - }); -}; + }; +} diff --git a/lib/routes/lanqiao/courses.ts b/lib/routes/lanqiao/courses.ts index 1b35007eb..dc265b654 100644 --- a/lib/routes/lanqiao/courses.ts +++ b/lib/routes/lanqiao/courses.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import utils from './utils'; import MarkdownIt from 'markdown-it'; -export default async (ctx) => { +export const route: Route = { + path: '/courses/:sort/:tag', + categories: ['design'], + example: '/lanqiao/courses/latest/all', + parameters: { sort: '排序规则 sort, 默认(`default`)、最新(`latest`)、最热(`hotest`)', tag: '课程标签 `tag`,可在该页面找到:https://www.lanqiao.cn/courses/' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '全站发布的课程', + maintainers: ['huhuhang'], + handler, +}; + +async function handler(ctx) { const sort = ctx.req.param('sort'); const tag = ctx.req.param('tag'); // 发起 HTTP GET 请求 @@ -50,7 +69,7 @@ export default async (ctx) => { default: '默认', }; - ctx.set('data', { + return { // 源标题 title: `蓝桥云课${sortType[sort]}课程列表【${tag}】`, // 源链接 @@ -59,5 +78,5 @@ export default async (ctx) => { description: `蓝桥云课【${tag}】标签下${sortType[sort]}课程列表`, // 遍历此前获取的数据 item: items, - }); -}; + }; +} diff --git a/lib/routes/lanqiao/questions.ts b/lib/routes/lanqiao/questions.ts index d3526355d..c4e79bef4 100644 --- a/lib/routes/lanqiao/questions.ts +++ b/lib/routes/lanqiao/questions.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 MarkdownIt from 'markdown-it'; -export default async (ctx) => { +export const route: Route = { + path: '/questions/:id', + categories: ['design'], + example: '/lanqiao/questions/2', + parameters: { id: 'topic_id 主题 `id` 可在社区板块 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['lanqiao.cn/questions/', 'lanqiao.cn/questions/topics/:id'], + }, + name: '技术社区', + maintainers: ['huhuhang'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); // 发起 HTTP GET 请求 const response = await got({ @@ -42,7 +64,7 @@ export default async (ctx) => { }) ) ); - ctx.set('data', { + return { // 源标题 title: `蓝桥云课技术社区【${topic_name}】`, // 源链接 @@ -51,5 +73,5 @@ export default async (ctx) => { description: `蓝桥云课技术社区【${topic_name}】`, // 遍历此前获取的数据 item: items, - }); -}; + }; +} diff --git a/lib/routes/laohu8/personal.ts b/lib/routes/laohu8/personal.ts index f0d6f1d4d..4ba1cb475 100644 --- a/lib/routes/laohu8/personal.ts +++ b/lib/routes/laohu8/personal.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://www.laohu8.com'; -export default async (ctx) => { +export const route: Route = { + path: '/personal/:id', + categories: ['other'], + example: '/laohu8/personal/3527667596890271', + parameters: { id: '用户 ID,见网址链接' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['laohu8.com/personal/:id'], + }, + name: '个人主页', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const url = `${rootUrl}/personal/${id}`; @@ -25,9 +47,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `老虎社区 - ${author} 个人社区`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/latepost/index.ts b/lib/routes/latepost/index.ts index 47652a6b5..367bf535b 100644 --- a/lib/routes/latepost/index.ts +++ b/lib/routes/latepost/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -21,7 +22,25 @@ const arrayToDictionary = (arr) => ]) ); -export default async (ctx) => { +export const route: Route = { + path: '/:proma?', + categories: ['traditional-media'], + example: '/latepost', + parameters: { proma: '栏目 id,见下表,默认为最新报道' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '报道', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const proma = ctx.req.param('proma'); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 5; @@ -96,7 +115,7 @@ export default async (ctx) => { const $ = load(currentResponse); - ctx.set('data', { + return { item: items, title: `${title} - ${proma ? columns[proma].title : defaultTitle}`, link: currentUrl, @@ -106,5 +125,5 @@ export default async (ctx) => { icon, logo: icon, author: title, - }); -}; + }; +} diff --git a/lib/routes/layoffs/index.ts b/lib/routes/layoffs/index.ts index d23fc937b..a94e0043c 100644 --- a/lib/routes/layoffs/index.ts +++ b/lib/routes/layoffs/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -37,7 +38,18 @@ const getMappings = function (obj) { return [mapping, reverseMapping]; }; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['layoffs.fyi/'], + target: '', + }, + name: 'Unknown', + maintainers: ['BrandNewLifeJackie26'], + handler, +}; + +async function handler() { const headers = { 'x-airtable-application-id': 'app1PaujS9zxVGUZ4', 'x-airtable-inter-service-client': 'webClient', @@ -108,7 +120,7 @@ export default async (ctx) => { const countryMapping = getMappings(table.columns.find((col) => col.name === 'Country').typeOptions.choices)[0]; const rows = table.rows.slice(0, ROW_COUNT); - ctx.set('data', { + return { title: 'Tech layoff data feed from layoffs.fyi', link: WEBSITE_URL, description: 'This feed gets tech layoff data from layoffs.fyi and display them in a user friendly way', @@ -131,5 +143,5 @@ export default async (ctx) => { link: source, // Laid off source link }; }), - }); -}; + }; +} diff --git a/lib/routes/learnblockchain/posts.ts b/lib/routes/learnblockchain/posts.ts index f5d966840..9d9a784f6 100644 --- a/lib/routes/learnblockchain/posts.ts +++ b/lib/routes/learnblockchain/posts.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import { load } from 'cheerio'; import got from '@/utils/got'; import { parseRelativeDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/posts/:cid/:sort?', + categories: ['design'], + example: '/learnblockchain/posts/DApp/newest', + parameters: { cid: '分类id,更多分类可以论坛的URL找到', sort: '排序方式,默认精选' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '文章', + maintainers: ['running-grass'], + handler, +}; + +async function handler(ctx) { const cid = ctx.req.param('cid') || 'all'; const sort = ctx.req.param('sort'); @@ -19,7 +38,7 @@ export default async (ctx) => { const $ = load(data); const list = $('div.stream-list section.stream-list-item'); - ctx.set('data', { + return { title: `登链社区--${cid}`, link: url, description: `登链社区`, @@ -39,5 +58,5 @@ export default async (ctx) => { return json; }) .get(), - }); -}; + }; +} diff --git a/lib/routes/learnku/topic.ts b/lib/routes/learnku/topic.ts index ca413cf7e..adcf33e58 100644 --- a/lib/routes/learnku/topic.ts +++ b/lib/routes/learnku/topic.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 { art } from '@/utils/render'; import * as path from 'node:path'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:community/:category?', + categories: ['blog'], + example: '/learnku/laravel/qa', + parameters: { community: '社区 标识,可在 找到', category: '分类,如果不传 `category` 则获取全部分类' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['learnku.com/:community'], + target: '/:community', + }, + name: '社区', + maintainers: ['kayw-geek'], + handler, +}; + +async function handler(ctx) { const community = ctx.req.param('community'); const category = ctx.req.param('category') || ''; @@ -67,10 +90,10 @@ export default async (ctx) => { ['links', { name: '链接' }], ['', { name: '最新' }], ]); - ctx.set('data', { + return { title: `LearnKu - ${title} - ${categoryTitle.get(category).name}`, link: url, description, item: item.filter((item) => item !== ''), - }); -}; + }; +} diff --git a/lib/routes/leetcode/articles.ts b/lib/routes/leetcode/articles.ts index 5666dcee0..ab38470f7 100644 --- a/lib/routes/leetcode/articles.ts +++ b/lib/routes/leetcode/articles.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,28 @@ const md = MarkdownIt({ const host = 'https://leetcode.com'; const gqlEndpoint = `${host}/graphql`; -export default async (ctx) => { +export const route: Route = { + path: '/articles', + categories: ['design'], + example: '/leetcode/articles', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['leetcode.com/articles'], + }, + name: 'Articles', + maintainers: ['LogicJake'], + handler, +}; + +async function handler() { const link = new URL('/articles/', host).href; const response = await got(link); const $ = load(response.data); @@ -76,11 +98,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), description: $('meta[property="og:description"]').attr('content'), image: 'https://assets.leetcode.com/static_assets/public/icons/favicon-192x192.png', link, item: out, - }); -}; + }; +} diff --git a/lib/routes/leetcode/dailyquestion-cn.ts b/lib/routes/leetcode/dailyquestion-cn.ts index 319414230..a3c0daf51 100644 --- a/lib/routes/leetcode/dailyquestion-cn.ts +++ b/lib/routes/leetcode/dailyquestion-cn.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -7,7 +8,17 @@ import * as path from 'node:path'; const host = 'https://leetcode.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/dailyquestion/cn', + radar: { + source: ['leetcode.cn/'], + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler() { const question = { date: '', link: '', @@ -103,7 +114,7 @@ export default async (ctx) => { link: question.link, }; - ctx.set('data', { + return { title: 'LeetCode 每日一题', link: 'https://leetcode.cn', description: 'Leetcode 每日一题', @@ -114,5 +125,5 @@ export default async (ctx) => { link: rssData.link, }, ], - }); -}; + }; +} diff --git a/lib/routes/leetcode/dailyquestion-en.ts b/lib/routes/leetcode/dailyquestion-en.ts index 2d8173ba2..f5e57d0bc 100644 --- a/lib/routes/leetcode/dailyquestion-en.ts +++ b/lib/routes/leetcode/dailyquestion-en.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -7,7 +8,17 @@ import * as path from 'node:path'; const host = 'https://leetcode.com'; -export default async (ctx) => { +export const route: Route = { + path: '/dailyquestion/en', + radar: { + source: ['leetcode.com/'], + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler() { const question = { date: '', link: '', @@ -104,7 +115,7 @@ export default async (ctx) => { link: question.link, }; - ctx.set('data', { + return { title: 'LeetCode Daily Question', link: 'https://leetcode.com', description: 'Leetcode Daily Question', @@ -115,5 +126,5 @@ export default async (ctx) => { link: rssData.link, }, ], - }); -}; + }; +} diff --git a/lib/routes/leetcode/dailyquestion-solution-cn.ts b/lib/routes/leetcode/dailyquestion-solution-cn.ts index 2f5342758..6f951c1c3 100644 --- a/lib/routes/leetcode/dailyquestion-solution-cn.ts +++ b/lib/routes/leetcode/dailyquestion-solution-cn.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import MarkdownIt from 'markdown-it'; const md = MarkdownIt({ @@ -6,7 +7,17 @@ const md = MarkdownIt({ }); import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -export default async (ctx) => { +export const route: Route = { + path: '/dailyquestion/solution/cn', + radar: { + source: ['leetcode.cn/'], + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler() { const baseurl = `https://leetcode.cn`; const url = `${baseurl}/graphql/`; const headers = { @@ -164,7 +175,7 @@ export default async (ctx) => { s = s.replaceAll(/(```)([\d#+A-Za-z-]+)\s*?(\[.*?])?\n/g, '\r\n###$2\r\n$1$2\r\n'); return s; }; - ctx.set('data', { + return { title: 'LeetCode 每日一题题解', description: 'LeetCode 每日一题题解', link: questionUrl, @@ -183,5 +194,5 @@ export default async (ctx) => { author: art.author.username, })), ], - }); -}; + }; +} diff --git a/lib/routes/leetcode/dailyquestion-solution-en.ts b/lib/routes/leetcode/dailyquestion-solution-en.ts index 07f35ebd2..9de5abfb2 100644 --- a/lib/routes/leetcode/dailyquestion-solution-en.ts +++ b/lib/routes/leetcode/dailyquestion-solution-en.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import MarkdownIt from 'markdown-it'; const md = MarkdownIt({ @@ -7,7 +8,17 @@ const md = MarkdownIt({ import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/dailyquestion/solution/en', + radar: { + source: ['leetcode.com/'], + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler() { const baseurl = `https://leetcode.com`; const url = `${baseurl}/graphql/`; const headers = { @@ -194,7 +205,7 @@ export default async (ctx) => { return s; }; article.content = await handleText(article.content); - ctx.set('data', { + return { title: 'LeetCode DailyQuestion Solution', description: 'LeetCode DailyQuestion Solution', link: questionUrl, @@ -213,5 +224,5 @@ export default async (ctx) => { author: 'leetcode', }, ], - }); -}; + }; +} diff --git a/lib/routes/leiphone/index.ts b/lib/routes/leiphone/index.ts index dcc129f17..e649be3ac 100644 --- a/lib/routes/leiphone/index.ts +++ b/lib/routes/leiphone/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 utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/:do?/:keyword?', + radar: { + source: ['leiphone.com/'], + target: '', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const todo = ctx.req.param('do') ?? ''; const keyword = ctx.req.param('keyword') ?? ''; const rootUrl = 'https://www.leiphone.com'; @@ -17,10 +29,10 @@ export default async (ctx) => { .map((e) => $(e).attr('href')); const items = await utils.ProcessFeed(list, cache); - ctx.set('data', { + return { title: `雷峰网${todo === 'category' ? ` ${keyword}` : ''}`, description: '雷峰网 - 读懂智能&未来', link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/leiphone/newsflash.ts b/lib/routes/leiphone/newsflash.ts index 7f234bd18..724981d69 100644 --- a/lib/routes/leiphone/newsflash.ts +++ b/lib/routes/leiphone/newsflash.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import utils from './utils'; // import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/newsflash', + categories: ['traditional-media'], + example: '/leiphone/newsflash', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['leiphone.com/'], + }, + name: '业界资讯', + maintainers: [], + handler, +}; + +async function handler() { const url = 'https://www.leiphone.com/site/YejieKuaixun'; const res = await got.get(url); const article = (res.data || {}).article || []; @@ -11,10 +33,10 @@ export default async (ctx) => { const list = article.map((item) => item.url); const items = await utils.ProcessFeed(list, cache); - ctx.set('data', { + return { title: '雷峰网 业界资讯', description: '雷峰网 - 读懂智能&未来', link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/lemmy/index.ts b/lib/routes/lemmy/index.ts index 780134d7d..7aa547b6a 100644 --- a/lib/routes/lemmy/index.ts +++ b/lib/routes/lemmy/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,25 @@ import MarkdownIt from 'markdown-it'; const md = MarkdownIt({ html: true }); import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/:community/:sort?', + categories: ['new-media'], + example: '/lemmy/technology@lemmy.world/Hot', + parameters: { community: 'Lemmmy community, for example technology@lemmy.world', sort: 'Sort by, defaut to Active' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Community', + maintainers: ['wb14123'], + handler, +}; + +async function handler(ctx) { const sort = ctx.req.param('sort') ?? 'Active'; const community = ctx.req.param('community'); const communitySlices = community.split('@'); @@ -54,10 +73,10 @@ export default async (ctx) => { return item; }); - ctx.set('data', { + return { title: `${community} - ${sort} posts`, description: communityData.description, link: communityData.actor_id, item: items, - }); -}; + }; +} diff --git a/lib/routes/lfsyd/home.ts b/lib/routes/lfsyd/home.ts index 473cb5c4a..1dd9c946c 100644 --- a/lib/routes/lfsyd/home.ts +++ b/lib/routes/lfsyd/home.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 { ProcessForm, ProcessFeed } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/home', + categories: ['reading'], + example: '/lfsyd/home', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.iyingdi.com/'], + }, + name: '首页', + maintainers: ['auto-bot-ty'], + handler, +}; + +async function handler() { const rootUrl = 'https://www.iyingdi.com'; const url = 'https://api.iyingdi.com/mweb/feed/recommend-content-list'; const form = { @@ -35,9 +57,9 @@ export default async (ctx) => { })); const items = await ProcessFeed(cache, articleList); - ctx.set('data', { + return { title: '首页 - 旅法师营地', link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/lfsyd/old-home.ts b/lib/routes/lfsyd/old-home.ts index d36ede781..45b0fe2a7 100644 --- a/lib/routes/lfsyd/old-home.ts +++ b/lib/routes/lfsyd/old-home.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 { ProcessFeed } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/old_home', + categories: ['reading'], + example: '/lfsyd/old_home', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.iyingdi.com/'], + }, + name: '首页(旧版)', + maintainers: ['auto-bot-ty'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ?? 10; const rootUrl = 'https://www.iyingdi.com'; const url = `${rootUrl}/feed/list/user/v3?feedIdUp=0&feedIdDown=0&hotfeed=1&system=web`; @@ -22,9 +44,9 @@ export default async (ctx) => { const items = await ProcessFeed(cache, articleList); - ctx.set('data', { + return { title: '旅法师营地 - 首页资讯(旧版)', link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/lfsyd/tag.ts b/lib/routes/lfsyd/tag.ts index bbb69cea9..23e1095af 100644 --- a/lib/routes/lfsyd/tag.ts +++ b/lib/routes/lfsyd/tag.ts @@ -1,9 +1,21 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { ProcessForm, ProcessFeed } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/tag/:tagId?', + radar: { + source: ['mob.iyingdi.com/fine/:tagId'], + target: '/tag/:tagId', + }, + name: 'Unknown', + maintainers: ['auto-bot-ty'], + handler, +}; + +async function handler(ctx) { const tagId = ctx.req.param('tagId'); const tagList = { 17: '炉石传说', @@ -57,9 +69,9 @@ export default async (ctx) => { const items = await ProcessFeed(cache, articleList); - ctx.set('data', { + return { title: `${tagName || tagJson[0].tag} - 旅法师营地 `, link: `${rootUrl}/tz/tag/${tagId}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/lfsyd/user.ts b/lib/routes/lfsyd/user.ts index ab809ddfc..12147e5ee 100644 --- a/lib/routes/lfsyd/user.ts +++ b/lib/routes/lfsyd/user.ts @@ -1,9 +1,21 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { ProcessForm, ProcessFeed } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/user/:id?', + radar: { + source: ['www.iyingdi.com/tz/people/:id', 'www.iyingdi.com/tz/people/:id/*'], + target: '/user/:id', + }, + name: 'Unknown', + maintainers: ['auto-bot-ty'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const rootUrl = 'https://www.iyingdi.com'; const listUrl = 'https://api.iyingdi.com/web/user/event-list'; @@ -41,9 +53,9 @@ export default async (ctx) => { const items = await ProcessFeed(cache, articleList); - ctx.set('data', { + return { title: `${nickname} - 旅法师营地 `, link: `${rootUrl}/tz/people/${id}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/lifeweek/channel.ts b/lib/routes/lifeweek/channel.ts index 6bd975acf..023e5e555 100644 --- a/lib/routes/lifeweek/channel.ts +++ b/lib/routes/lifeweek/channel.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import getRssItem from './utils'; @@ -6,7 +7,18 @@ const rootApiUrl = 'https://www.lifeweek.com.cn/api/userWebFollow/getFollowTagCo const rootUrl = 'https://www.lifeweek.com.cn/column'; const articleRootUrl = 'https://www.lifeweek.com.cn/article'; -export default async (ctx) => { +export const route: Route = { + path: '/channel/:id', + radar: { + source: ['lifeweek.com.cn/column/:channel'], + target: '/channel/:channel', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const channel = ctx.req.param('id'); const url = `${rootApiUrl}=${channel}`; const { data } = await got(url); @@ -18,9 +30,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: data.model.tagName, link: `${rootUrl}/${channel}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/lifeweek/tag.ts b/lib/routes/lifeweek/tag.ts index d1bb6fa2e..98ed0c9d3 100644 --- a/lib/routes/lifeweek/tag.ts +++ b/lib/routes/lifeweek/tag.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import getRssItem from './utils'; @@ -5,7 +6,18 @@ const rootApiUrl = 'https://www.lifeweek.com.cn/api/userWebFollow/getFollowTagCo const rootUrl = 'https://www.lifeweek.com.cn/articleList'; const articleRootUrl = 'https://www.lifeweek.com.cn/article'; -export default async (ctx) => { +export const route: Route = { + path: '/tag/:id', + radar: { + source: ['lifeweek.com.cn/articleList/:tag'], + target: '/tag/:tag', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const tag = ctx.req.param('id'); const url = `${rootApiUrl}=${tag}`; const { data } = await got(url); @@ -17,9 +29,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: data.model.tagName, link: `${rootUrl}/${tag}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/lightnovel/light-novel.ts b/lib/routes/lightnovel/light-novel.ts index 978671fe0..549f8ed45 100644 --- a/lib/routes/lightnovel/light-novel.ts +++ b/lib/routes/lightnovel/light-novel.ts @@ -1,10 +1,22 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { parseDate } from '@/utils/parse-date'; import got from '@/utils/got'; import { load } from 'cheerio'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/:keywords/:security_key?', + radar: { + source: ['lightNovel.us/'], + target: '/:keywords/:security_key', + }, + name: 'Unknown', + maintainers: ['nightmare-mio'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://www.lightnovel.us'; const { type, keywords, security_key = config.lightnovel.cookie } = ctx.req.param(); const { data: response } = await got({ @@ -55,9 +67,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `轻之国度-追踪${keywords}更新-${type} `, link: baseUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/line/publisher.ts b/lib/routes/line/publisher.ts index 41c02ad40..5e07b42b1 100644 --- a/lib/routes/line/publisher.ts +++ b/lib/routes/line/publisher.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { baseUrl, parseList, parseItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/today/:edition/publisher/:id', + categories: ['traditional-media'], + example: '/line/today/th/publisher/101048', + parameters: { edition: 'Edition, see table above', id: 'Channel ID, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['today.line.me/:edition/v2/publisher/:id'], + }, + name: 'TODAY - Channel', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { edition, id } = ctx.req.param(); const { data: publisherInfo } = await got(`${baseUrl}/webapi/portal/page/setting`, { @@ -44,11 +66,11 @@ export default async (ctx) => { const items = await parseItems(list, cache.tryGet); - ctx.set('data', { + return { title: `${publisherInfo.data.name} - Line Today`, description: publisherInfo.data.introduction, image: publisherInfo.data.icon ? `https://obs.line-scdn.net/${publisherInfo.data.icon.hash}` : undefined, link: `${baseUrl}/${edition}/v2/publisher/${id}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/line/today.ts b/lib/routes/line/today.ts index 84f50615c..ad97a645d 100644 --- a/lib/routes/line/today.ts +++ b/lib/routes/line/today.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { baseUrl as rootUrl, parseList, parseItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/today/:edition?/:tab?', + categories: ['traditional-media'], + example: '/line/today', + parameters: { edition: 'Edition, see below, Taiwan by default', tab: 'Tag, can be found in URL, `top` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['today.line.me/'], + }, + name: 'TODAY', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const edition = ctx.req.param('edition') || 'tw'; const tab = ctx.req.param('tab') || 'top'; @@ -37,9 +59,9 @@ export default async (ctx) => { const items = await parseItems(list, cache.tryGet); - ctx.set('data', { + return { title: `${title} - Line Today`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/linkedin/cn/index.ts b/lib/routes/linkedin/cn/index.ts index 26ce2e389..16281072e 100644 --- a/lib/routes/linkedin/cn/index.ts +++ b/lib/routes/linkedin/cn/index.ts @@ -1,13 +1,32 @@ +import { Route } from '@/types'; import { parseSearchHit, parseJobPosting } from './utils'; const siteUrl = 'https://www.linkedin.cn/incareer/jobs/search'; -export default async (ctx) => { +export const route: Route = { + path: '/cn/jobs/:keywords?', + categories: ['other'], + example: '/linkedin/cn/jobs/Software', + parameters: { keywords: '搜索关键字' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Jobs', + maintainers: ['bigfei'], + handler, +}; + +async function handler(ctx) { const { title, jobs } = await parseSearchHit(ctx); const items = await Promise.all(jobs.map((job) => parseJobPosting(ctx, job))); - ctx.set('data', { + return { title: `领英 - ${title}`, link: siteUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/linkedin/jobs.ts b/lib/routes/linkedin/jobs.ts index 8f1cdb41a..b59d32082 100644 --- a/lib/routes/linkedin/jobs.ts +++ b/lib/routes/linkedin/jobs.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { parseJobSearch, KEYWORDS_QUERY_KEY, JOB_TYPES, JOB_TYPES_QUERY_KEY, EXP_LEVELS_QUERY_KEY, parseParamsToSearchParams, EXP_LEVELS, parseParamsToString } from './utils'; @@ -5,7 +6,25 @@ import { parseJobSearch, KEYWORDS_QUERY_KEY, JOB_TYPES, JOB_TYPES_QUERY_KEY, EXP const BASE_URL = 'https://www.linkedin.com/'; const JOB_SEARCH_PATH = '/jobs-guest/jobs/api/seeMoreJobPostings/search'; -export default async (ctx) => { +export const route: Route = { + path: '/jobs/:job_types/:exp_levels/:keywords?', + categories: ['other'], + example: '/linkedin/jobs/C-P/1/software engineer', + parameters: { job_types: "See the following table for details, use '-' as delimiter", exp_levels: "See the following table for details, use '-' as delimiter", keywords: 'keywords' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Jobs', + maintainers: [], + handler, +}; + +async function handler(ctx) { const jobTypesParam = parseParamsToSearchParams(ctx.req.param('job_types'), JOB_TYPES); const expLevelsParam = parseParamsToSearchParams(ctx.req.param('exp_levels'), EXP_LEVELS); @@ -25,7 +44,7 @@ export default async (ctx) => { const jobTypes = parseParamsToString(ctx.req.param('job_types'), JOB_TYPES); const expLevels = parseParamsToString(ctx.req.param('exp_levels'), EXP_LEVELS); const feedTitle = 'LinkedIn Job Listing' + (jobTypes ? ` | Job Types: ${jobTypes}` : '') + (expLevels ? ` | Experience Levels: ${expLevels}` : '') + (ctx.req.param('keywords') ? ` | Keywords: ${ctx.req.param('keywords')}` : ''); - ctx.set('data', { + return { title: feedTitle, link: url, description: 'This feed gets LinkedIn job posts', @@ -40,5 +59,5 @@ export default async (ctx) => { link: job.link, // job source link }; }), - }); -}; + }; +} diff --git a/lib/routes/linkresearcher/index.ts b/lib/routes/linkresearcher/index.ts index 8d1d925fb..567144f5e 100644 --- a/lib/routes/linkresearcher/index.ts +++ b/lib/routes/linkresearcher/index.ts @@ -1,10 +1,18 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import qs from 'query-string'; import { parseDate } from '@/utils/parse-date'; const baseURL = 'https://www.linkresearcher.com'; -export default async (ctx) => { +export const route: Route = { + path: '/:params', + name: 'Unknown', + maintainers: ['yech1990'], + handler, +}; + +async function handler(ctx) { // parse params const params = ctx.req.param('params'); const query = qs.parse(params); @@ -57,12 +65,12 @@ export default async (ctx) => { author: item.authors === undefined ? '' : item.authors.join(', '), })); - ctx.set('data', { + return { title: `领研 | ${title}`, description: '领研是链接华人学者的人才及成果平台。领研为国内外高校、科研机构及科技企业提供科研人才招聘服务,也是青年研究者的职业发展指导及线上培训平台;研究者还可将自己的研究论文上传至领研,与超过五十万华人学者分享工作的最新进展。', image: 'https://www.linkresearcher.com/assets/images/logo-app.png', link: baseURL, item: out, - }); -}; + }; +} diff --git a/lib/routes/linovelib/novel.ts b/lib/routes/linovelib/novel.ts index b5eefcbcc..113f3d09f 100644 --- a/lib/routes/linovelib/novel.ts +++ b/lib/routes/linovelib/novel.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: '/novel/:id', + categories: ['government'], + example: '/linovelib/novel/2547', + parameters: { id: '小说 id,对应书架开始阅读 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '小说更新', + maintainers: ['misakicoca'], + handler, +}; + +async function handler(ctx) { const response = await got(`https://www.linovelib.com/novel/${ctx.req.param('id')}/catalog`); const $ = load(response.data); @@ -23,11 +42,11 @@ export default async (ctx) => { .get(); items.reverse(); - ctx.set('data', { + return { title: `哩哔轻小说 - ${title}`, link: `https://www.linovelib.com/novel/${ctx.req.param('id')}/catalog`, description: title, language: 'zh', item: items, - }); -}; + }; +} diff --git a/lib/routes/liquipedia/cs-matches.ts b/lib/routes/liquipedia/cs-matches.ts index aecebc81e..fe2eb27b6 100644 --- a/lib/routes/liquipedia/cs-matches.ts +++ b/lib/routes/liquipedia/cs-matches.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: '/counterstrike/matches/:team', + radar: { + source: ['liquipedia.net/counterstrike/:id/Matches', 'liquipedia.net/dota2/:id'], + target: '/counterstrike/matches/:id', + }, + name: 'Unknown', + maintainers: ['CookiePieWw'], + handler, +}; + +async function handler(ctx) { const team = ctx.req.param('team'); const rootUrl = 'https://liquipedia.net'; @@ -38,9 +50,9 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title: `[Counter-Strike] ${team} Match Results From Liquipedia`, link: currentUrl, item: matches, - }); -}; + }; +} diff --git a/lib/routes/liquipedia/dota2-matches.ts b/lib/routes/liquipedia/dota2-matches.ts index 5638a7979..ead29039f 100644 --- a/lib/routes/liquipedia/dota2-matches.ts +++ b/lib/routes/liquipedia/dota2-matches.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: '/dota2/matches/:id', + categories: ['reading'], + example: '/liquipedia/dota2/matches/Team_Aster', + parameters: { id: '战队名称,可在url中找到。例如:https://liquipedia.net/dota2/Team_Aster' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['liquipedia.net/dota2/:id'], + }, + name: 'Dota2 战队最近比赛结果', + maintainers: ['wzekin'], + handler, +}; + +async function handler(ctx) { const team = ctx.req.param('id'); const url = `https://liquipedia.net/dota2/${team}`; const response = await got({ @@ -15,7 +37,7 @@ export default async (ctx) => { const $ = load(data); const list = $('div.recent-matches > table > tbody > tr[style]'); - ctx.set('data', { + return { title: `Liquipedia Dota2 ${team} Matches`, link: url, item: list?.toArray().map((item) => { @@ -43,5 +65,5 @@ export default async (ctx) => { guid: url + dateTime, }; }), - }); -}; + }; +} diff --git a/lib/routes/literotica/category.ts b/lib/routes/literotica/category.ts index 30125c243..d060f16ee 100644 --- a/lib/routes/literotica/category.ts +++ b/lib/routes/literotica/category.ts @@ -1,9 +1,20 @@ +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/:category', + radar: { + source: ['literotica.com/c/:category', 'literotica.com/'], + }, + name: 'Unknown', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category'); const rootUrl = 'https://www.literotica.com'; @@ -48,9 +59,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/literotica/new.ts b/lib/routes/literotica/new.ts index 5130adc76..36d9a817e 100644 --- a/lib/routes/literotica/new.ts +++ b/lib/routes/literotica/new.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: '/new', + categories: ['government'], + example: '/literotica/new', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['literotica.com/'], + }, + name: 'New Stories', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://www.literotica.com'; const currentUrl = `${rootUrl}/stories/new_submissions.php`; @@ -52,9 +74,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/liulinblog/index.ts b/lib/routes/liulinblog/index.ts index 02f90e5f1..5556aa97a 100644 --- a/lib/routes/liulinblog/index.ts +++ b/lib/routes/liulinblog/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 { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:params{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const params = ctx.req.param('params'); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 20; @@ -84,7 +92,7 @@ export default async (ctx) => { const icon = $('link[rel="icon"]').prop('href'); const title = $('img.logo').prop('alt'); - ctx.set('data', { + return { item: items, title: `${title} - ${params ? $('h1.term-title').text().split('搜索到')[0] : '最新'}`, link: currentUrl, @@ -95,5 +103,5 @@ export default async (ctx) => { logo: icon, subtitle: $('p.term-description').text(), author: title, - }); -}; + }; +} diff --git a/lib/routes/liulinblog/itnews.ts b/lib/routes/liulinblog/itnews.ts index 4b6d2be02..bc627efc5 100644 --- a/lib/routes/liulinblog/itnews.ts +++ b/lib/routes/liulinblog/itnews.ts @@ -1,5 +1,13 @@ -export default (ctx) => { +import { Route } from '@/types'; +export const route: Route = { + path: '/itnews/:channel', + name: 'Unknown', + maintainers: [], + handler, +}; + +function handler(ctx) { const { channel } = ctx.req.param(); const redirectTo = `/liulinblog/${channel}`; ctx.redirect(redirectTo); -}; +} diff --git a/lib/routes/liveuamap/index.ts b/lib/routes/liveuamap/index.ts index 6b5cc5747..59fd9051b 100644 --- a/lib/routes/liveuamap/index.ts +++ b/lib/routes/liveuamap/index.ts @@ -1,8 +1,31 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { isValidHost } from '@/utils/valid-host'; -export default async (ctx) => { +export const route: Route = { + path: '/:region?', + categories: ['traditional-media'], + example: '/liveuamap', + parameters: { region: 'region 热点地区,默认为`ukraine`,其他选项见liveuamap.com的三级域名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['liveuamap.com/:region*'], + target: '/:region', + }, + name: '实时消息', + maintainers: ['CoderSherlock'], + handler, +}; + +async function handler(ctx) { const region = ctx.req.param('region') ?? 'ukraine'; const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 50; if (!isValidHost(region)) { @@ -29,9 +52,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `Liveuamap - ${region}`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/lkong/forum.ts b/lib/routes/lkong/forum.ts index af9586348..f76ddb52b 100644 --- a/lib/routes/lkong/forum.ts +++ b/lib/routes/lkong/forum.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 { viewForum, viewThread } from './query'; -export default async (ctx) => { +export const route: Route = { + path: '/forum/:id?/:digest?', + radar: { + source: ['lkong.com/forum/:id', 'lkong.com/'], + }, + name: 'Unknown', + maintainers: ['nczitzk', 'ma6254'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? '8'; const digest = ctx.req.param('digest'); @@ -50,10 +61,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${response.data.data.forum.name} - 龙空`, link: currentUrl, item: items, description: response.data.data.forumCount.info, - }); -}; + }; +} diff --git a/lib/routes/lkong/thread.ts b/lib/routes/lkong/thread.ts index 9bae138c7..0181a6602 100644 --- a/lib/routes/lkong/thread.ts +++ b/lib/routes/lkong/thread.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 * as path from 'node:path'; import { viewThread, countReplies } from './query'; -export default async (ctx) => { +export const route: Route = { + path: '/thread/:id', + radar: { + source: ['lkong.com/thread/:id', 'lkong.com/'], + }, + name: 'Unknown', + maintainers: ['nczitzk', 'ma6254'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const rootUrl = 'https://www.lkong.com'; @@ -52,9 +63,9 @@ export default async (ctx) => { }), })); - ctx.set('data', { + return { title: `${response.data.data.thread.title} - 龙空`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/lofter/tag.ts b/lib/routes/lofter/tag.ts index d1466cfd5..343a44ad0 100644 --- a/lib/routes/lofter/tag.ts +++ b/lib/routes/lofter/tag.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 { JSDOM } from 'jsdom'; -export default async (ctx) => { +export const route: Route = { + path: '/tag/:name?/:type?', + categories: ['new-media'], + example: '/lofter/tag/cosplay/date', + parameters: { name: 'tag name, such as `名侦探柯南`, `摄影` by default', type: 'ranking type, see below, new by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Tag', + maintainers: ['hoilc', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const name = ctx.req.param('name') ?? '摄影'; const type = ctx.req.param('type') ?? 'new'; const pageSize = 20; @@ -86,9 +105,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `${name} - ${title} | LOFTER`, link: linkUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/lofter/user.ts b/lib/routes/lofter/user.ts index 81716427b..da232da26 100644 --- a/lib/routes/lofter/user.ts +++ b/lib/routes/lofter/user.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { isValidHost } from '@/utils/valid-host'; -export default async (ctx) => { +export const route: Route = { + path: '/user/:name?', + categories: ['new-media'], + example: '/lofter/user/i', + parameters: { name: 'Lofter user name, can be found in the URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'User', + maintainers: ['hondajojo', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const name = ctx.req.param('name') ?? 'i'; const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : '50'; if (!isValidHost(name)) { @@ -52,10 +71,10 @@ export default async (ctx) => { category: item.post.tag.split(','), })); - ctx.set('data', { + return { title: `${items[0].author} | LOFTER`, link: rootUrl, item: items, description: response.data.response.posts[0].post.blogInfo.selfIntro, - }); -}; + }; +} diff --git a/lib/routes/logclub/index.ts b/lib/routes/logclub/index.ts index f3c25e97b..16661d1de 100644 --- a/lib/routes/logclub/index.ts +++ b/lib/routes/logclub/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: '/:category{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { category = 'news' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 11; @@ -120,7 +128,7 @@ export default async (ctx) => { const subtitle = $('meta[name="keywords"]').prop('content'); const author = subtitle.split(/,/)[0]; - ctx.set('data', { + return { item: items, title: $('title').text().split(/-/)[0].trim(), link: currentUrl, @@ -133,5 +141,5 @@ export default async (ctx) => { author, itunes_author: author, itunes_category: 'News', - }); -}; + }; +} diff --git a/lib/routes/logclub/report.ts b/lib/routes/logclub/report.ts index bdc276147..dc6b733d5 100644 --- a/lib/routes/logclub/report.ts +++ b/lib/routes/logclub/report.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: ['/lc_report/:id?', '/report/:id?'], + categories: ['traditional-media'], + example: '/logclub/lc_report', + 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 = 'Report' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 11; @@ -87,7 +106,7 @@ export default async (ctx) => { const icon = new URL($('link[rel="shortcut icon"]').prop('href'), rootUrl).href; const subtitle = $('meta[name="keywords"]').prop('content'); - ctx.set('data', { + return { item: items, title: `${$('title').text()}${title}`, link: currentUrl, @@ -98,5 +117,5 @@ export default async (ctx) => { logo: icon, subtitle: subtitle.replaceAll(',', ''), author: subtitle.split(/,/)[0], - }); -}; + }; +} diff --git a/lib/routes/logonews/index.ts b/lib/routes/logonews/index.ts index 9d9da2545..9f893807c 100644 --- a/lib/routes/logonews/index.ts +++ b/lib/routes/logonews/index.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 { 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: ['/work/tags/:tag', '/tag/:tag', '*'], + radar: { + source: ['logonews.cn/work/tags/:tag'], + }, + name: 'Unknown', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const params = getSubPath(ctx); const isWork = params.indexOf('/work') === 0; @@ -73,9 +84,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/loltw/news.ts b/lib/routes/loltw/news.ts index 2a391fc59..4e6db2605 100644 --- a/lib/routes/loltw/news.ts +++ b/lib/routes/loltw/news.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 { 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/:category?', + categories: ['reading'], + example: '/loltw/news', + parameters: { category: '新闻分类,置空为全部新闻' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '台服新闻', + maintainers: ['hoilc'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? ''; const baseUrl = 'https://lol.garena.tw'; @@ -32,9 +51,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '英雄联盟 - 台服新闻', link: category ? `${baseUrl}/news/${category}` : `${baseUrl}/news`, item: items, - }); -}; + }; +} diff --git a/lib/routes/lovelive-anime/news.ts b/lib/routes/lovelive-anime/news.ts index 5cf534e07..c378a5b4d 100644 --- a/lib/routes/lovelive-anime/news.ts +++ b/lib/routes/lovelive-anime/news.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 * as path from 'node:path'; import { art } from '@/utils/render'; const renderDescription = (desc) => art(path.join(__dirname, 'templates/description.art'), desc); -export default async (ctx) => { +export const route: Route = { + path: '/news/:option?', + categories: ['program-update'], + example: '/lovelive-anime/news', + parameters: { option: 'Crawl full text when `option` is `detail`.' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.lovelive-anime.jp/', 'www.lovelive-anime.jp/news'], + target: '/news', + }, + name: 'Love Live! Official Website Latest NEWS', + maintainers: ['axojhf'], + handler, +}; + +async function handler(ctx) { const rootUrl = 'https://www.lovelive-anime.jp/news/'; const response = await got(rootUrl); @@ -52,9 +75,9 @@ export default async (ctx) => { ); } - ctx.set('data', { + return { title: 'lovelive official website news', link: 'https://www.lovelive-anime.jp/news/', item: items, - }); -}; + }; +} diff --git a/lib/routes/lovelive-anime/schedules.ts b/lib/routes/lovelive-anime/schedules.ts index 50a63d910..e76a0fe16 100644 --- a/lib/routes/lovelive-anime/schedules.ts +++ b/lib/routes/lovelive-anime/schedules.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 dayjs from 'dayjs'; import utc from 'dayjs/plugin/utc'; import timezone from 'dayjs/plugin/timezone'; -export default async (ctx) => { +export const route: Route = { + path: '/schedules/:serie?/:category?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { dayjs.extend(utc); dayjs.extend(timezone); const serie = ctx.req.param('serie'); @@ -49,11 +57,11 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `${category} - ${serie} - Love Live Official Website Schedule`, link: 'https://www.lovelive-anime.jp/schedule/', item: items, description: 'The schedule date and other information shall be subject to the announcement on the official website. The RSS route has not been strictly tested, and the captured information cannot be guaranteed to be correct.', - }); -}; + }; +} diff --git a/lib/routes/lovelive-anime/topics.ts b/lib/routes/lovelive-anime/topics.ts index 1d7ff3443..08bb77279 100644 --- a/lib/routes/lovelive-anime/topics.ts +++ b/lib/routes/lovelive-anime/topics.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 { parseDate } from '@/utils/parse-date'; const renderDescription = (desc) => art(path.join(__dirname, 'templates/description.art'), desc); -export default async (ctx) => { +export const route: Route = { + path: '/topics/:abbr/:category?/:option?', + categories: ['program-update'], + example: '/lovelive-anime/topics/otonokizaka', + parameters: { + abbr: 'The path to the Love Live series of sub-projects on the official website is detailed in the table below', + category: 'The official website lists the Topics category, `category` is `detail` when crawling the full text, other categories see the following table for details', + option: 'Crawl full text when `option` is `detail`.', + }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Love Live Official Website Categories Topics', + maintainers: ['axojhf'], + handler, +}; + +async function handler(ctx) { const abbr = ctx.req.param('abbr'); const rootUrl = `https://www.lovelive-anime.jp/${abbr}`; const topicsUrlPart = 'yuigaoka' === abbr ? 'topics/' : 'topics.php'; @@ -75,9 +98,9 @@ export default async (ctx) => { ); } - ctx.set('data', { + return { title: `${categoryName} - ${abbrDetail[abbr]} - Love Live Official Website Topics`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/lsnu/jiaowc/tzgg.ts b/lib/routes/lsnu/jiaowc/tzgg.ts index b18930ab7..c914178d6 100644 --- a/lib/routes/lsnu/jiaowc/tzgg.ts +++ b/lib/routes/lsnu/jiaowc/tzgg.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: '/jiaowc/tzgg/:category?', + categories: ['forecast'], + example: '/lsnu/jiaowc/tzgg', + parameters: { category: '分类名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['lsnu.edu.cn/'], + target: '/jiaowc/tzgg', + }, + name: '教学部通知公告', + maintainers: ['nyaShine'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category'); const url = category ? `https://jiaowc.lsnu.edu.cn/tzgg/${category}.htm` : 'https://jiaowc.lsnu.edu.cn/tzgg.htm'; @@ -45,9 +68,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: '乐山师范学院教学部通知公告', link: 'https://jiaowc.lsnu.edu.cn/tzgg.htm', item: out, - }); -}; + }; +} diff --git a/lib/routes/luogu/contest.ts b/lib/routes/luogu/contest.ts index b93b1cdd5..91d0a5e35 100644 --- a/lib/routes/luogu/contest.ts +++ b/lib/routes/luogu/contest.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,28 @@ const typeMap = { // }, }; -export default async (ctx) => { +export const route: Route = { + path: '/contest', + categories: ['design'], + example: '/luogu/contest', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['luogu.com.cn/contest/list', 'luogu.com.cn/'], + }, + name: '比赛列表', + maintainers: ['prnake'], + handler, +}; + +async function handler() { const link = `${baseUrl}/contest/list`; const { data: response } = await got(link); const $ = load(response); @@ -65,10 +87,10 @@ export default async (ctx) => { result.push(item); } - ctx.set('data', { + return { title: $('head title').text(), link, image: 'https://www.luogu.com.cn/favicon.ico', item: result, - }); -}; + }; +} diff --git a/lib/routes/luogu/daily.ts b/lib/routes/luogu/daily.ts index 1413a1b85..3ee4fb59a 100644 --- a/lib/routes/luogu/daily.ts +++ b/lib/routes/luogu/daily.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: '/daily/:id?', + categories: ['design'], + example: '/luogu/daily', + parameters: { id: '年度日报所在帖子 id,可在 URL 中找到,不填默认为 `47327`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['luogu.com.cn/discuss/47327', 'luogu.com.cn/'], + target: '/daily', + }, + name: '日报', + maintainers: ['LogicJake ', 'prnake ', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? 47327; const link = `https://www.luogu.com.cn/discuss/${id}`; const response = await got(link); @@ -30,9 +53,9 @@ export default async (ctx) => { }, ]; - ctx.set('data', { + return { title: '洛谷日报', link, item, - }); -}; + }; +} diff --git a/lib/routes/luogu/user-blog.ts b/lib/routes/luogu/user-blog.ts index 49c4e708d..58d7fbba3 100644 --- a/lib/routes/luogu/user-blog.ts +++ b/lib/routes/luogu/user-blog.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: '/user/blog/:name', + categories: ['design'], + example: '/luogu/user/blog/ftiasch', + parameters: { name: '博客名称' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['luogu.com.cn/blog/:name'], + }, + name: '用户博客', + maintainers: [], + handler, +}; + +async function handler(ctx) { const name = ctx.req.param('name'); const blogBaseUrl = `https://www.luogu.com.cn/blog/${name}/`; @@ -41,9 +63,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: blogTitle, link: blogBaseUrl, item, - }); -}; + }; +} diff --git a/lib/routes/luogu/user-feed.ts b/lib/routes/luogu/user-feed.ts index ca5abb567..f71b33a35 100644 --- a/lib/routes/luogu/user-feed.ts +++ b/lib/routes/luogu/user-feed.ts @@ -1,10 +1,32 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import MarkdownIt from 'markdown-it'; const md = MarkdownIt(); -export default async (ctx) => { +export const route: Route = { + path: '/user/feed/:uid', + categories: ['design'], + example: '/luogu/user/feed/1', + parameters: { uid: '用户 UID' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['luogu.com.cn/user/:uid'], + }, + name: '用户动态', + maintainers: ['solstice23'], + handler, +}; + +async function handler(ctx) { const getUsernameFromUID = (uid) => cache.tryGet('luogu:username:' + uid, async () => { const { data } = await got(`https://www.luogu.com.cn/user/${uid}?_contentOnly=1`); @@ -17,7 +39,7 @@ export default async (ctx) => { const data = response.feeds.result; - ctx.set('data', { + return { title: `${name} 的洛谷动态`, link: `https://www.luogu.com.cn/user/${uid}#activity`, allowEmpty: true, @@ -29,5 +51,5 @@ export default async (ctx) => { link: `https://www.luogu.com.cn/user/${uid}#activity`, guid: item.id, })), - }); -}; + }; +} diff --git a/lib/routes/luxiangdong/archive.ts b/lib/routes/luxiangdong/archive.ts index 4c7c594bf..17a4937a6 100644 --- a/lib/routes/luxiangdong/archive.ts +++ b/lib/routes/luxiangdong/archive.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: '/archive', + categories: ['programming'], + example: '/luxiangdong/archive', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['luxiangdong.com/'], + }, + name: '文章', + maintainers: ['Levix'], + handler, +}; + +async function handler() { const { data } = await got(`https://www.luxiangdong.com/content.json?t=${Date.now()}`); const items = data.posts.map((item) => ({ @@ -17,12 +39,12 @@ export default async (ctx) => { category: item.tags.map((tag) => tag.name), })); - ctx.set('data', { + return { // 源标题 title: '土猛的员外', // 源链接 link: 'https://www.luxiangdong.com/', // 源文章 item: items, - }); -}; + }; +} diff --git a/lib/routes/lvv2/news.ts b/lib/routes/lvv2/news.ts index e966e654f..a72bc1e1d 100644 --- a/lib/routes/lvv2/news.ts +++ b/lib/routes/lvv2/news.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 titleMap = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/news/:channel/:sort?', + categories: ['traditional-media'], + example: '/lvv2/news/sort-score', + parameters: { channel: '频道,见下表', sort: '排序方式,仅得分和24小时榜可选填该参数,见下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '频道', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const channel = ctx.req.param('channel'); const sort = (channel === 'sort-realtime' || channel === 'sort-score') && !ctx.req.param('sort') ? 't-week' : ctx.req.param('sort'); const url = `${rootUrl}/${channel}/${sort}`; @@ -70,9 +89,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `lvv2 - ${sort ? titleMap[channel][sort] : titleMap[channel]}`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/lvv2/top.ts b/lib/routes/lvv2/top.ts index 17db338e3..f3664c8a6 100644 --- a/lib/routes/lvv2/top.ts +++ b/lib/routes/lvv2/top.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 titleMap = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/top/:channel/:sort?', + categories: ['traditional-media'], + example: '/lvv2/top/sort-score', + parameters: { channel: '频道,见下表', sort: '排序方式,仅得分和24小时榜可选填该参数,见下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '24 小时点击排行 Top 10', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const channel = ctx.req.param('channel'); const sort = (channel === 'sort-realtime' || channel === 'sort-score') && !ctx.req.param('sort') ? 't-week' : ctx.req.param('sort'); const url = `${rootUrl}/${channel}/${sort}`; @@ -72,9 +91,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `lvv2 - ${sort ? titleMap[channel][sort] : titleMap[channel]} 24小时点击 Top 10`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/lxixsxa/discography.ts b/lib/routes/lxixsxa/discography.ts index e6cbf10d3..3de3a5c96 100644 --- a/lib/routes/lxixsxa/discography.ts +++ b/lib/routes/lxixsxa/discography.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 { parseJSONP } from './jsonp-helper'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/disco', + categories: ['multimedia'], + example: '/lxixsxa/disco', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.lxixsxa.com/', 'www.lxixsxa.com/discography'], + }, + name: 'Latest Discography', + maintainers: ['Kiotlin'], + handler, +}; + +async function handler() { const api = 'https://www.sonymusic.co.jp/json/v2/artist/lisa/discography/start/0/count/-1'; const url = 'https://www.sonymusic.co.jp/artist/lisa/discography'; @@ -29,7 +51,7 @@ export default async (ctx) => { comment: item.catch_copy === '' ? '今日もいい日だ!' : item.catch_copy, })); - ctx.set('data', { + return { // the source title title, // the source url @@ -53,5 +75,5 @@ export default async (ctx) => { // the article link link: `${url}/${item.referID}`, })), - }); -}; + }; +} diff --git a/lib/routes/lxixsxa/information.ts b/lib/routes/lxixsxa/information.ts index d9cb931a5..211469152 100644 --- a/lib/routes/lxixsxa/information.ts +++ b/lib/routes/lxixsxa/information.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 { 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', + categories: ['multimedia'], + example: '/lxixsxa/info', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.lxixsxa.com/', 'www.lxixsxa.com/info'], + }, + name: 'News', + maintainers: ['Kiotlin'], + handler, +}; + +async function handler() { const api = 'https://www.sonymusic.co.jp/json/v2/artist/lisa/information/start/0/count/-1'; const url = 'https://www.sonymusic.co.jp/artist/lisa/info'; @@ -26,7 +48,7 @@ export default async (ctx) => { description: item.article, })); - ctx.set('data', { + return { // the source title title, // the source url @@ -47,5 +69,5 @@ export default async (ctx) => { // the article link link: `${url}/${item.id}`, })), - }); -}; + }; +} diff --git a/lib/routes/m4/index.ts b/lib/routes/m4/index.ts index 79e7cdcc5..6cb2fc8d6 100644 --- a/lib/routes/m4/index.ts +++ b/lib/routes/m4/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: '/:id?/:category{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { id = 'news', category = 'china' } = ctx.req.param(); if (!isValidHost(id)) { throw new Error('Invalid id'); @@ -76,7 +84,7 @@ export default async (ctx) => { const image = $('a.logo0_b img').prop('src'); - ctx.set('data', { + return { item: items, title: $('title').text(), link: currentUrl, @@ -86,5 +94,5 @@ export default async (ctx) => { subtitle: $('meta[name="keywords"]').prop('content'), author: $('meta[name="author"]').prop('content'), allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/macfilos/blog.ts b/lib/routes/macfilos/blog.ts index c32cf8580..fff9817e6 100644 --- a/lib/routes/macfilos/blog.ts +++ b/lib/routes/macfilos/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: ['traditional-media'], + example: '/macfilos/blog', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['macfilos.com/blog', 'macfilos.com/'], + }, + name: 'Blog', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const rootUrl = 'https://www.macfilos.com'; const currentUrl = `${rootUrl}/blog`; @@ -54,9 +76,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/macmenubar/recently.ts b/lib/routes/macmenubar/recently.ts index af0da5c97..afd45cfaf 100644 --- a/lib/routes/macmenubar/recently.ts +++ b/lib/routes/macmenubar/recently.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; async function getCategoryId(categories) { @@ -11,7 +12,25 @@ async function getCategoryId(categories) { return response.reduce((queryString, item) => queryString + item.id + ',', ''); } -export default async (ctx) => { +export const route: Route = { + path: '/recently/:category?', + categories: ['programming'], + example: '/macmenubar/recently/developer-apps,system-tools', + parameters: { category: 'Category path name, seperate by comma, default is all categories. Category path name can be found in url' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Recently', + maintainers: ['5upernova-heng'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://macmenubar.com/wp-json/wp/v2/posts'; const categories = ctx.req.param('category'); const searchParams = { @@ -39,9 +58,9 @@ export default async (ctx) => { category: [...tags, ...categories], }; }); - ctx.set('data', { + return { title: 'Recent Posts | MacMenuBar.com', link: 'https://macmenubar.com/recently-added/', item: items, - }); -}; + }; +} diff --git a/lib/routes/macupdate/app.ts b/lib/routes/macupdate/app.ts index 0e2ddd5b6..22a38958f 100644 --- a/lib/routes/macupdate/app.ts +++ b/lib/routes/macupdate/app.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: '/app/:appId/:appSlug?', + categories: ['university'], + example: '/macupdate/app/11942', + parameters: { appId: 'Application unique ID, can be found in URL', appSlug: 'Application slug, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['macupdate.com/app/mac/:appId/:appSlug'], + }, + name: 'Update', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { appId, appSlug } = ctx.req.param(); const baseUrl = 'https://www.macupdate.com'; const link = `${baseUrl}/app/mac/${appId}${appSlug ? `/${appSlug}` : ''}`; @@ -27,7 +49,7 @@ export default async (ctx) => { author: appData.developer.name, }; - ctx.set('data', { + return { title: appData.title, description: appData.description, link: `${baseUrl}${asPath}`, @@ -35,9 +57,9 @@ export default async (ctx) => { icon: appData.logo.source, item: [item], language: 'en', - }); + }; ctx.set('json', { pageProps: nextData.props.initialProps.pageProps, }); -}; +} diff --git a/lib/routes/magazinelib/latest-magazine.ts b/lib/routes/magazinelib/latest-magazine.ts index dffb4b7d0..12fa6fc36 100644 --- a/lib/routes/magazinelib/latest-magazine.ts +++ b/lib/routes/magazinelib/latest-magazine.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'; const host = 'https://magazinelib.com'; -export default async (ctx) => { +export const route: Route = { + path: '/latest-magazine/:query?', + categories: ['government'], + example: '/magazinelib/latest-magazine/new+yorker', + parameters: { query: 'query, search page querystring' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Latest Magazine', + maintainers: ['EthanWng97'], + handler, +}; + +async function handler(ctx) { const query = ctx.req.param('query'); const url = `${host}/wp-json/wp/v2/posts/`; const response = await got({ @@ -43,7 +62,7 @@ export default async (ctx) => { return data; }); - ctx.set('data', { + return { title: `MagazineLib - Latest Magazines${subTitle}`, link: `{host}/?s=${query}`, description: `MagazineLib - Latest Magazines${subTitle}`, @@ -54,5 +73,5 @@ export default async (ctx) => { pubDate: new Date(item.pubDate).toUTCString(), description: item.description, })), - }); -}; + }; +} diff --git a/lib/routes/mail/imap.ts b/lib/routes/mail/imap.ts index b23f2a0d9..93ae00646 100644 --- a/lib/routes/mail/imap.ts +++ b/lib/routes/mail/imap.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { ImapFlow } from 'imapflow'; import { config } from '@/config'; @@ -5,7 +6,14 @@ import { simpleParser } from 'mailparser'; import logger from '@/utils/logger'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/imap/:email/:folder{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { email, folder = 'INBOX' } = ctx.req.param(); const { limit = 10 } = ctx.req.query(); const mailConfig = { @@ -91,10 +99,10 @@ export default async (ctx) => { await client.logout(); - ctx.set('data', { + return { title: `${email}'s Inbox${folder === 'INBOX' ? '' : ` - ${folder}`}`, link: `https://${email.split('@')[1]}`, item: items, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/mangadex/index.ts b/lib/routes/mangadex/index.ts index 493099a75..80ff36463 100644 --- a/lib/routes/mangadex/index.ts +++ b/lib/routes/mangadex/index.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: '/:id/:lang?', + radar: { + source: ['mangadex.org/title/:id/*', 'mangadex.org/title/:id'], + target: '/:id', + }, + name: 'Unknown', + maintainers: ['vzz64'], + handler, +}; + +async function handler(ctx) { const { id, lang } = ctx.req.param(); let { data } = await got.get(`https://api.mangadex.org/manga/${id}`); if (data.result === 'error') { @@ -43,7 +55,7 @@ export default async (ctx) => { if (data.result === 'error') { throw new Error(data.errors[0].detail); } - ctx.set('data', { + return { title: `${title} - MangaDex`, link: `https://mangadex.org/title/${id}`, description, @@ -66,5 +78,5 @@ export default async (ctx) => { }; }), language: lang, - }); -}; + }; +} diff --git a/lib/routes/manhuagui/comic.ts b/lib/routes/manhuagui/comic.ts index a6219f1a0..10fc8eaf2 100644 --- a/lib/routes/manhuagui/comic.ts +++ b/lib/routes/manhuagui/comic.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { load } from 'cheerio'; import got from '@/utils/got'; import LZString from 'lz-string'; @@ -43,7 +44,29 @@ const getChapters = ($) => { .reduce((acc, curr) => [...acc, ...curr]); }; -export default async (ctx) => { +export const route: Route = { + path: ['/comic/:id/:chapterCnt?', '/:domain?/comic/:id/:chapterCnt?'], + categories: ['program-update'], + example: '/manhuagui/comic/22942/5', + parameters: { id: '漫画ID', chapterCnt: '返回章节的数量,默认为0,返回所有章节' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.mhgui.com/comic/:id/'], + target: '/comic/:id', + }, + name: '漫画更新', + maintainers: ['MegrezZhu'], + handler, +}; + +async function handler(ctx) { const { id, domain } = ctx.req.param(); if (domain === 'mhgui') { baseUrl = 'https://www.mhgui.com'; @@ -72,12 +95,12 @@ export default async (ctx) => { const reg = /最近[于於].+更新至/; // 处理已下架的漫画 if ($('.status > span').text().indexOf('已下架') > 0) { - ctx.set('data', { + return { title: `看漫画 - ${bookTitle} 已下架`, link: `${baseUrl}/comic/${id}/`, description: bookIntro, item: [{ link: `${baseUrl}/comic/${id}/`, title: bookTitle, description: '已下架' }], - }); + }; } else { const pub_date_str = $('.status > span') .text() @@ -104,11 +127,11 @@ export default async (ctx) => { itemsLen = chapterCnt < $.newChapterCnt ? $.newChapterCnt : chapterCnt; } - ctx.set('data', { + return { title: `看漫画 - ${bookTitle}`, link: `${baseUrl}/comic/${id}/`, description: bookIntro, item: items.slice(0, itemsLen), - }); + }; } -}; +} diff --git a/lib/routes/manhuagui/subscribe.ts b/lib/routes/manhuagui/subscribe.ts index d457b9973..36b138e4c 100644 --- a/lib/routes/manhuagui/subscribe.ts +++ b/lib/routes/manhuagui/subscribe.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 { config } from '@/config'; const web_url = 'https://www.manhuagui.com/user/book/shelf/1'; -export default async (ctx) => { +export const route: Route = { + path: '/subscribe', + categories: ['program-update'], + example: '/manhuagui/subscribe', + parameters: {}, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.mhgui.com/user/book/shelf'], + }, + name: '漫画个人订阅', + maintainers: ['shininome'], + handler, +}; + +async function handler() { if (!config.manhuagui || !config.manhuagui.cookie) { throw new Error('manhuagui RSS is disabled due to the lack of relevant config'); } @@ -48,10 +70,10 @@ export default async (ctx) => { return single; }) .get(); // 这里获取数组= = - ctx.set('data', { + return { title, link, description, item, - }); -}; + }; +} diff --git a/lib/routes/mastodon/account-id.ts b/lib/routes/mastodon/account-id.ts index b35347022..bd5539188 100644 --- a/lib/routes/mastodon/account-id.ts +++ b/lib/routes/mastodon/account-id.ts @@ -1,7 +1,15 @@ +import { Route } from '@/types'; import utils from './utils'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/account_id/:site/:account_id/statuses/:only_media?', + name: 'Unknown', + maintainers: ['notofoe'], + handler, +}; + +async function handler(ctx) { const site = ctx.req.param('site'); const account_id = ctx.req.param('account_id'); const only_media = ctx.req.param('only_media') ? 'true' : 'false'; @@ -11,11 +19,11 @@ export default async (ctx) => { const { account_data, data } = await utils.getAccountStatuses(site, account_id, only_media); - ctx.set('data', { + return { title: `${account_data.display_name} (@${account_data.acct})`, link: account_data.url, description: account_data.note, item: utils.parseStatuses(data), allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/mastodon/acct.ts b/lib/routes/mastodon/acct.ts index 666abc9cd..e2e8da82e 100644 --- a/lib/routes/mastodon/acct.ts +++ b/lib/routes/mastodon/acct.ts @@ -1,6 +1,25 @@ +import { Route } from '@/types'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/acct/:acct/statuses/:only_media?', + categories: ['new-media'], + example: '/mastodon/acct/CatWhitney@mastodon.social/statuses', + parameters: { acct: 'Webfinger account URI, like `user@host`', only_media: 'whether only display media content, default to false, any value to true' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'User timeline', + maintainers: ['notofoe'], + handler, +}; + +async function handler(ctx) { const acct = ctx.req.param('acct'); const only_media = ctx.req.param('only_media') ? 'true' : 'false'; @@ -8,11 +27,11 @@ export default async (ctx) => { const { account_data, data } = await utils.getAccountStatuses(site, account_id, only_media); - ctx.set('data', { + return { title: `${account_data.display_name} (@${account_data.acct})`, link: account_data.url, description: account_data.note, item: utils.parseStatuses(data), allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/mastodon/timeline-local.ts b/lib/routes/mastodon/timeline-local.ts index eb04ca197..43c2efad4 100644 --- a/lib/routes/mastodon/timeline-local.ts +++ b/lib/routes/mastodon/timeline-local.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import utils from './utils'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/timeline/:site/:only_media?', + categories: ['new-media'], + example: '/mastodon/timeline/pawoo.net/true', + parameters: { site: 'instance address, only domain, no `http://` or `https://` protocol header', only_media: 'whether only display media content, default to false, any value to true' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Instance timeline (local)', + maintainers: ['hoilc'], + handler, +}; + +async function handler(ctx) { const site = ctx.req.param('site'); const only_media = ctx.req.param('only_media') ? 'true' : 'false'; if (!config.feature.allow_user_supply_unsafe_domain && !utils.allowSiteList.includes(site)) { @@ -14,9 +33,9 @@ export default async (ctx) => { const response = await got.get(url, { headers: utils.apiHeaders() }); const list = response.data; - ctx.set('data', { + return { title: `Local Public${ctx.req.param('only_media') ? ' Media' : ''} Timeline on ${site}`, link: `https://${site}`, item: utils.parseStatuses(list), - }); -}; + }; +} diff --git a/lib/routes/mastodon/timeline-remote.ts b/lib/routes/mastodon/timeline-remote.ts index 03acc54cb..ddf99c2d3 100644 --- a/lib/routes/mastodon/timeline-remote.ts +++ b/lib/routes/mastodon/timeline-remote.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import utils from './utils'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/remote/:site/:only_media?', + categories: ['new-media'], + example: '/mastodon/remote/pawoo.net/true', + parameters: { site: 'instance address, only domain, no `http://` or `https://` protocol header', only_media: 'whether only display media content, default to false, any value to true' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Instance timeline (federated)', + maintainers: ['hoilc'], + handler, +}; + +async function handler(ctx) { const site = ctx.req.param('site'); const only_media = ctx.req.param('only_media') ? 'true' : 'false'; if (!config.feature.allow_user_supply_unsafe_domain && !utils.allowSiteList.includes(site)) { @@ -14,9 +33,9 @@ export default async (ctx) => { const response = await got.get(url, { headers: utils.apiHeaders() }); const list = response.data; - ctx.set('data', { + return { title: `Federated Public${ctx.req.param('only_media') ? ' Media' : ''} Timeline on ${site}`, link: `https://${site}`, item: utils.parseStatuses(list), - }); -}; + }; +} diff --git a/lib/routes/mckinsey/cn/index.ts b/lib/routes/mckinsey/cn/index.ts index 2308abf13..7dd1c9caf 100644 --- a/lib/routes/mckinsey/cn/index.ts +++ b/lib/routes/mckinsey/cn/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; // use stock `got` package as a workaround for // https://github.com/DIYgod/RSSHub/issues/8239 // https://github.com/DIYgod/RSSHub/pull/8288 @@ -8,7 +9,25 @@ import { categories } from './category-map'; const baseUrl = 'https://www.mckinsey.com.cn'; const endpoint = `${baseUrl}/wp-json`; -export default async (ctx) => { +export const route: Route = { + path: '/cn/:category?', + categories: ['other'], + example: '/mckinsey/cn', + parameters: { category: '分类,见下表,默认为全部' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '洞见', + maintainers: ['laampui'], + handler, +}; + +async function handler(ctx) { const { category = '25' } = ctx.req.param(); if (isNaN(category)) { categories.find((c) => c.slug === category); @@ -29,9 +48,9 @@ export default async (ctx) => { pubDate: parseDate(item.date_gmt), })); - ctx.set('data', { + return { title: category ? `McKinsey Greater China - ${categories[category].name}` : `McKinsey Greater China`, link: `${baseUrl}/${category === '25' ? categories[category].slug : `${categories[25].slug}/${categories[category].slug}`}/`, item: items, - }); -}; + }; +} diff --git a/lib/routes/mdpi/journal.ts b/lib/routes/mdpi/journal.ts index e137b916a..b576ee912 100644 --- a/lib/routes/mdpi/journal.ts +++ b/lib/routes/mdpi/journal.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 { art } from '@/utils/render'; import { CookieJar } from 'tough-cookie'; const cookieJar = new CookieJar(); -export default async (ctx) => { +export const route: Route = { + path: '/:journal', + categories: ['finance'], + example: '/mdpi/analytica', + parameters: { journal: 'Journal Name, get it from the journal homepage' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.mdpi.com/journal/:journal'], + }, + name: 'Journal', + maintainers: ['Derekmini'], + handler, +}; + +async function handler(ctx) { const journal = ctx.req.param('journal'); const host = 'https://www.mdpi.com'; const jrnlUrl = `${host}/journal/${journal}`; @@ -60,9 +82,9 @@ export default async (ctx) => { return item; }); - ctx.set('data', { + return { title: jrnlName, link: jrnlUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/medieval-china/post.ts b/lib/routes/medieval-china/post.ts index 7b23bc66c..099d869b5 100644 --- a/lib/routes/medieval-china/post.ts +++ b/lib/routes/medieval-china/post.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: ['medieval-china.club/'], + target: '', + }, + name: 'Unknown', + maintainers: ['artefaritaKuniklo'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://medieval-china.club'; const { data: response } = await got(baseUrl); const $ = load(response); @@ -34,12 +46,12 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '中国的中古', link: baseUrl, item: items, image: 'https://medieval-china.club/images/icons/favicon-144x144.png', description: '世界那么大,你无法去到每一个地方,感受每一处风景;时间那么长,那些逝去的人你也终将无法与之谋面。而通过古人之文字,今人之分享,你可以领略以前风光之奇绝瑰玮,感受逝人之人情冷暖。中古就是这么一个地方,大家来自全球各地,不同时区,不同性别,不同身份,不同职业,但是大家都被中古的绚烂华章聚集在一起,哀其所哀,乐其所乐。这是一个虚拟的世界,但是我们仿佛跨越千里而来,谈一场绝世爱恋,今夕何夕!仅以此网站献给中古club的每一位成员,契阔谈宴,西园不芜!', - }); -}; + }; +} diff --git a/lib/routes/medium/following.ts b/lib/routes/medium/following.ts index 5690e5f2d..8915255ea 100644 --- a/lib/routes/medium/following.ts +++ b/lib/routes/medium/following.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import { config } from '@/config'; import parseArticle from './parse-article.js'; import { getFollowingFeedQuery } from './graphql.js'; -export default async (ctx) => { +export const route: Route = { + path: '/following/:user', + categories: ['programming'], + example: '/medium/following/imsingee', + parameters: { user: 'Username' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Personalized Recommendations - Following', + maintainers: ['ImSingee'], + handler, +}; + +async function handler(ctx) { const user = ctx.req.param('user'); const cookie = config.medium.cookies[user]; @@ -23,9 +42,9 @@ export default async (ctx) => { const parsedArticles = await Promise.all(urls.map((url) => parseArticle(ctx, url))); - ctx.set('data', { + return { title: `${user} Medium Following`, link: 'https://medium.com/?feed=following', item: parsedArticles, - }); -}; + }; +} diff --git a/lib/routes/medium/for-you.ts b/lib/routes/medium/for-you.ts index cde83f367..3d30b93f7 100644 --- a/lib/routes/medium/for-you.ts +++ b/lib/routes/medium/for-you.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import { config } from '@/config'; import parseArticle from './parse-article.js'; import { getWebInlineRecommendedFeedQuery } from './graphql.js'; -export default async (ctx) => { +export const route: Route = { + path: '/for-you/:user', + categories: ['programming'], + example: '/medium/for-you/imsingee', + parameters: { user: 'Username' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Personalized Recommendations - For You', + maintainers: ['ImSingee'], + handler, +}; + +async function handler(ctx) { const user = ctx.req.param('user'); const cookie = config.medium.cookies[user]; @@ -23,9 +42,9 @@ export default async (ctx) => { const parsedArticles = await Promise.all(urls.map((url) => parseArticle(ctx, url))); - ctx.set('data', { + return { title: `${user} Medium For You`, link: 'https://medium.com/', item: parsedArticles, - }); -}; + }; +} diff --git a/lib/routes/medium/list.ts b/lib/routes/medium/list.ts index ee630b765..33e3b774d 100644 --- a/lib/routes/medium/list.ts +++ b/lib/routes/medium/list.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import { config } from '@/config'; import parseArticle from './parse-article.js'; import { getUserCatalogMainContentQuery } from './graphql.js'; -export default async (ctx) => { +export const route: Route = { + path: '/list/:user/:catalogId', + categories: ['programming'], + example: '/medium/list/imsingee/f2d8d48096a9', + parameters: { user: 'Username', catalogId: 'List ID' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'List', + maintainers: ['ImSingee'], + handler, +}; + +async function handler(ctx) { const user = ctx.req.param('user'); const catalogId = ctx.req.param('catalogId'); @@ -24,9 +43,9 @@ export default async (ctx) => { const parsedArticles = await Promise.all(urls.map((url) => parseArticle(ctx, url))); - ctx.set('data', { + return { title: `List: ${name}`, link: `https://medium.com/@${user}/list/${catalogId}`, item: parsedArticles, - }); -}; + }; +} diff --git a/lib/routes/medium/tag.ts b/lib/routes/medium/tag.ts index 4cfe293e1..425e9b405 100644 --- a/lib/routes/medium/tag.ts +++ b/lib/routes/medium/tag.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import { config } from '@/config'; import parseArticle from './parse-article.js'; import { getWebInlineTopicFeedQuery } from './graphql.js'; -export default async (ctx) => { +export const route: Route = { + path: '/tag/:user/:tag', + categories: ['programming'], + example: '/medium/tag/imsingee/cybersecurity', + parameters: { user: 'Username', tag: 'Subscribed Tag' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Personalized Recommendations - Tag', + maintainers: ['ImSingee'], + handler, +}; + +async function handler(ctx) { const user = ctx.req.param('user'); const tag = ctx.req.param('tag'); @@ -24,9 +43,9 @@ export default async (ctx) => { const parsedArticles = await Promise.all(urls.map((url) => parseArticle(ctx, url))); - ctx.set('data', { + return { title: `${user} Medium Following Tag ${tag}`, link: `https://medium.com/?tag=${tag}`, item: parsedArticles, - }); -}; + }; +} diff --git a/lib/routes/medsci/index.ts b/lib/routes/medsci/index.ts index 719fe8e08..65d58dc25 100644 --- a/lib/routes/medsci/index.ts +++ b/lib/routes/medsci/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, parseRelativeDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/:sid?/:tid?', + categories: ['traditional-media'], + example: '/medsci', + parameters: { sid: '科室,见下表,默认为推荐', tid: '亚专业,可在对应科室页 URL 中找到,默认为该科室的全部' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '资讯', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { let sid = ctx.req.param('sid') ?? ''; const tid = ctx.req.param('tid') ?? ''; @@ -60,9 +79,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${sid ? $('.department-header-active').text() : '推荐'} -${tid ? ` ${$('.department-keywords-ul .active').text()} -` : ''} MedSci.cn`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/metacritic/index.ts b/lib/routes/metacritic/index.ts index 636598ff9..6dcac3344 100644 --- a/lib/routes/metacritic/index.ts +++ b/lib/routes/metacritic/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 * as path from 'node:path'; import { sorts, types } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/:type?/:sort?/:filter?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { type = 'game', sort = 'new', filter } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 50; @@ -108,7 +116,7 @@ export default async (ctx) => { const icon = new URL($('meta[data-hid="msapplication-task-metacritic"]').prop('content').split('icon-uri=').pop(), rootUrl).href; - ctx.set('data', { + return { item: items, title: $('title').text(), link: currentUrl, @@ -120,5 +128,5 @@ export default async (ctx) => { subtitle: $('meta[name="msapplication-tooltip"]').prop('content'), author: $('meta[name="twitter:site"]').prop('content'), allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/metacritic/release.ts b/lib/routes/metacritic/release.ts index e4c9d54ec..7f4b0170f 100644 --- a/lib/routes/metacritic/release.ts +++ b/lib/routes/metacritic/release.ts @@ -41,7 +41,7 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: toTitleCase(`Metacritic ${ctx.req.param('platform')} games ${title}`), link: url, item: result.map((item) => ({ @@ -49,5 +49,5 @@ export default async (ctx) => { description: `Release Date: ${item.date}
    Metacritic Score: ${item.metascore}
    User Score: ${item.userscore}
    `, link: item.url, })), - }); + }; }; diff --git a/lib/routes/meteor/boards.ts b/lib/routes/meteor/boards.ts index 781151708..14767e84f 100644 --- a/lib/routes/meteor/boards.ts +++ b/lib/routes/meteor/boards.ts @@ -1,12 +1,34 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { baseUrl, getBoards } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/boards', + categories: ['blog'], + example: '/meteor/boards', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['meteor.today/'], + }, + name: '看板列表', + maintainers: ['TonyRL'], + handler, +}; + +async function handler() { const items = await getBoards(cache.tryGet); - ctx.set('data', { + return { title: '看板列表', link: `${baseUrl}/board/all`, item: items, - }); -}; + }; +} diff --git a/lib/routes/meteor/index.ts b/lib/routes/meteor/index.ts index b09b2c63e..b8777a6f4 100644 --- a/lib/routes/meteor/index.ts +++ b/lib/routes/meteor/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 { baseUrl, getBoards, renderDesc } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/:board?', + categories: ['blog'], + example: '/meteor/all', + parameters: { board: '看板 ID 或簡稱,可在 URL 或下方路由找到,預設為 `all`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '看板', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { let { board = 'all' } = ctx.req.param(); const boards = await getBoards(cache.tryGet); @@ -36,11 +55,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${board === 'all' ? '全部看板' : boardInfo.title} | Meteor 學生社群`, description: board === 'all' ? null : boardInfo.feedDescription, image: board === 'all' ? null : boardInfo.imgUrl === 'not_set' ? null : boardInfo.imgUrl, link: `${board === 'all' ? `${baseUrl}/board/all` : boardInfo.link}/new`, item: items, - }); -}; + }; +} diff --git a/lib/routes/metmuseum/exhibitions.ts b/lib/routes/metmuseum/exhibitions.ts index 84d35e380..e2d2594ee 100644 --- a/lib/routes/metmuseum/exhibitions.ts +++ b/lib/routes/metmuseum/exhibitions.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; @@ -11,7 +12,14 @@ function generateExhibitionItem(result) { }; } -export default async (ctx) => { +export const route: Route = { + path: '/exhibitions/:state?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const searchType = ctx.req.param('state') ?? 'current'; const url = `https://www.metmuseum.org/ghidorah/ExhibitionListing/Search?searchType=${searchType}`; @@ -23,9 +31,9 @@ export default async (ctx) => { const data = response.data.data; - ctx.set('data', { + return { title: 'The Metropolitan Museum of Art - Exhibitions', link: 'https://www.metmuseum.org/exhibitions', item: data.results.map((element) => generateExhibitionItem(element)), - }); -}; + }; +} diff --git a/lib/routes/microsoft/addon.ts b/lib/routes/microsoft/addon.ts index 896af34a1..8089d968c 100644 --- a/lib/routes/microsoft/addon.ts +++ b/lib/routes/microsoft/addon.ts @@ -1,6 +1,28 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/edge/addon/:crxid', + categories: ['university'], + example: '/microsoft/edge/addon/gangkeiaobmjcjokiofpkfpcobpbmnln', + parameters: { crxid: 'Addon id, can be found in addon url' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['microsoftedge.microsoft.com/addons/detail/:name/:crxid'], + }, + name: 'Addons Update', + maintainers: ['hoilc', 'DIYgod'], + handler, +}; + +async function handler(ctx) { const crxid = ctx.req.param('crxid'); const page_url = `https://microsoftedge.microsoft.com/addons/detail/${crxid}`; @@ -13,7 +35,7 @@ export default async (ctx) => { }, }); - ctx.set('data', { + return { title: `${data.name} - Microsoft Edge Addons`, description: data.shortDescription, image: `https:${data.thumbnail}`, @@ -28,5 +50,5 @@ export default async (ctx) => { link: page_url, }, ], - }); -}; + }; +} diff --git a/lib/routes/mihoyo/bbs/follow-list.ts b/lib/routes/mihoyo/bbs/follow-list.ts index 39abe3b3e..8eb503321 100644 --- a/lib/routes/mihoyo/bbs/follow-list.ts +++ b/lib/routes/mihoyo/bbs/follow-list.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 cache from './cache'; const renderDescription = (description, images) => art(path.join(__dirname, '../templates/description.art'), { description, images }); -export default async (ctx) => { +export const route: Route = { + path: '/bbs/follow-list/:uid', + categories: ['reading'], + example: '/mihoyo/bbs/follow-list/77005350', + parameters: { uid: '用户uid' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '米游社 - 用户关注', + maintainers: ['CaoMeiYouRen'], + handler, +}; + +async function handler(ctx) { const uid = ctx.req.param('uid'); const page_size = ctx.req.query('limit') || '20'; const searchParams = { @@ -50,4 +69,4 @@ export default async (ctx) => { item: items, }; ctx.set('data', data); -}; +} diff --git a/lib/routes/mihoyo/bbs/img-ranking.ts b/lib/routes/mihoyo/bbs/img-ranking.ts index 5e19ece2b..67e821730 100644 --- a/lib/routes/mihoyo/bbs/img-ranking.ts +++ b/lib/routes/mihoyo/bbs/img-ranking.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { DATA_MAP, RANKING_TYPE_MAP } from './static-data'; import { post2item } from './utils'; @@ -38,7 +39,29 @@ const getRankingTypeInfo = (game, ranking_type) => { }; }; -export default async (ctx) => { +export const route: Route = { + path: '/bbs/img-ranking/:game/:routeParams?', + categories: ['reading'], + example: '/mihoyo/bbs/img-ranking/ys/forumType=tongren&cateType=illustration&rankingType=daily', + parameters: { game: '游戏缩写', routeParams: '额外参数;请参阅以下说明和表格' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['miyoushe.com/:game/imgRanking/:forum_id/:ranking_id/:cate_id'], + target: '/bbs/img-ranking/:game', + }, + name: '米游社 - 同人榜', + maintainers: ['CaoMeiYouRen'], + handler, +}; + +async function handler(ctx) { const game = ctx.req.param('game'); const routeParams = Object.fromEntries(new URLSearchParams(ctx.req.param('routeParams'))); const { forumType: forum_type = 'tongren', cateType: cate_type, rankingType: ranking_type, lastId: last_id = '' } = routeParams; @@ -78,4 +101,4 @@ export default async (ctx) => { item: items, }; ctx.set('data', data); -}; +} diff --git a/lib/routes/mihoyo/bbs/official.ts b/lib/routes/mihoyo/bbs/official.ts index f4c62408e..ea48d2de3 100644 --- a/lib/routes/mihoyo/bbs/official.ts +++ b/lib/routes/mihoyo/bbs/official.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { post2item } from './utils'; // 游戏id @@ -17,7 +18,25 @@ const TYPE_MAP = { 3: '资讯', }; -export default async (ctx) => { +export const route: Route = { + path: '/bbs/official/:gids/:type?/:page_size?/:last_id?', + categories: ['reading'], + example: '/mihoyo/bbs/official/2/3/20/', + parameters: { gids: '游戏id', type: '公告类型,默认为 2(即 活动)', page_size: '分页大小,默认为 20 ', last_id: '跳过的公告数,例如指定为 40 就是从第 40 条公告开始,可用于分页' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '米游社 - 官方公告', + maintainers: ['CaoMeiYouRen'], + handler, +}; + +async function handler(ctx) { const { gids, type = '2', page_size = '20', last_id = '' } = ctx.req.param(); const query = new URLSearchParams({ gids, @@ -43,4 +62,4 @@ export default async (ctx) => { }; ctx.set('data', data); -}; +} diff --git a/lib/routes/mihoyo/bbs/timeline.ts b/lib/routes/mihoyo/bbs/timeline.ts index c66c5c9db..7746fe37c 100644 --- a/lib/routes/mihoyo/bbs/timeline.ts +++ b/lib/routes/mihoyo/bbs/timeline.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import cache from './cache'; import { config } from '@/config'; import { post2item } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/bbs/timeline', + categories: ['reading'], + example: '/mihoyo/bbs/timeline', + parameters: {}, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['miyoushe.com/:game/timeline'], + }, + name: '米游社 - 用户关注动态', + maintainers: ['CaoMeiYouRen'], + handler, +}; + +async function handler(ctx) { if (!config.mihoyo.cookie) { throw new Error('Miyoushe Timeline is not available due to the absense of [Miyoushe Cookie]. Check relevant config tutorial'); } @@ -38,4 +60,4 @@ export default async (ctx) => { item: items, }; ctx.set('data', data); -}; +} diff --git a/lib/routes/mihoyo/bbs/user-post.ts b/lib/routes/mihoyo/bbs/user-post.ts index 7e394bb6e..b485defa5 100644 --- a/lib/routes/mihoyo/bbs/user-post.ts +++ b/lib/routes/mihoyo/bbs/user-post.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { post2item } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/bbs/user-post/:uid', + categories: ['reading'], + example: '/mihoyo/bbs/user-post/77005350', + parameters: { uid: '用户uid' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '米游社 - 用户帖子', + maintainers: ['CaoMeiYouRen'], + handler, +}; + +async function handler(ctx) { const uid = ctx.req.param('uid'); const size = ctx.req.query('limit') || '20'; const searchParams = { @@ -29,4 +48,4 @@ export default async (ctx) => { item: items, }; ctx.set('data', data); -}; +} diff --git a/lib/routes/mihoyo/sr/news.ts b/lib/routes/mihoyo/sr/news.ts index 41ab69ffc..79d011953 100644 --- a/lib/routes/mihoyo/sr/news.ts +++ b/lib/routes/mihoyo/sr/news.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; @@ -42,7 +43,29 @@ const categories = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/sr/:location?/:category?', + categories: ['reading'], + example: '/mihoyo/sr', + parameters: { location: '区域,可选 `zh-cn`(国服,简中)或 `zh-tw`(国际服,繁中)', category: '分类,见下表,默认为最新' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['sr.mihoyo.com/news'], + target: '/sr', + }, + name: '崩坏:星穹铁道', + maintainers: ['shinanory'], + handler, +}; + +async function handler(ctx) { // location 地区 category 类型 const { location = 'zh-cn', category = 'news-all' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 50; @@ -61,9 +84,9 @@ export default async (ctx) => { category: item.sCategoryName, })); - ctx.set('data', { + return { title: `${categories[location][category].title}-崩坏:星穹铁道`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/mihoyo/ys/news.ts b/lib/routes/mihoyo/ys/news.ts index 861595d57..bd0539695 100644 --- a/lib/routes/mihoyo/ys/news.ts +++ b/lib/routes/mihoyo/ys/news.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -79,7 +80,29 @@ const apiUrls = { 'zh-tw': '/content_v2_user/app/a1b1f9d3315447cc/getContentList', }; -export default async (ctx) => { +export const route: Route = { + path: '/ys/:location?/:category?', + categories: ['reading'], + example: '/mihoyo/ys', + parameters: { location: '区域,可选 `main`(简中)或 `zh-tw`(繁中)', category: '分类,见下表,默认为最新' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['genshin.hoyoverse.com/:location/news'], + target: '/ys/:location', + }, + name: '原神', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const location = ctx.req.param('location') ?? 'main'; const category = ctx.req.param('category') ?? 'latest'; const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 50; @@ -157,9 +180,9 @@ export default async (ctx) => { ); } - ctx.set('data', { + return { title: `原神 - ${categories[category][location].title}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/mindmeister/example.ts b/lib/routes/mindmeister/example.ts index ff10677e1..08dc2a12a 100644 --- a/lib/routes/mindmeister/example.ts +++ b/lib/routes/mindmeister/example.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'; const baseUrl = 'https://www.mindmeister.com'; -export default async (ctx) => { +export const route: Route = { + path: '/:category?/:language?', + categories: ['journal'], + example: '/mindmeister/mind-map-examples', + parameters: { category: 'Categories, see the table below, `mind-map-examples` by default', language: 'Languages, see the table below, `en` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Public Maps', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { category = 'mind-map-examples', language = 'en' } = ctx.req.param(); const link = `${baseUrl}${language === 'en' || language === 'other' ? '' : `/${language}`}/${category === 'mind-map-examples' ? category : `mind-maps/${category}?language=${language}`}`; const respsonse = await got(link); @@ -37,11 +56,11 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('head title').text(), description: $('head meta[name=description]').text(), link, item: items, language, - }); -}; + }; +} diff --git a/lib/routes/minecraft/version.ts b/lib/routes/minecraft/version.ts index 10a717a5d..2574a2f6f 100644 --- a/lib/routes/minecraft/version.ts +++ b/lib/routes/minecraft/version.ts @@ -1,6 +1,28 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/version', + categories: ['reading'], + example: '/minecraft/version', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['minecraft.net/'], + }, + name: 'Java Game Update', + maintainers: ['TheresaQWQ'], + handler, +}; + +async function handler() { const url = 'https://launchermeta.mojang.com/mc/game/version_manifest.json'; const response = await got({ @@ -19,7 +41,7 @@ export default async (ctx) => { const title = `Minecraft Java版游戏更新`; - ctx.set('data', { + return { title, link: `https://www.minecraft.net/`, description: title, @@ -30,5 +52,5 @@ export default async (ctx) => { link: `https://www.minecraft.net`, guid: item.id + item.type, })), - }); -}; + }; +} diff --git a/lib/routes/mingpao/index.ts b/lib/routes/mingpao/index.ts index 3d7924514..f24b9b82f 100644 --- a/lib/routes/mingpao/index.ts +++ b/lib/routes/mingpao/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -21,7 +22,14 @@ const renderDesc = (media, desc) => desc, }); -export default async (ctx) => { +export const route: Route = { + path: '/:type?/:category?', + name: 'Unknown', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') ?? 'ins'; const category = ctx.req.param('category') ?? (type === 'ins' ? 'all' : 's00001'); const link = `https://news.mingpao.com/rss/${type}/${category}.xml`; @@ -92,12 +100,12 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: feed.title, link: feed.link, description: feed.description, item: items, image: feed.image.url, language: feed.language, - }); -}; + }; +} diff --git a/lib/routes/mirror/index.ts b/lib/routes/mirror/index.ts index 0e36a74b5..23b208d33 100644 --- a/lib/routes/mirror/index.ts +++ b/lib/routes/mirror/index.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'; @@ -7,7 +8,25 @@ const md = MarkdownIt({ }); import { isValidHost } from '@/utils/valid-host'; -export default async (ctx) => { +export const route: Route = { + path: '/:id', + categories: ['traditional-media'], + example: '/mirror/tingfei.eth', + parameters: { id: 'user id' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'User', + maintainers: ['fifteen42', 'rde9', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); if (!id.endsWith('.eth') && !isValidHost(id)) { throw new Error('Invalid id'); @@ -32,11 +51,11 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `${data.props.pageProps.publicationLayoutProject.displayName} - Mirror`, description: data.props.pageProps.publicationLayoutProject.description, image: data.props.pageProps.publicationLayoutProject.avatarURL, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/missav/new.ts b/lib/routes/missav/new.ts index 48a579d6e..a9ab73508 100644 --- a/lib/routes/missav/new.ts +++ b/lib/routes/missav/new.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 { load } from 'cheerio'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/new', + categories: ['picture'], + example: '/missav/new', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['missav.com/dm397/new', 'missav.com/new', 'missav.com/'], + }, + name: '最近更新', + maintainers: ['TonyRL'], + handler, +}; + +async function handler() { const baseUrl = 'https://missav.com'; const { data: response } = await got(`${baseUrl}/dm397/new`); const $ = load(response); @@ -30,10 +52,10 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('head title').text(), description: $('head meta[name="description"]').attr('content'), link: baseUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/misskey/featured-notes.ts b/lib/routes/misskey/featured-notes.ts index 4a6bb1d2c..6dc707fce 100644 --- a/lib/routes/misskey/featured-notes.ts +++ b/lib/routes/misskey/featured-notes.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import utils from './utils'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/notes/featured/:site', + categories: ['new-media'], + example: '/misskey/notes/featured/misskey.io', + parameters: { site: 'instance address, domain only, without `http://` or `https://` protocol header' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Featured Notes', + maintainers: ['Misaka13514'], + handler, +}; + +async function handler(ctx) { const site = ctx.req.param('site'); if (!config.feature.allow_user_supply_unsafe_domain && !utils.allowSiteList.includes(site)) { throw new Error(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`); @@ -21,9 +40,9 @@ export default async (ctx) => { const list = response.data; - ctx.set('data', { + return { title: `Featured Notes on ${site}`, link: `https://${site}/explore`, item: utils.parseNotes(list, site), - }); -}; + }; +} diff --git a/lib/routes/mittrchina/index.ts b/lib/routes/mittrchina/index.ts index 464c41cff..11939f467 100644 --- a/lib/routes/mittrchina/index.ts +++ b/lib/routes/mittrchina/index.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 { 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: '/:type?', + categories: ['traditional-media'], + example: '/mittrchina/index', + parameters: { type: '类型,见下表,默认为首页资讯' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '首页', + maintainers: ['EsuRt', 'queensferryme'], + handler, +}; + +async function handler(ctx) { const typeMap = { breaking: { title: '快讯', @@ -95,9 +114,9 @@ export default async (ctx) => { ); } - ctx.set('data', { + return { title: `MIT 科技评论 - ${typeMap[type].title}`, link: `https://www.mittrchina.com/${type}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/mixcloud/index.ts b/lib/routes/mixcloud/index.ts index 03c81af6a..ac07c29c7 100644 --- a/lib/routes/mixcloud/index.ts +++ b/lib/routes/mixcloud/index.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import CryptoJS from 'crypto-js'; import { parseDate } from '@/utils/parse-date'; import { queries } from './queries'; -export default async (ctx) => { +export const route: Route = { + path: '/:username/:type?', + categories: ['picture'], + example: '/mixcloud/dholbach/uploads', + parameters: { username: 'Username, can be found in URL', type: 'Type, see below, uploads by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: true, + supportScihub: false, + }, + name: 'User', + maintainers: ['Misaka13514'], + handler, +}; + +async function handler(ctx) { const host = 'https://www.mixcloud.com'; const imageBaseURL = 'https://thumbnailer.mixcloud.com/unsafe/480x480/'; const graphqlURL = 'https://app.mixcloud.com/graphql'; @@ -102,12 +121,12 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `Mixcloud - ${data.user.displayName}'s ${config[type].name}`, description: biog.replaceAll('\n', '
    '), itunes_author: data.user.displayName, image, link: `${host}/${username}/${type}/`, item: items, - }); -}; + }; +} diff --git a/lib/routes/modb/topic.ts b/lib/routes/modb/topic.ts index 1cc16a91f..4acc36fbd 100644 --- a/lib/routes/modb/topic.ts +++ b/lib/routes/modb/topic.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { load } from 'cheerio'; import got from '@/utils/got'; @@ -5,7 +6,25 @@ import logger from '@/utils/logger'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/topic/:id', + categories: ['design'], + example: '/modb/topic/44158', + parameters: { id: '合辑序号' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '合辑', + maintainers: ['yueneiqi'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://www.modb.pro'; const topicId = ctx.req.param('id'); const response = await got({ @@ -52,9 +71,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '墨天轮合辑', link: `${baseUrl}/topic/${topicId}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/modelscope/community.ts b/lib/routes/modelscope/community.ts index 02c271cf6..b9c0b622e 100644 --- a/lib/routes/modelscope/community.ts +++ b/lib/routes/modelscope/community.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 { art } from '@/utils/render'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/community', + categories: ['design'], + example: '/modelscope/community', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['community.modelscope.cn/'], + }, + name: 'DevPress 官方社区', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://community.modelscope.cn'; const { data } = await got.post(`${baseUrl}/v1/namespace_page/article`, { @@ -49,11 +71,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'ModelScope魔搭社区-DevPress官方社区', description: 'ModelScope魔搭社区 DevPress官方社区-ModelScope旨在打造下一代开源的模型即服务共享平台,为泛AI开发者提供灵活、易用、低成本的一站式模型服务产品,让模型应用更简单。', image: 'https://g.alicdn.com/sail-web/maas/0.8.10/favicon/128.ico', link: baseUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/modelscope/datasets.ts b/lib/routes/modelscope/datasets.ts index d61ef74ac..f3d71d745 100644 --- a/lib/routes/modelscope/datasets.ts +++ b/lib/routes/modelscope/datasets.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -12,7 +13,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: '/datasets', + categories: ['design'], + example: '/modelscope/datasets', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['modelscope.cn/datasets'], + }, + name: '数据集', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://modelscope.cn'; const link = `${baseUrl}/datasets`; @@ -50,11 +72,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '数据集首页 · 魔搭社区', description: 'ModelScope——汇聚各领域先进的机器学习模型,提供模型探索体验、推理、训练、部署和应用的一站式服务。在这里,共建模型开源社区,发现、学习、定制和分享心仪的模型。', image: 'https://g.alicdn.com/sail-web/maas/0.8.10/favicon/128.ico', link, item: items, - }); -}; + }; +} diff --git a/lib/routes/modelscope/models.ts b/lib/routes/modelscope/models.ts index 46f58ad10..f44c29994 100644 --- a/lib/routes/modelscope/models.ts +++ b/lib/routes/modelscope/models.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import MarkdownIt from 'markdown-it'; @@ -7,7 +8,28 @@ const md = MarkdownIt({ }); import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/models', + categories: ['design'], + example: '/modelscope/models', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['modelscope.cn/models'], + }, + name: '模型库', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://modelscope.cn'; const link = `${baseUrl}/models`; @@ -37,11 +59,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '模型库首页 · 魔搭社区', description: 'ModelScope——汇聚各领域先进的机器学习模型,提供模型探索体验、推理、训练、部署和应用的一站式服务。在这里,共建模型开源社区,发现、学习、定制和分享心仪的模型。', image: 'https://g.alicdn.com/sail-web/maas/0.8.10/favicon/128.ico', link, item: items, - }); -}; + }; +} diff --git a/lib/routes/modelscope/studios.ts b/lib/routes/modelscope/studios.ts index a2cdb823e..1e6de65ed 100644 --- a/lib/routes/modelscope/studios.ts +++ b/lib/routes/modelscope/studios.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -12,7 +13,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: '/studios', + categories: ['design'], + example: '/modelscope/studios', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['modelscope.cn/studios'], + }, + name: '创空间', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://modelscope.cn'; const link = `${baseUrl}/studios`; @@ -52,11 +74,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '创空间首页 · 魔搭社区', description: 'ModelScope——汇聚各领域先进的机器学习模型,提供模型探索体验、推理、训练、部署和应用的一站式服务。在这里,共建模型开源社区,发现、学习、定制和分享心仪的模型。', image: 'https://g.alicdn.com/sail-web/maas/0.8.10/favicon/128.ico', link, item: items, - }); -}; + }; +} diff --git a/lib/routes/modrinth/versions.ts b/lib/routes/modrinth/versions.ts index 5c6740f8e..d257e13e0 100644 --- a/lib/routes/modrinth/versions.ts +++ b/lib/routes/modrinth/versions.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -18,10 +19,47 @@ const md = MarkdownIt({ html: true, }); -export default async ( - /** @type {import('koa').Context} */ - ctx -) => { +export const route: Route = { + path: '/models', + categories: ['reading'], + example: '/modrinth/project/sodium/versions', + parameters: { + id: 'Id or slug of the Modrinth project', + routeParams: 'Extra route params. See the table below for options', + }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: [ + 'modrinth.com/mod/:id/*', + 'modrinth.com/plugin/:id/*', + 'modrinth.com/datapack/:id/*', + 'modrinth.com/shader/:id/*', + 'modrinth.com/resourcepack/:id/*', + 'modrinth.com/modpack/:id/*', + 'modrinth.com/mod/:id', + 'modrinth.com/plugin/:id', + 'modrinth.com/datapack/:id', + 'modrinth.com/shader/:id', + 'modrinth.com/resourcepack/:id', + 'modrinth.com/modpack/:id', + ], + target: '/project/:id/versions', + }, + description: + '| Name | Example |\n| -------------- | -------------------------------------------- |\n| loaders | loaders=fabric\\&loaders=quilt\\&loaders=forge |\n| game\\_versions | game\\_versions=1.20.1\\&game\\_versions=1.20.2 |\n| featured | featured=true |', + name: 'Project versions', + maintainers: ['SettingDust'], + handler, +}; + +async function handler(ctx) { const { /** @type string */ id, @@ -55,7 +93,7 @@ export default async ( }, }).json(); - ctx.set('data', { + return { title: `${project.title} Modrinth versions`, description: project.description, link: `https://modrinth.com/project/${id}`, @@ -70,11 +108,11 @@ export default async ( guid: it.id, author: authors[index].name, })), - }); + }; } catch (error) { if (error?.response?.statusCode === 404) { throw new Error(`${error.message}: Project ${id} not found`); } throw error; } -}; +} diff --git a/lib/routes/mohw/clarification.ts b/lib/routes/mohw/clarification.ts index 8ad67fecf..3862d6311 100644 --- a/lib/routes/mohw/clarification.ts +++ b/lib/routes/mohw/clarification.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: '/clarification', + categories: ['study'], + example: '/mohw/clarification', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['mohw.gov.tw/'], + }, + name: '即時新聞澄清', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://www.mohw.gov.tw'; const currentUrl = `${rootUrl}/lp-17-1.html`; @@ -43,9 +65,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '即時新聞澄清 - 台灣衛生福利部', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/mox/index.ts b/lib/routes/mox/index.ts index 3367aab42..1c614da47 100644 --- a/lib/routes/mox/index.ts +++ b/lib/routes/mox/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: ['program-update'], + example: '/mox', + parameters: { category: '分类,可在对应分类页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['mox.moe/l/:category', 'mox.moe/'], + }, + name: '首頁', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? ''; const rootUrl = 'https://mox.moe'; @@ -50,9 +72,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'Mox.moe', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/mpaypass/main.ts b/lib/routes/mpaypass/main.ts index a697868a9..eaf9cf209 100644 --- a/lib/routes/mpaypass/main.ts +++ b/lib/routes/mpaypass/main.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: '/main/:type?', + categories: ['traditional-media'], + example: '/mpaypass/main/policy', + parameters: { type: '新闻类型,类型可在URL中找到,类似`policy`,`eye`等,空或其他任意值展示最新新闻' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Unknown', + maintainers: ['zhuan-zhu'], + handler, +}; + +async function handler(ctx) { const { type = '' } = ctx.req.param(); const title_url = type ? `http://www.mpaypass.com.cn/${type}.html` : 'http://www.mpaypass.com.cn'; @@ -52,9 +71,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `移动支付网-${title_cn}`, link: title_url, item: out, - }); -}; + }; +} diff --git a/lib/routes/mpaypass/news.ts b/lib/routes/mpaypass/news.ts index a3db5318d..c597bdb1b 100644 --- a/lib/routes/mpaypass/news.ts +++ b/lib/routes/mpaypass/news.ts @@ -1,14 +1,36 @@ +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: '/news', + categories: ['traditional-media'], + example: '/mpaypass/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['mpaypass.com.cn/'], + }, + name: '新闻', + maintainers: ['LogicJake', 'genghis-yang'], + handler, +}; + +async function handler() { const link = 'http://m.mpaypass.com.cn'; const listData = await got(link); const $list = load(listData.data); - ctx.set('data', { + return { title: '新闻 - 移动支付网', link, language: 'zh-CN', @@ -36,5 +58,5 @@ export default async (ctx) => { }) .get() ), - }); -}; + }; +} diff --git a/lib/routes/mrdx/daily.ts b/lib/routes/mrdx/daily.ts index 88374a7a6..e59c7b355 100644 --- a/lib/routes/mrdx/daily.ts +++ b/lib/routes/mrdx/daily.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; @@ -8,7 +9,28 @@ dayjs.extend(utc); import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/today', + categories: ['bbs'], + example: '/mrdx/today', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['mrdx.cn*'], + }, + name: '今日', + maintainers: ['Dustin-Jiang'], + handler, +}; + +async function handler() { const dateFormatted = dayjs().utcOffset(8).format('YYYYMMDD'); const baseUrl = `http://mrdx.cn/content/${dateFormatted}`; @@ -70,11 +92,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '新华每日电讯', link, item: passages, description: '中国报纸发行前三强。位列《人民日报》,《参考消息》之后。《新华每日电讯》是新华社出版的一份新闻电讯报,1993年创办,具有很高的权威性和准确性,有“一报在手,便知天下”之美誉。《新华每日电讯》为对开八版日报,集中刊登[新华社]每天向国内播发的电讯稿及图片稿。打开《新华每日电讯》,中国和全世界每天发生的重大事件便将一目了然。在人类生活节奏日益加快的当今世界,《新华每日电讯》以最便捷、最醒目的方式为公众提供最重要的新闻报道,受到公众的喜爱和拥护。', - }); -}; + }; +} diff --git a/lib/routes/mrm/index.ts b/lib/routes/mrm/index.ts index 06bc5620c..9c115c4bd 100644 --- a/lib/routes/mrm/index.ts +++ b/lib/routes/mrm/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: ['other'], + example: '/mrm', + parameters: { category: 'N' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '通知', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { category = 'zonghezixun3' } = ctx.req.param(); const baseUrl = 'http://www.mrm.com.cn'; const link = `${baseUrl}/${category}.html`; @@ -35,9 +54,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), link, item: items, - }); -}; + }; +} diff --git a/lib/routes/mvm/index.ts b/lib/routes/mvm/index.ts index 8889441bb..3b92284fb 100644 --- a/lib/routes/mvm/index.ts +++ b/lib/routes/mvm/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: ['finance'], + example: '/mvm', + parameters: { category: '分类,见下表,默认为本期要目' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['mwm.net.cn/web/:category', 'mwm.net.cn/'], + }, + name: '分类', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'bqym'; const rootUrl = 'http://www.mwm.net.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/mydrivers/cid.ts b/lib/routes/mydrivers/cid.ts index d3c0d3013..b975993a0 100644 --- a/lib/routes/mydrivers/cid.ts +++ b/lib/routes/mydrivers/cid.ts @@ -1,10 +1,18 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { parseDate } from '@/utils/parse-date'; import parser from '@/utils/rss-parser'; import { rootUrl, rootRSSUrl, title, categories, getInfo, processItems } from './util'; -export default async (ctx) => { +export const route: Route = { + path: ['/cid/:id?', '/zhibo'], + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 100; @@ -29,10 +37,10 @@ export default async (ctx) => { items = await processItems(items, cache.tryGet); } - ctx.set('data', { + return { ...(await getInfo(currentUrl, cache.tryGet)), item: items, title: `${title} - ${feed.title.split(/_/).pop() || categories.zhibo}`, - }); -}; + }; +} diff --git a/lib/routes/mydrivers/index.ts b/lib/routes/mydrivers/index.ts index eef2c9021..d9fabae91 100644 --- a/lib/routes/mydrivers/index.ts +++ b/lib/routes/mydrivers/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -11,7 +12,14 @@ import * as path from 'node:path'; import { rootUrl, title, categories, convertToQueryString, getInfo, processItems } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/:category{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { let { category = 'new' } = ctx.req.param(); let newTitle = ''; @@ -53,7 +61,7 @@ export default async (ctx) => { items = await processItems(items, cache.tryGet); - ctx.set('data', { + return { ...(await getInfo(currentUrl, cache.tryGet)), ...Object.fromEntries( Object.entries({ @@ -61,5 +69,5 @@ export default async (ctx) => { title: newTitle, }).filter(([value]) => value) ), - }); -}; + }; +} diff --git a/lib/routes/mydrivers/rank.ts b/lib/routes/mydrivers/rank.ts index a15e646a8..e3ba5804b 100644 --- a/lib/routes/mydrivers/rank.ts +++ b/lib/routes/mydrivers/rank.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, getInfo, processItems } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/rank/:range?', + categories: ['traditional-media'], + example: '/mydrivers/rank', + parameters: { range: '时间范围,见下表,默认为24小时最热' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['m.mydrivers.com/newsclass.aspx'], + target: '/rank', + }, + name: '排行', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const { range = '0' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 10; @@ -34,8 +57,8 @@ export default async (ctx) => { items = await processItems(items, cache.tryGet); - ctx.set('data', { + return { item: items, ...(await getInfo(currentUrl, cache.tryGet, Number.parseInt(range, 10))), - }); -}; + }; +} diff --git a/lib/routes/myfigurecollection/activity.ts b/lib/routes/myfigurecollection/activity.ts index 916053b03..25c2e4053 100644 --- a/lib/routes/myfigurecollection/activity.ts +++ b/lib/routes/myfigurecollection/activity.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -9,7 +10,36 @@ import { art } from '@/utils/render'; import * as path from 'node:path'; import { isValidHost } from '@/utils/valid-host'; -export default async (ctx) => { +export const route: Route = { + path: '/activity/:category?/:language?/:latestAdditions?/:latestEdits?/:latestAlerts?/:latestPictures?', + categories: ['game'], + example: '/myfigurecollection/activity', + parameters: { + category: 'Category, Figures by default', + language: 'Language, as above, `en` by default', + latestAdditions: 'Latest Additions, on as `1` by default, off as `0`', + latestEdits: 'Changes, on as `1` by default, off as `0`', + latestAlerts: 'Alerts, on as `1` by default, off as `0`', + latestPictures: 'Pictures, on as `1` by default, off as `0`', + }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['zh.myfigurecollection.net/browse', 'zh.myfigurecollection.net/'], + target: '/:category?/:language?', + }, + name: 'Activity', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? '-1'; const language = ctx.req.param('language') ?? ''; const latestAdditions = ctx.req.param('latestAdditions') ?? '1'; @@ -56,11 +86,11 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('title') .text() .replace(/ \(.*\)/, ''), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/myfigurecollection/index.ts b/lib/routes/myfigurecollection/index.ts index 207cd0c59..a428c3edd 100644 --- a/lib/routes/myfigurecollection/index.ts +++ b/lib/routes/myfigurecollection/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -14,7 +15,28 @@ const shortcuts = { potm: 'picture/browse/potm/', }; -export default async (ctx) => { +export const route: Route = { + path: '/:category?/:language?', + categories: ['game'], + example: '/myfigurecollection/potd', + parameters: { category: '分类,默认为每日圖片', language: '语言,见上表,默认为空,即 `en`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['zh.myfigurecollection.net/browse', 'zh.myfigurecollection.net/'], + }, + name: '圖片', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const language = ctx.req.param('language') ?? ''; const category = ctx.req.param('category') ?? 'figure'; if (language && !isValidHost(language)) { @@ -76,11 +98,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title') .text() .replace(/ \(.*\)/, ''), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/mygopen/index.ts b/lib/routes/mygopen/index.ts index e892c0f35..b8e93b968 100644 --- a/lib/routes/mygopen/index.ts +++ b/lib/routes/mygopen/index.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: '/:label?', + categories: ['traditional-media'], + example: '/mygopen', + parameters: { label: '分類,见下表,默认为首页' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['mygopen.com/search/label/:label', 'mygopen.com/'], + }, + name: '分類', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const label = ctx.req.param('label') ?? ''; const rootUrl = 'https://www.mygopen.com'; @@ -20,10 +42,10 @@ export default async (ctx) => { link: item.link.pop().href, })); - ctx.set('data', { + return { title: `MyGoPen${label ? `: ${label}` : ''}`, link: currentUrl, item: items, description: '詐騙與謠言頻傳的年代,「MyGoPen|這是假消息」提醒網路使用者隨時要用謹慎懷疑的態度面對網路上的消息。', - }); -}; + }; +} diff --git a/lib/routes/mymusicsheet/usersheets.ts b/lib/routes/mymusicsheet/usersheets.ts index 1d4cafb55..2ffb2fd67 100644 --- a/lib/routes/mymusicsheet/usersheets.ts +++ b/lib/routes/mymusicsheet/usersheets.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -5,7 +6,29 @@ 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: '/user/sheets/:username/:iso?/:freeOnly?', + categories: ['game'], + example: '/mymusicsheet/user/sheets/HalcyonMusic/USD/1', + parameters: { username: '用户名,可在URL中找到', iso: '用于显示价格的ISO 4217货币代码, 支持常见代码, 默认为人民币, 即`CNY`', freeOnly: '只返回免费谱, 任意值为开启' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['mymusicsheet.com:username/*', 'mymusicsheet.com/:username'], + target: '/user/sheets/:username', + }, + name: 'User Sheets', + maintainers: ['Freddd13'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://www.mymusicsheet.com'; const graphqlUrl = 'https://mms.pd.mapia.io/mms/graphql'; const exchangeRateUrl = 'https://payport.pd.mapia.io/v2/currency'; @@ -115,9 +138,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `${username}'s sheets`, link: `https://www.mymusicsheet.com/${username}?viewType=sheet&orderBy=createdAt`, item: items, - }); -}; + }; +} diff --git a/lib/routes/mysql/release.ts b/lib/routes/mysql/release.ts index ac107baaf..e02497183 100644 --- a/lib/routes/mysql/release.ts +++ b/lib/routes/mysql/release.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { config } from '@/config'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/release/:version?', + categories: ['design'], + example: '/mysql/release/8.0', + parameters: { version: 'Version, see below, 8.0 by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Release Notes', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const version = ctx.req.param('version') ?? '8.0'; const rootUrl = 'https://dev.mysql.com'; @@ -55,9 +74,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/nasa/apod-cn.ts b/lib/routes/nasa/apod-cn.ts index 572dacb8c..e484395d3 100644 --- a/lib/routes/nasa/apod-cn.ts +++ b/lib/routes/nasa/apod-cn.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: '/apod-cn', + categories: ['anime'], + example: '/nasa/apod-cn', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['apod.nasa.govundefined'], + }, + name: 'NASA 中文', + maintainers: ['nczitzk', 'williamgateszhao'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 10; const rootUrl = `https://www.nasachina.cn/wp-json/wp/v2/posts?categories=2&per_page=${limit}`; const { data } = await got({ @@ -16,9 +38,9 @@ export default async (ctx) => { link: item.link, })); - ctx.set('data', { + return { title: 'NASA中文 - 天文·每日一图', link: 'https://www.nasachina.cn/nasa-image-of-the-day', item: items, - }); -}; + }; +} diff --git a/lib/routes/nasa/apod-ncku.ts b/lib/routes/nasa/apod-ncku.ts index c363505fd..8e2086c8d 100644 --- a/lib/routes/nasa/apod-ncku.ts +++ b/lib/routes/nasa/apod-ncku.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: '/apod-ncku', + categories: ['anime'], + example: '/nasa/apod-ncku', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['apod.nasa.govundefined'], + }, + name: 'Cheng Kung University Mirror', + maintainers: ['nczitzk', 'williamgateszhao'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 10; const rootUrl = 'http://sprite.phys.ncku.edu.tw/astrolab/mirrors/apod/archivepix.html'; const response = await got({ @@ -44,9 +66,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'NASA 每日一天文圖 (成大物理分站) ', link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/nasa/apod.ts b/lib/routes/nasa/apod.ts index d37488c74..e97640fd3 100644 --- a/lib/routes/nasa/apod.ts +++ b/lib/routes/nasa/apod.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: '/apod', + categories: ['anime'], + example: '/nasa/apod', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['apod.nasa.govundefined'], + }, + name: 'NASA', + maintainers: ['nczitzk', 'williamgateszhao'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 10; const rootUrl = 'https://apod.nasa.gov/apod/archivepix.html'; const response = await got({ @@ -44,9 +66,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'NASA Astronomy Picture of the Day', link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/natgeo/dailyphoto.ts b/lib/routes/natgeo/dailyphoto.ts index aeca1f1f5..64be6924e 100644 --- a/lib/routes/natgeo/dailyphoto.ts +++ b/lib/routes/natgeo/dailyphoto.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 { art } from '@/utils/render'; import * as path from 'node:path'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/dailyphoto', + categories: ['anime'], + example: '/natgeo/dailyphoto', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['nationalgeographic.com/photo-of-the-day/*', 'nationalgeographic.com/'], + }, + name: '每日一图', + maintainers: ['LogicJake', 'OrangeEd1t', 'TonyRL'], + handler, +}; + +async function handler() { const rootUrl = 'https://www.nationalgeographic.com'; const apiUrl = `${rootUrl}/photo-of-the-day`; const response = await cache.tryGet(apiUrl, async () => (await got(apiUrl)).data, config.cache.contentExpire, false); @@ -28,9 +50,9 @@ export default async (ctx) => { author: item.img.crdt, })); - ctx.set('data', { + return { title: 'Nat Geo Photo of the Day', link: apiUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/natgeo/dailyselection.ts b/lib/routes/natgeo/dailyselection.ts index 944b920ea..d7c0e89ff 100644 --- a/lib/routes/natgeo/dailyselection.ts +++ b/lib/routes/natgeo/dailyselection.ts @@ -1,8 +1,16 @@ +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: '/dailyselection', + name: 'Unknown', + maintainers: ['OrangeEd1t'], + handler, +}; + +async function handler() { const host = 'http://dili.bdatu.com/jiekou/mains/p1.html'; const data = await got(host); @@ -33,9 +41,9 @@ export default async (ctx) => { return info; }); - ctx.set('data', { + return { title: 'Photo of the Daily Selection', link: api, item: out, - }); -}; + }; +} diff --git a/lib/routes/natgeo/natgeo.ts b/lib/routes/natgeo/natgeo.ts index 2397a9880..0e59239af 100644 --- a/lib/routes/natgeo/natgeo.ts +++ b/lib/routes/natgeo/natgeo.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { load } from 'cheerio'; import got from '@/utils/got'; @@ -26,7 +27,29 @@ async function loadContent(link) { }; } -export default async (ctx) => { +export const route: Route = { + path: '/:cat/:type?', + categories: ['shopping'], + example: '/natgeo/environment/article', + parameters: { cat: '分类', type: '类型, 例如`https://www.natgeomedia.com/environment/photo/`对应 `cat`, `type` 分别为 `environment`, `photo`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['natgeomedia.com/:cat/:type', 'natgeomedia.com/'], + target: '/:cat/:type', + }, + name: '分类', + maintainers: ['fengkx'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') ?? ''; const url = `https://www.natgeomedia.com/${ctx.req.param('cat')}/${type}`; const res = await got(url); @@ -49,9 +72,9 @@ export default async (ctx) => { return { ...single, ...other }; }) ); - ctx.set('data', { + return { title: $('title').text(), link: url, item: out, - }); -}; + }; +} diff --git a/lib/routes/nationalgeographic/latest-stories.ts b/lib/routes/nationalgeographic/latest-stories.ts index fc04fc702..f1d7fb1ea 100644 --- a/lib/routes/nationalgeographic/latest-stories.ts +++ b/lib/routes/nationalgeographic/latest-stories.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -15,7 +16,28 @@ const findNatgeo = ($) => .match(/\['__natgeo__']=({.*?});/)[1] ); -export default async (ctx) => { +export const route: Route = { + path: '/latest-stories', + categories: ['shopping'], + example: '/nationalgeographic/latest-stories', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.nationalgeographic.com/pages/topic/latest-stories'], + }, + name: 'Latest Stories', + maintainers: ['miles170'], + handler, +}; + +async function handler() { const currentUrl = 'https://www.nationalgeographic.com/pages/topic/latest-stories'; const response = await got(currentUrl); const $ = load(response.data); @@ -52,9 +74,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('meta[property="og:title"]').attr('content'), link: currentUrl, item: items.filter((item) => item !== null), - }); -}; + }; +} diff --git a/lib/routes/nature/cover.ts b/lib/routes/nature/cover.ts index a08a3823f..00b7c97b5 100644 --- a/lib/routes/nature/cover.ts +++ b/lib/routes/nature/cover.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; // The content is generateed by undocumentated API of nature journals // This router has **just** been tested in: @@ -21,7 +22,28 @@ import { parseDate } from '@/utils/parse-date'; import { baseUrl, journalMap } from './utils'; import { CookieJar } from 'tough-cookie'; -export default async (ctx) => { +export const route: Route = { + path: '/cover', + categories: ['finance'], + example: '/nature/cover', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['nature.com/'], + }, + name: 'Cover Story', + maintainers: ['y9c'], + handler, +}; + +async function handler() { const cookieJar = new CookieJar(); await got('https://www.nature.com', { cookieJar }); @@ -59,10 +81,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'Nature Covers Story', description: 'Find out the cover story of some Nature journals.', link: baseUrl, item: out, - }); -}; + }; +} diff --git a/lib/routes/nature/highlight.ts b/lib/routes/nature/highlight.ts index 66b8f51eb..0f9b2f224 100644 --- a/lib/routes/nature/highlight.ts +++ b/lib/routes/nature/highlight.ts @@ -1,8 +1,31 @@ +import { Route } from '@/types'; import { load } from 'cheerio'; import got from '@/utils/got'; import { baseUrl, cookieJar, getArticleList, getArticle } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/highlight/:journal?', + categories: ['finance'], + example: '/nature/highlight', + parameters: { journal: 'short name for a journal, `nature` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: true, + }, + radar: { + source: ['nature.com/:journal/articles', 'nature.com/:journal', 'nature.com/'], + target: '/highlight/:journal', + }, + name: 'Research Highlight', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { journal = 'nature' } = ctx.req.param(); const url = `${baseUrl}/${journal}/articles?type=research-highlight`; @@ -13,10 +36,10 @@ export default async (ctx) => { items = await Promise.all(items.map((item) => getArticle(item))); - ctx.set('data', { + return { title: $('title').text().trim(), description: $('meta[name=description]').attr('content'), link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/nature/news-and-comment.ts b/lib/routes/nature/news-and-comment.ts index 33e783370..6c5af24f7 100644 --- a/lib/routes/nature/news-and-comment.ts +++ b/lib/routes/nature/news-and-comment.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; // example usage: `/nature/news-and-comment/ng` // The journals from NPG are run by different group of people, // and the website of may not be consitent for all the journals @@ -16,7 +17,18 @@ import { load } from 'cheerio'; import got from '@/utils/got'; import { baseUrl, cookieJar, getArticleList, getArticle } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/news-and-comment/:journal?', + radar: { + source: ['nature.com/latest-news', 'nature.com/news', 'nature.com/'], + target: '/news', + }, + name: 'Unknown', + maintainers: ['y9c', 'TonyRL'], + handler, +}; + +async function handler(ctx) { const journal = ctx.req.param('journal'); const pageURL = `${baseUrl}/${journal}/news-and-comment`; @@ -28,10 +40,10 @@ export default async (ctx) => { items = await Promise.all(items.map((item) => getArticle(item))); - ctx.set('data', { + return { title: pageCapture('title').text(), description: pageDescription, link: pageURL, item: items, - }); -}; + }; +} diff --git a/lib/routes/nature/news.ts b/lib/routes/nature/news.ts index 1599d3134..2d2ad5af6 100644 --- a/lib/routes/nature/news.ts +++ b/lib/routes/nature/news.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 { baseUrl, cookieJar, getArticle } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/news', + categories: ['finance'], + example: '/nature/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: true, + }, + radar: { + source: ['nature.com/latest-news', 'nature.com/news', 'nature.com/'], + }, + name: 'Nature News', + maintainers: ['y9c', 'TonyRL'], + handler, +}; + +async function handler() { const url = `${baseUrl}/latest-news`; const res = await got(url, { cookieJar }); const $ = load(res.data); @@ -22,10 +44,10 @@ export default async (ctx) => { items = await Promise.all(items.map((item) => cache.tryGet(item.link, () => getArticle(item)))); - ctx.set('data', { + return { title: 'Nature | Latest News', description: $('meta[name=description]').attr('content'), link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/nature/research.ts b/lib/routes/nature/research.ts index 70fb0b1e8..0ebd509a6 100644 --- a/lib/routes/nature/research.ts +++ b/lib/routes/nature/research.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; // example usage: `/nature/research/ng` // The journals from NPG are run by different group of people, // and the website of may not be consitent for all the journals @@ -17,7 +18,29 @@ import { load } from 'cheerio'; import got from '@/utils/got'; import { baseUrl, cookieJar, getArticleList, getDataLayer, getArticle } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/research/:journal?', + categories: ['finance'], + example: '/nature/research/ng', + parameters: { journal: 'short name for a journal, `nature` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: true, + }, + radar: { + source: ['nature.com/:journal/research-articles', 'nature.com/:journal', 'nature.com/'], + target: '/research/:journal', + }, + name: 'Latest Research', + maintainers: ['y9c', 'TonyRL'], + handler, +}; + +async function handler(ctx) { const journal = ctx.req.param('journal') ?? 'nature'; const pageURL = `${baseUrl}/${journal}/research-articles`; @@ -30,10 +53,10 @@ export default async (ctx) => { items = await Promise.all(items.map((item) => getArticle(item))); - ctx.set('data', { + return { title: `Nature (${pageTitle}) | Latest Research`, description: pageCapture('meta[name="description"]').attr('content') || `Nature, a nature research journal`, link: pageURL, item: items, - }); -}; + }; +} diff --git a/lib/routes/nature/siteindex.ts b/lib/routes/nature/siteindex.ts index 93be70df8..5045f03c4 100644 --- a/lib/routes/nature/siteindex.ts +++ b/lib/routes/nature/siteindex.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { baseUrl, cookieJar } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/siteindex', + categories: ['finance'], + example: '/nature/siteindex', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Journal List', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const response = await got(`${baseUrl}/siteindex`, { cookieJar }); const $ = load(response.data); @@ -38,12 +57,12 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'Nature siteindex', link: response.url, item: items, - }); + }; ctx.set('json', { items, }); -}; +} diff --git a/lib/routes/nautil/topics.ts b/lib/routes/nautil/topics.ts index d315af650..afeb473ba 100644 --- a/lib/routes/nautil/topics.ts +++ b/lib/routes/nautil/topics.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 { art } from '@/utils/render'; import * as path from 'node:path'; const baseUrl = 'https://nautil.us'; -export default async (ctx) => { +export const route: Route = { + path: '/topic/:tid', + categories: ['traditional-media'], + example: '/nautil/topic/arts', + parameters: { tid: 'topic' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['nautil.us/topics/:tid'], + }, + name: 'Topics', + maintainers: ['emdoe'], + handler, +}; + +async function handler(ctx) { const categoryIdMap = await cache.tryGet('nautil:categories', async () => { const { data } = await got(`${baseUrl}/wp-json/wp/v2/categories`, { searchParams: { @@ -53,9 +75,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: 'Nautilus | ' + categoryIdMap.find((item) => item.slug === ctx.req.param('tid').toLowerCase()).name, link: `${baseUrl}/topics/${ctx.req.param('tid')}/`, item: out, - }); -}; + }; +} diff --git a/lib/routes/nbd/daily.ts b/lib/routes/nbd/daily.ts index a5a5398d8..03861727d 100644 --- a/lib/routes/nbd/daily.ts +++ b/lib/routes/nbd/daily.ts @@ -1,3 +1,25 @@ -export default (ctx) => { - ctx.redirect('/nbd/332'); +import { Route } from '@/types'; +export const route: Route = { + path: '/daily', + categories: ['other'], + example: '/nbd/daily', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['nbd.com.cn/', 'nbd.com.cn/columns/332'], + }, + name: '重磅原创', + maintainers: ['yuuow'], + handler, }; + +function handler(ctx) { + ctx.redirect('/nbd/332'); +} diff --git a/lib/routes/nbd/index.ts b/lib/routes/nbd/index.ts index 3d4568e08..69fc8f5ba 100644 --- a/lib/routes/nbd/index.ts +++ b/lib/routes/nbd/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: '/:id?', + categories: ['other'], + example: '/nbd', + parameters: { id: '分类 id,见下表,默认为要闻' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['nbd.com.cn/', 'nbd.com.cn/columns/:id?'], + }, + name: '分类', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? '3'; const rootUrl = 'https://www.nbd.com.cn'; @@ -48,9 +70,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('h1').text() || $('.u-channelname').text()} - 每经网`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/nber/index.ts b/lib/routes/nber/index.ts index 7ee7e1990..77914a2ba 100644 --- a/lib/routes/nber/index.ts +++ b/lib/routes/nber/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -15,7 +16,28 @@ async function getData(url) { return response.results; } -export default async (ctx) => { +export const route: Route = { + path: ['/papers', '/news'], + categories: ['finance'], + example: '/nber/papers', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: true, + }, + radar: { + source: ['nber.org/papers'], + }, + name: 'All Papers', + maintainers: [], + handler, +}; + +async function handler(ctx) { const url = 'https://www.nber.org/api/v1/working_page_listing/contentType/working_paper/_/_/search'; const baseUrl = 'https://www.nber.org'; const data = await cache.tryGet(url, () => getData(url), config.cache.routeExpire, false); @@ -44,10 +66,10 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: 'NBER Working Paper', link: 'https://www.nber.org/papers', item: items, description: `National Bureau of Economic Research Working Papers articles`, - }); -}; + }; +} diff --git a/lib/routes/ncc-cma/cmdp.ts b/lib/routes/ncc-cma/cmdp.ts index f504bb332..3f297f448 100644 --- a/lib/routes/ncc-cma/cmdp.ts +++ b/lib/routes/ncc-cma/cmdp.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 { art } from '@/utils/render'; import * as path from 'node:path'; import iconv from 'iconv-lite'; -export default async (ctx) => { +export const route: Route = { + path: '/cmdp/image/:id{.+}?', + name: 'Unknown', + maintainers: [], + 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; @@ -68,7 +76,7 @@ export default async (ctx) => { const image = $('img.logo').prop('src'); const icon = new URL('favicon.ico', rootUrl).href; - ctx.set('data', { + return { item: items, title: `${author} - ${subtitle}${titles.length === 0 ? '' : ` - ${titles.join('|')}`}`, link: currentUrl, @@ -80,5 +88,5 @@ export default async (ctx) => { subtitle, author, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/ncepu/master/masterinfo.ts b/lib/routes/ncepu/master/masterinfo.ts index fc640401c..0be8310d5 100644 --- a/lib/routes/ncepu/master/masterinfo.ts +++ b/lib/routes/ncepu/master/masterinfo.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 url_map = { pyxx: 'pyxx/pyxx', }; -export default async (ctx) => { +export const route: Route = { + path: '/master/:type', + categories: ['forecast'], + example: '/ncepu/master/tzgg', + parameters: { type: '类型参数' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '北京校区研究生院', + maintainers: ['nilleo'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); const response = await got(`https://yjsy.ncepu.edu.cn/${url_map[type]}/index.htm`); const data = response.data; // 获取页面 html 数据 @@ -65,10 +84,10 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `${title_map[type]}-华北电力大学研究生院`, link: `http://yjsy.ncepu.edu.cn/${url_map[type]}`, description: `华北电力大学研究生院${description_map[type]}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/ncu/jwc.ts b/lib/routes/ncu/jwc.ts index 01caf4250..502905d2f 100644 --- a/lib/routes/ncu/jwc.ts +++ b/lib/routes/ncu/jwc.ts @@ -1,15 +1,37 @@ +import { Route } from '@/types'; import got from '@/utils/got'; // 自订的 got import { load } from 'cheerio'; // 可以使用类似 jQuery 的 API HTML 解析器 import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/jwc', + categories: ['forecast'], + example: '/ncu/jwc', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['jwc.ncu.edu.cn/', 'jwc.ncu.edu.cn/jwtz/index.htm'], + }, + name: '教务通知', + maintainers: ['ywh555hhh'], + handler, +}; + +async function handler() { const baseUrl = 'https://jwc.ncu.edu.cn'; const response = await got(baseUrl); const $ = load(response.body); const list = $('.box3 .inner ul.img-list li'); - ctx.set('data', { + return { title: '南昌大学教务处', link: baseUrl, description: '南昌大学教务处', @@ -29,5 +51,5 @@ export default async (ctx) => { pubDate: parseDate(date, 'MM-DD').setFullYear(month < 6 ? new Date().getFullYear() - 1 : new Date().getFullYear()), }; }), - }); -}; + }; +} diff --git a/lib/routes/ncwu/notice.ts b/lib/routes/ncwu/notice.ts index 2f4b6036d..a692babdc 100644 --- a/lib/routes/ncwu/notice.ts +++ b/lib/routes/ncwu/notice.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { load } from 'cheerio'; const baseUrl = 'https://www.ncwu.edu.cn/xxtz.htm'; -export default async (ctx) => { +export const route: Route = { + path: '/notice', + categories: ['forecast'], + example: '/ncwu/notice', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ncwu.edu.cn/xxtz.htm'], + }, + name: '学校通知', + maintainers: [], + handler, +}; + +async function handler() { const response = await got(baseUrl); const $ = load(response.data); @@ -19,9 +41,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('title').text(), link: baseUrl, item: list, - }); -}; + }; +} diff --git a/lib/routes/ndss-symposium/ndss.ts b/lib/routes/ndss-symposium/ndss.ts index f83778c50..af17f3c26 100644 --- a/lib/routes/ndss-symposium/ndss.ts +++ b/lib/routes/ndss-symposium/ndss.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://www.ndss-symposium.org'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/ndss', + categories: ['finance'], + example: '/ndss-symposium/ndss', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ndss-symposium.org/'], + }, + name: 'Accepted papers', + maintainers: ['ZeddYu'], + handler, +}; + +async function handler() { // use wordpress api to get data const { data } = await got(`${url}/wp-json/wp/v2/pages?slug=accepted-papers`); @@ -71,11 +93,11 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: 'NDSS', link: url, description: 'The Network and Distributed System Security (NDSS) Symposium Accpeted Papers', allowEmpty: true, item: items.flat(), - }); -}; + }; +} diff --git a/lib/routes/neatdownloadmanager/download.ts b/lib/routes/neatdownloadmanager/download.ts index f1ffdd8d1..e84e7a660 100644 --- a/lib/routes/neatdownloadmanager/download.ts +++ b/lib/routes/neatdownloadmanager/download.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: '/download/:os?', + categories: ['university'], + example: '/neatdownloadmanager/download', + parameters: { os: 'Operating system, windows or macos, all by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['neatdownloadmanager.com/index.php', 'neatdownloadmanager.com/'], + }, + name: 'Download', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const os = (ctx.req.param('os') ?? '').toLowerCase(); const rootUrl = 'https://www.neatdownloadmanager.com'; @@ -38,9 +60,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: 'Neat Download Manager', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/neea/index.ts b/lib/routes/neea/index.ts index 7b2b26ed2..c02d90c33 100644 --- a/lib/routes/neea/index.ts +++ b/lib/routes/neea/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,14 @@ function loadContent(link) { }); } -export default async (ctx) => { +export const route: Route = { + path: '/:type?', + name: 'Unknown', + maintainers: ['SunShinenny'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); const host = `http://${type}.neea.edu.cn${typeDic[type].url}`; const response = await got({ @@ -48,13 +56,13 @@ export default async (ctx) => { return Object.assign({}, single, other); }) ); - ctx.set('data', { + return { title: `${typeDic[String(type)].title}动态`, link: host, description: `${typeDic[String(type)].title}动态 `, item: process, - }); -}; + }; +} const typeDic = { // 国家教育考试 diff --git a/lib/routes/neea/jlpt.ts b/lib/routes/neea/jlpt.ts index 12c515b0c..efc043893 100644 --- a/lib/routes/neea/jlpt.ts +++ b/lib/routes/neea/jlpt.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: '/jlpt', + categories: ['journal'], + example: '/neea/jlpt', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['jlpt.neea.cn/'], + }, + name: '教育部考试中心日本语能力测试重要通知', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://news.neea.cn'; const currentUrl = `${rootUrl}/JLPT/1/newslist.htm`; @@ -45,9 +67,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '重要通知 - 教育部考试中心日本语能力测试', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/nenu/sohac.ts b/lib/routes/nenu/sohac.ts index fc61eee2d..4181dc335 100644 --- a/lib/routes/nenu/sohac.ts +++ b/lib/routes/nenu/sohac.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: '/sohac/*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 10; const path = getSubPath(ctx) === '/sohac' ? '/index/tzgg' : getSubPath(ctx).replace(/^\/sohac/, ''); @@ -54,9 +62,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/nenu/yjsy.ts b/lib/routes/nenu/yjsy.ts index 6a5351309..498b96d3e 100644 --- a/lib/routes/nenu/yjsy.ts +++ b/lib/routes/nenu/yjsy.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: '/yjsy/*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 10; const path = getSubPath(ctx) === '/yjsy' ? '/tzgg' : getSubPath(ctx).replace(/^\/yjsy/, ''); @@ -58,9 +66,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/netflav/index.ts b/lib/routes/netflav/index.ts index f7590223f..fbf34f051 100644 --- a/lib/routes/netflav/index.ts +++ b/lib/routes/netflav/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: '/', + radar: { + source: ['netflav.com/'], + target: '', + }, + name: 'Unknown', + maintainers: ['TonyRL'], + handler, +}; + +async function handler() { const baseUrl = 'https://netflav.com'; const { data } = await got(baseUrl); @@ -30,7 +42,7 @@ export default async (ctx) => { category: [...new Set(item.tags?.map((t) => t.replace(/^(\w{2}:)/, '')))], })); - ctx.set('data', { + return { title: head.find((h) => h[0] === 'title')[1].children, description: head.find((h) => h[0] === 'meta' && h[1].name === 'description')[1].content, logo: `${baseUrl}${head.find((h) => h[0] === 'meta' && h[1].property === 'og:image')[1].content}`, @@ -38,5 +50,5 @@ export default async (ctx) => { link: baseUrl, item: items, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/neu/bmie.ts b/lib/routes/neu/bmie.ts index d39a3ea32..5ca915ba8 100644 --- a/lib/routes/neu/bmie.ts +++ b/lib/routes/neu/bmie.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 map = { alumni_style: 557, }; -export default async (ctx) => { +export const route: Route = { + path: '/bmie/:type', + categories: ['forecast'], + example: '/neu/bmie/news', + parameters: { type: '分类 id 见下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '医学与生物信息工程学院', + maintainers: ['tennousuathena'], + handler, +}; + +async function handler(ctx) { let type = ctx.req.param('type'); if (map[type] !== undefined) { type = map[type]; @@ -87,9 +106,9 @@ export default async (ctx) => { return result; }) ); - ctx.set('data', { + return { title: `东北大学 医学与生物信息工程学院 ${title}`, link: newsUrl, item: results, - }); -}; + }; +} diff --git a/lib/routes/neu/news.ts b/lib/routes/neu/news.ts index 6ef3ea6fc..96e5c4b71 100644 --- a/lib/routes/neu/news.ts +++ b/lib/routes/neu/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 { parseDate } from '@/utils/parse-date'; const baseUrl = 'https://neunews.neu.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/news/:type', + categories: ['forecast'], + example: '/neu/news/ddyw', + parameters: { type: '种类名,见下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['neunews.neu.edu.cn/:type/list.htm'], + }, + name: '新闻网', + maintainers: ['JeasonLau'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); const newsUrl = `${baseUrl}/${type}/list.htm`; const response = await got(newsUrl); @@ -37,9 +59,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `东北大学新闻网-${$('head title').text()}`, link: newsUrl, item: results, - }); -}; + }; +} diff --git a/lib/routes/newmuseum/exhibitions.ts b/lib/routes/newmuseum/exhibitions.ts index ea4a1889e..fbb3b9083 100644 --- a/lib/routes/newmuseum/exhibitions.ts +++ b/lib/routes/newmuseum/exhibitions.ts @@ -1,6 +1,25 @@ +import { Route } from '@/types'; import buildData from '@/utils/common-config'; -export default async (ctx) => { +export const route: Route = { + path: '/exhibitions', + categories: ['shopping'], + example: '/newmuseum/exhibitions', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Exhibitions', + maintainers: ['chazeon'], + handler, +}; + +async function handler(ctx) { let link; const state = ctx.req.query('state'); @@ -27,4 +46,4 @@ export default async (ctx) => { }, }) ); -}; +} diff --git a/lib/routes/newrank/douyin.ts b/lib/routes/newrank/douyin.ts index 0dde078bc..435cb8283 100644 --- a/lib/routes/newrank/douyin.ts +++ b/lib/routes/newrank/douyin.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import utils from './utils'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/douyin/:dyid', + categories: ['new-media'], + example: '/newrank/douyin/110266463747', + parameters: { dyid: '抖音ID,可在新榜账号详情 URL 中找到' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '抖音短视频', + maintainers: ['lessmoe'], + handler, +}; + +async function handler(ctx) { if (!config.newrank || !config.newrank.cookie) { throw new Error('newrank RSS is disabled due to the lack of relevant config'); } @@ -54,10 +73,10 @@ export default async (ctx) => { pubDate: item.create_time, })); - ctx.set('data', { + return { title: name + ' - 抖音', description, link: 'https://xd.newrank.cn/data/d/account/workList/' + uid, item: items, - }); -}; + }; +} diff --git a/lib/routes/newrank/wechat.ts b/lib/routes/newrank/wechat.ts index a85baba8b..53e12d243 100644 --- a/lib/routes/newrank/wechat.ts +++ b/lib/routes/newrank/wechat.ts @@ -1,10 +1,29 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { finishArticleItem } from '@/utils/wechat-mp'; import { load } from 'cheerio'; import utils from './utils'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/wechat/:wxid', + categories: ['new-media'], + example: '/newrank/wechat/chijiread', + parameters: { wxid: '微信号,若微信号与新榜信息不一致,以新榜为准' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '微信公众号', + maintainers: ['lessmoe'], + handler, +}; + +async function handler(ctx) { if (!config.newrank || !config.newrank.cookie) { throw new Error('newrank RSS is disabled due to the lack of relevant config'); } @@ -57,9 +76,9 @@ export default async (ctx) => { })); await Promise.all(items.map((item) => finishArticleItem(item))); - ctx.set('data', { + return { title: name + ' - 微信公众号', link: `https://www.newrank.cn/new/readDetial?account=${uid}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/news/xhsxw.ts b/lib/routes/news/xhsxw.ts index 356d042cf..90ae5ddc6 100644 --- a/lib/routes/news/xhsxw.ts +++ b/lib/routes/news/xhsxw.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 { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: ['/xhsxw', '/whxw'], + categories: ['traditional-media'], + example: '/news/xhsxw', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['news.cn/xhsxw.htm'], + }, + name: '新华社新闻', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 100; const rootUrl = 'http://www.news.cn'; @@ -70,7 +92,7 @@ export default async (ctx) => { const image = new URL('20141223_xhsxw_logo_v1.png', rootUrl).href; const icon = new URL('favicon.ico', rootUrl).href; - ctx.set('data', { + return { item: items, title, link: currentUrl, @@ -81,5 +103,5 @@ export default async (ctx) => { logo: icon, author: title.split(/_/).pop(), allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/newsmarket/index.ts b/lib/routes/newsmarket/index.ts index 20c0c91a1..a55441bd2 100644 --- a/lib/routes/newsmarket/index.ts +++ b/lib/routes/newsmarket/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: '/newsmarket', + parameters: { category: '分类,见下表,默认为首页' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['newsmarket.com.tw/blog/category/:category', 'newsmarket.com.tw/'], + }, + name: '分類', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? ''; const rootUrl = 'https://www.newsmarket.com.tw'; @@ -56,9 +78,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/newzmz/index.ts b/lib/routes/newzmz/index.ts index 1ffdb34d4..a81585eec 100644 --- a/lib/routes/newzmz/index.ts +++ b/lib/routes/newzmz/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 { rootUrl, getItems, getItemInfo, processItems } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/:id?/:downLinkType?', + categories: ['picture'], + example: '/newzmz/qEzRyY3v', + parameters: { id: '剧集 id,可在剧集下载页 URL 中找到', downLinkType: '下载链接类型,默认为磁力链' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['newzmz.com/'], + target: '', + }, + name: '指定剧集', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const { id = '1', downLinkType = '磁力链' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 50; @@ -51,7 +74,7 @@ export default async (ctx) => { const title = `${$('title').text()}${headerTitle ? ` - ${headerTitle}` : ''}`; const icon = $('link[rel="shortcut icon"]').prop('href'); - ctx.set('data', { + return { item: isCategory ? items : items.slice(0, limit), title, link: currentUrl, @@ -63,5 +86,5 @@ export default async (ctx) => { subtitle: $('meta[name="keywords"]').prop('content'), author: title, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/nextapple/realtime.ts b/lib/routes/nextapple/realtime.ts index 054e67170..2c12f3a1d 100644 --- a/lib/routes/nextapple/realtime.ts +++ b/lib/routes/nextapple/realtime.ts @@ -1,10 +1,32 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { load } from 'cheerio'; import asyncPool from 'tiny-async-pool'; -export default async (ctx) => { +export const route: Route = { + path: '/realtime/:category?', + categories: ['traditional-media'], + example: '/nextapple/realtime/latest', + parameters: { category: '類別,見下表,默認為首頁' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['tw.nextapple.com/', 'tw.nextapple.com/realtime/:category'], + }, + name: '最新新聞', + maintainers: ['miles170'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'latest'; const currentUrl = `https://tw.nextapple.com/realtime/${category}`; const response = await got(currentUrl); @@ -37,9 +59,9 @@ export default async (ctx) => { items.push(item); } - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/nga/forum.ts b/lib/routes/nga/forum.ts index 64bc54b42..4119da393 100644 --- a/lib/routes/nga/forum.ts +++ b/lib/routes/nga/forum.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { config } from '@/config'; @@ -5,7 +6,25 @@ import { parseDate } from '@/utils/parse-date'; const X_UA = 'NGA_skull/6.0.5(iPhone10,3;iOS 12.0.1)'; -export default async (ctx) => { +export const route: Route = { + path: '/forum/:fid/:recommend?', + categories: ['blog'], + example: '/nga/forum/489', + parameters: { fid: '分区 id, 可在分区主页 URL 找到, 没有 fid 时 stid 同样适用', recommend: '是否只显示精华主题, 留空为否, 任意值为是' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '分区帖子', + maintainers: ['xyqfer'], + handler, +}; + +async function handler(ctx) { const { fid, recommend } = ctx.req.param(); const timestamp = Math.floor(Date.now() / 1000); let cookieString = `guestJs=${timestamp};`; @@ -63,10 +82,10 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `NGA-${forumname}${recommend ? '-精华' : ''}`, link: `https://nga.178.com/thread.php?fid=${fid}`, description: 'NGA是国内专业的游戏玩家社区,魔兽世界,英雄联盟,炉石传说,风暴英雄,暗黑破坏神3(D3)游戏攻略讨论,以及其他热门游戏玩家社区', item: resultItem, - }); -}; + }; +} diff --git a/lib/routes/nga/post.ts b/lib/routes/nga/post.ts index f536e7b33..7e9e48f12 100644 --- a/lib/routes/nga/post.ts +++ b/lib/routes/nga/post.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import iconv from 'iconv-lite'; @@ -5,7 +6,25 @@ import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/post/:tid/:authorId?', + categories: ['blog'], + example: '/nga/post/18449558', + parameters: { tid: '帖子 id, 可在帖子 URL 找到', authorId: '作者 id' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '帖子', + maintainers: ['xyqfer', 'syrinka'], + handler, +}; + +async function handler(ctx) { const getPageUrl = (tid, authorId, page = 1, hash = '') => `https://nga.178.com/read.php?tid=${tid}&page=${page}${authorId ? `&authorid=${authorId}` : ''}&rand=${Math.random() * 1000}#${hash}`; const getPage = async (tid, authorId, pageId = 1) => { const link = getPageUrl(tid, authorId, pageId); @@ -114,9 +133,9 @@ export default async (ctx) => { const rssTitle = authorName ? `NGA ${authorName} ${title}` : `NGA ${title}`; - ctx.set('data', { + return { title: rssTitle, link: getPageUrl(tid, authorId, pageId), item: items.get(), - }); -}; + }; +} diff --git a/lib/routes/ngocn2/index.ts b/lib/routes/ngocn2/index.ts index 5a5811659..c9546ce94 100644 --- a/lib/routes/ngocn2/index.ts +++ b/lib/routes/ngocn2/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: '/ngocn2', + parameters: { category: '分类,见下表,默认为所有文章' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ngocn2.org/'], + }, + name: '首页', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'article'; const rootUrl = 'https://ngocn2.org'; @@ -49,9 +71,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('.sectitle__content').text()} - NGOCN`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/nhentai/other.ts b/lib/routes/nhentai/other.ts index cd70a1d32..9dc6d1e5e 100644 --- a/lib/routes/nhentai/other.ts +++ b/lib/routes/nhentai/other.ts @@ -1,9 +1,36 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { getSimple, getDetails, getTorrents } from './util'; const supportedKeys = new Set(['parody', 'character', 'tag', 'artist', 'group', 'language', 'category']); -export default async (ctx) => { +export const route: Route = { + path: '/:key/:keyword/:mode?', + categories: ['program-update'], + example: '/nhentai/language/chinese', + parameters: { + key: 'Filter term, can be: `parody`, `character`, `tag`, `artist`, `group`, `language` or `category`', + keyword: 'Filter value', + mode: 'mode, `simple` to only show cover, `detail` to show all pages, `torrent` to include Magnet URI, need login, refer to [Route-specific Configurations](/install/#configuration-route-specific-configurations), default to `simple`', + }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: true, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['nhentai.net/:key/:keyword'], + target: '/:key/:keyword', + }, + name: 'Filter', + maintainers: ['MegrezZhu', 'hoilc'], + handler, +}; + +async function handler(ctx) { const { key, keyword, mode } = ctx.req.param(); if (!supportedKeys.has(key)) { @@ -22,10 +49,10 @@ export default async (ctx) => { items = await getTorrents(cache, simples, limit); } - ctx.set('data', { + return { title: `nhentai - ${key} - ${keyword}`, link: url, description: 'hentai', item: items, - }); -}; + }; +} diff --git a/lib/routes/nhentai/search.ts b/lib/routes/nhentai/search.ts index 55bdbe2b2..f60f0e828 100644 --- a/lib/routes/nhentai/search.ts +++ b/lib/routes/nhentai/search.ts @@ -1,7 +1,33 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { getSimple, getDetails, getTorrents } from './util'; -export default async (ctx) => { +export const route: Route = { + path: '/search/:keyword/:mode?', + categories: ['program-update'], + example: '/nhentai/search/language%3Ajapanese+-scat+-yaoi+-guro+-"mosaic+censorship"', + parameters: { + keyword: 'Keywords for search. You can copy the content after `q=` after searching on the original website, or you can enter it directly. See the [official website](https://nhentai.net/info/) for details', + mode: 'mode, `simple` to only show cover, `detail` to show all pages, `torrent` to include Magnet URI, need login, refer to [Route-specific Configurations](/install/#configuration-route-specific-configurations), default to `simple`', + }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: true, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['nhentai.net/:key/:keyword'], + target: '/:key/:keyword', + }, + name: 'Advanced Search', + maintainers: ['MegrezZhu', 'hoilc'], + handler, +}; + +async function handler(ctx) { const { keyword, mode } = ctx.req.param(); const url = `https://nhentai.net/search/?q=${keyword}`; @@ -16,9 +42,9 @@ export default async (ctx) => { items = await getTorrents(cache, simples, limit); } - ctx.set('data', { + return { title: `nhentai - search - ${keyword}`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/nhk/news-web-easy.ts b/lib/routes/nhk/news-web-easy.ts index 1f8b7715d..faad4d394 100644 --- a/lib/routes/nhk/news-web-easy.ts +++ b/lib/routes/nhk/news-web-easy.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 { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/news_web_easy', + categories: ['bbs'], + example: '/nhk/news_web_easy', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www3.nhk.or.jp/news/easy/', 'www3.nhk.or.jp/'], + }, + name: 'News Web Easy', + maintainers: ['Andiedie'], + handler, +}; + +async function handler(ctx) { const { data } = await got('https://www3.nhk.or.jp/news/easy/news-list.json'); const dates = data[0]; @@ -44,10 +66,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'NEWS WEB EASY', link: 'https://www3.nhk.or.jp/news/easy/', description: 'NEWS WEB EASYは、小学生・中学生の皆さんや、日本に住んでいる外国人のみなさんに、わかりやすいことば でニュースを伝えるウェブサイトです。', item: items, - }); -}; + }; +} diff --git a/lib/routes/nhk/news.ts b/lib/routes/nhk/news.ts index 7ecdbb3e8..32908bb1b 100644 --- a/lib/routes/nhk/news.ts +++ b/lib/routes/nhk/news.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'; const baseUrl = 'https://www3.nhk.or.jp'; const apiUrl = 'https://nwapi.nhk.jp'; -export default async (ctx) => { +export const route: Route = { + path: '/news/:lang?', + categories: ['bbs'], + example: '/nhk/news/en', + parameters: { lang: 'Language, see below, `en` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www3.nhk.or.jp/nhkworld/:lang/news/list/', 'www3.nhk.or.jp/nhkworld/:lang/news/'], + target: '/news/:lang', + }, + name: 'WORLD-JAPAN - Top Stories', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { lang = 'en' } = ctx.req.param(); const { data } = await got(`${apiUrl}/nhkworld/rdnewsweb/v7b/${lang}/outline/list.json`); const meta = await got(`${baseUrl}/nhkworld/common/assets/news/config/${lang}.json`); @@ -37,9 +60,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${Object.values(meta.data.config.navigation.header).find((h) => h.keyname === 'topstories')?.name} | NHK WORLD-JAPAN News`, link: `${baseUrl}/nhkworld/${lang}/news/list/`, item: items, - }); -}; + }; +} diff --git a/lib/routes/niaogebiji/cat.ts b/lib/routes/niaogebiji/cat.ts index 59d050e64..40b3b6468 100644 --- a/lib/routes/niaogebiji/cat.ts +++ b/lib/routes/niaogebiji/cat.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: '/cat/:cat', + categories: ['traditional-media'], + example: '/niaogebiji/cat/103', + parameters: { cat: '如 https://www.niaogebiji.com/cat/103,最后的数字就是id' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['niaogebiji.com/cat/:cat'], + }, + name: '分类目录', + maintainers: ['cKotoriKat'], + handler, +}; + +async function handler(ctx) { const categoryId = ctx.req.param('cat'); const link = `https://www.niaogebiji.com/cat/${categoryId}`; @@ -45,10 +67,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/niaogebiji/index.ts b/lib/routes/niaogebiji/index.ts index 13d6c8992..d3f5ae3ba 100644 --- a/lib/routes/niaogebiji/index.ts +++ b/lib/routes/niaogebiji/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: ['niaogebiji.com/', 'niaogebiji.com/bulletin'], + target: '', + }, + name: 'Unknown', + maintainers: ['WenryXu'], + handler, +}; + +async function handler() { const baseUrl = 'https://www.niaogebiji.com'; const { data: response } = await got(`${baseUrl}/pc/index/getMoreArticle`); @@ -34,9 +46,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '鸟哥笔记', link: baseUrl, item: result, - }); -}; + }; +} diff --git a/lib/routes/niaogebiji/today.ts b/lib/routes/niaogebiji/today.ts index 5ea6da459..2728a4bb2 100644 --- a/lib/routes/niaogebiji/today.ts +++ b/lib/routes/niaogebiji/today.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: '/today', + categories: ['traditional-media'], + example: '/niaogebiji/today', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['niaogebiji.com/', 'niaogebiji.com/bulletin'], + target: '', + }, + name: '今日事', + maintainers: ['KotoriK'], + handler, +}; + +async function handler() { const response = await got({ method: 'post', url: 'https://www.niaogebiji.com/pc/bulletin/index', @@ -18,7 +41,7 @@ export default async (ctx) => { const data = response.data.return_data; - ctx.set('data', { + return { title: '鸟哥笔记-今日事', link: 'https://www.niaogebiji.com/bulletin', item: data.map((item) => ({ @@ -30,5 +53,5 @@ export default async (ctx) => { category: item.seo_keywords.split(','), author: item.user_info.nickname, })), - }); -}; + }; +} diff --git a/lib/routes/nifd/research.ts b/lib/routes/nifd/research.ts index e45248836..2769fc8c5 100644 --- a/lib/routes/nifd/research.ts +++ b/lib/routes/nifd/research.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 titleMap = { '6363bdc7-3e1b-4771-a904-6162cd3a3143': '其他报告', }; -export default async (ctx) => { +export const route: Route = { + path: '/research/:categoryGuid?', + categories: ['other'], + example: '/nifd/research/3333d2af-91d6-429b-be83-28b92f31b6d7', + parameters: { categoryGuid: '资讯类型,默认为周报' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '研究', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const categoryGuid = ctx.req.param('categoryGuid') ?? '7a6a826d-b525-42aa-b550-4236e524227f'; const url = `${rootUrl}/Research?categoryGuid=${categoryGuid}`; @@ -45,9 +64,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `国家金融与发展实验室 - ${titleMap[categoryGuid]}`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/nikkei/asia/index.ts b/lib/routes/nikkei/asia/index.ts index e72f13ee1..9b62b71f9 100644 --- a/lib/routes/nikkei/asia/index.ts +++ b/lib/routes/nikkei/asia/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: '/asia', + categories: ['bbs'], + example: '/nikkei/asia', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['asia.nikkei.com/'], + }, + name: 'Nikkei Asia Latest News', + maintainers: ['rainrdx'], + handler, +}; + +async function handler() { const currentUrl = 'https://main-asianreview-nikkei.content.pugpig.com/editionfeed/4519/pugpig_atom_contents.json'; const response = await got({ @@ -45,10 +67,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'Nikkei Asia', link: 'https://asia.nikkei.com', image: 'https://main-asianreview-nikkei.content.pugpig.com/pugpig_assets/admin/pub120x120.jpg', item: items, - }); -}; + }; +} diff --git a/lib/routes/nikkei/cn/index.ts b/lib/routes/nikkei/cn/index.ts index b92ce5582..8437b838a 100644 --- a/lib/routes/nikkei/cn/index.ts +++ b/lib/routes/nikkei/cn/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'; @@ -6,7 +7,14 @@ import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; import parser from '@/utils/rss-parser'; -export default async (ctx) => { +export const route: Route = { + path: '/cn/*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { let language = ''; let path = getSubPath(ctx); @@ -84,10 +92,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: isOfficialRSS ? officialFeed.title : $('title').first().text(), description: isOfficialRSS ? officialFeed.description : '', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/nikkei/index.ts b/lib/routes/nikkei/index.ts index ab8322ad5..49f5ea5d7 100644 --- a/lib/routes/nikkei/index.ts +++ b/lib/routes/nikkei/index.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: ['/', '/index'], + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler() { const url = 'https://www.nikkei.com'; const response = await got(url); const $ = load(response.data); @@ -28,9 +36,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: '日本経済新聞', link: url, item: list, - }); -}; + }; +} diff --git a/lib/routes/nikkei/news.ts b/lib/routes/nikkei/news.ts index b6337b090..a15274ec7 100644 --- a/lib/routes/nikkei/news.ts +++ b/lib/routes/nikkei/news.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: '/:category/:article_type?', + categories: ['bbs'], + example: '/nikkei/news', + parameters: { category: 'Category, see table below', article_type: 'Only includes free articles, set `free` to enable, disabled by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.nikkei.com/:category/archive', 'www.nikkei.com/:category'], + target: '/:category', + }, + name: 'News', + maintainers: ['Arracc'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://www.nikkei.com'; const { category, article_type = 'paid' } = ctx.req.param(); let url = ''; @@ -80,12 +103,12 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '日本経済新聞 - ' + categoryName, description: $('meta[name="description"]').attr('content'), link: url, image: $('meta[property="og:image"]').attr('content'), language: 'ja', item: article_type === 'free' ? items.filter((item) => !item.paywall) : items, - }); -}; + }; +} diff --git a/lib/routes/nintendo/direct.ts b/lib/routes/nintendo/direct.ts index 74f6167fd..2276c5760 100644 --- a/lib/routes/nintendo/direct.ts +++ b/lib/routes/nintendo/direct.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 { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/direct', + categories: ['reading'], + example: '/nintendo/direct', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['nintendo.com/nintendo-direct/archive', 'nintendo.com/'], + }, + name: 'Nintendo Direct', + maintainers: ['HFO4'], + handler, +}; + +async function handler() { const response = await got('https://www.nintendo.com/nintendo-direct/archive/'); const data = response.data; @@ -25,10 +47,10 @@ export default async (ctx) => { }), })); - ctx.set('data', { + return { title: 'Nintendo Direct(任天堂直面会)', link: 'https://www.nintendo.com/nintendo-direct/archive/', description: '最新的任天堂直面会日程信息', item: result, - }); -}; + }; +} diff --git a/lib/routes/nintendo/eshop-cn.ts b/lib/routes/nintendo/eshop-cn.ts index eab811396..9741064e9 100644 --- a/lib/routes/nintendo/eshop-cn.ts +++ b/lib/routes/nintendo/eshop-cn.ts @@ -1,10 +1,21 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import util from './utils'; const software_url = 'https://www.nintendoswitch.com.cn/software/'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/eshop/cn', + radar: { + source: ['nintendoswitch.com.cn/software', 'nintendoswitch.com.cn/'], + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler() { const response = await got(software_url); // 获取Nuxt对象 @@ -36,10 +47,10 @@ export default async (ctx) => { data = await util.ProcessItemChina(data, cache); - ctx.set('data', { + return { title: 'Nintendo eShop(国服)新游戏', link: 'https://www.nintendoswitch.com.cn/software', description: 'Nintendo(国服)新上架的游戏', item: data, - }); -}; + }; +} diff --git a/lib/routes/nintendo/eshop-hk.ts b/lib/routes/nintendo/eshop-hk.ts index f77a0a9ba..75518ceeb 100644 --- a/lib/routes/nintendo/eshop-hk.ts +++ b/lib/routes/nintendo/eshop-hk.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: '/eshop/hk', + radar: { + source: ['nintendo.com.hk/software/switch', 'nintendo.com.hk/'], + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const response = await got('https://www.nintendo.com.hk/data/json/switch_software.json'); const data = response.data.filter(({ link }) => link.startsWith('https://')).slice(0, ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30); @@ -74,10 +85,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'Nintendo eShop(港服)新游戏', link: 'https://www.nintendo.com.hk/software/switch/', description: 'Nintendo eShop(港服)新上架的游戏', item: result, - }); -}; + }; +} diff --git a/lib/routes/nintendo/eshop-jp.ts b/lib/routes/nintendo/eshop-jp.ts index 86422e6e1..cdb191b19 100644 --- a/lib/routes/nintendo/eshop-jp.ts +++ b/lib/routes/nintendo/eshop-jp.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: '/eshop/jp', + radar: { + source: ['nintendo.co.jp/software/switch/index.html', 'nintendo.co.jp/'], + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const response = await got('https://search.nintendo.jp/nintendo_soft/search.json', { searchParams: { opt_sshow: 1, @@ -21,7 +32,7 @@ export default async (ctx) => { }); const data = response.data.result.items; - ctx.set('data', { + return { title: 'Nintendo eShop(日服)新游戏', link: 'https://www.nintendo.co.jp/software/switch/index.html', description: 'Nintendo eShop(日服)新上架的游戏', @@ -33,5 +44,5 @@ export default async (ctx) => { link: `https://ec.nintendo.com/JP/ja/titles/${item.id}`, pubDate: parseDate(item.pdate), })), - }); -}; + }; +} diff --git a/lib/routes/nintendo/eshop-us.ts b/lib/routes/nintendo/eshop-us.ts index 0260146d7..840be9cfb 100644 --- a/lib/routes/nintendo/eshop-us.ts +++ b/lib/routes/nintendo/eshop-us.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -5,7 +6,17 @@ import { art } from '@/utils/render'; import got from '@/utils/got'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/eshop/us', + radar: { + source: ['nintendo.com/store/games', 'nintendo.com/'], + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const response = await got.post('https://u3b6gr4ua3-dsn.algolia.net/1/indexes/store_game_en_us_release_des/query', { headers: { 'x-algolia-api-key': 'a29c6927638bfd8cee23993e51e721c9', @@ -21,7 +32,7 @@ export default async (ctx) => { }); const data = response.data.hits; - ctx.set('data', { + return { title: `Nintendo eShop(美服)新游戏`, link: `https://www.nintendo.com/store/games/`, description: `Nintendo eShop(美服)新上架的游戏`, @@ -30,6 +41,6 @@ export default async (ctx) => { description: art(path.join(__dirname, 'templates/eshop_us.art'), item), link: `https://www.nintendo.com${item.url}`, })), - }); + }; ctx.set('json', response.data); -}; +} diff --git a/lib/routes/nintendo/news-china.ts b/lib/routes/nintendo/news-china.ts index 91bf9574a..cad8239c5 100644 --- a/lib/routes/nintendo/news-china.ts +++ b/lib/routes/nintendo/news-china.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import util from './utils'; const news_url = 'https://www.nintendoswitch.com.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/news/china', + categories: ['reading'], + example: '/nintendo/news/china', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['nintendoswitch.com.cn/'], + }, + name: '首页资讯(中国)', + maintainers: ['NeverBehave'], + handler, +}; + +async function handler() { const response = await got(news_url); // 获取Nuxt对象 @@ -27,10 +49,10 @@ export default async (ctx) => { data = await util.ProcessNewsChina(data, cache); - ctx.set('data', { + return { title: 'Nintendo(中国大陆)主页资讯', link: 'https://www.nintendoswitch.com.cn', description: 'Nintendo 中国大陆官网刊登的资讯', item: data, - }); -}; + }; +} diff --git a/lib/routes/nintendo/news.ts b/lib/routes/nintendo/news.ts index 24fae9ff4..2f4be1d2d 100644 --- a/lib/routes/nintendo/news.ts +++ b/lib/routes/nintendo/news.ts @@ -1,16 +1,38 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import util from './utils'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/news', + categories: ['reading'], + example: '/nintendo/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['nintendo.com.hk/topics', 'nintendo.com.hk/'], + }, + name: 'News(Hong Kong only)', + maintainers: ['HFO4'], + handler, +}; + +async function handler(ctx) { const response = await got('https://www.nintendo.com.hk/data/json/topics.json'); const data = response.data.filter((val) => val.only_for !== 'tw' && val.url.startsWith('/topics/article/')).slice(0, ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30); // 获取新闻正文 const result = await util.ProcessNews(data, cache); - ctx.set('data', { + return { title: 'Nintendo(香港)主页资讯', link: 'https://www.nintendo.com.hk/topics/', description: 'Nintendo 香港有限公司官网刊登的资讯', @@ -20,5 +42,5 @@ export default async (ctx) => { link: `https://www.nintendo.com.hk${item.url}`, pubDate: parseDate(item.release_date, 'YYYY.M.D'), })), - }); -}; + }; +} diff --git a/lib/routes/nintendo/system-update.ts b/lib/routes/nintendo/system-update.ts index 065314a1b..640203c83 100644 --- a/lib/routes/nintendo/system-update.ts +++ b/lib/routes/nintendo/system-update.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: '/system-update', + categories: ['reading'], + example: '/nintendo/system-update', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['nintendo.co.jp/support/switch/system_update/index.html', 'nintendo.co.jp/'], + }, + name: 'Switch System Update(Japan)', + maintainers: ['hoilc'], + handler, +}; + +async function handler() { const url = 'https://www.nintendo.co.jp/support/switch/system_update/index.html'; const response = await got(url); @@ -9,7 +31,7 @@ export default async (ctx) => { const list = $('.c-heading-lv3').toArray().slice(1, -2); - ctx.set('data', { + return { title: 'Nintendo Switch 本体更新情報', link: url, item: list.map((update) => { @@ -34,5 +56,5 @@ export default async (ctx) => { pubDate: new Date(Number.parseInt(matched_date[1]), Number.parseInt(matched_date[2]) - 1, Number.parseInt(matched_date[3])), }; }), - }); -}; + }; +} diff --git a/lib/routes/nippon/index.ts b/lib/routes/nippon/index.ts index 7a5bd4d02..fb231f45f 100644 --- a/lib/routes/nippon/index.ts +++ b/lib/routes/nippon/index.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: '/:category?', + categories: ['shopping'], + example: '/nippon/Politics', + parameters: { category: '默认政治,可选如下' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.nippon.com/nippon/:category?', 'www.nippon.com/cn'], + }, + name: 'Unknown', + maintainers: ['laampui'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'Politics'; const path = category === 'Science,Technology' ? 'condition4' : 'category_code'; const res = await got.get(`https://www.nippon.com/api/search/cn/${path}/20/1/${category}?t=${Date.now()}`); @@ -24,9 +46,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `走进日本 - ${category}`, link: 'https://www.nippon.com/cn/economy/', item, - }); -}; + }; +} diff --git a/lib/routes/njglyy/ygbjypx.ts b/lib/routes/njglyy/ygbjypx.ts index 087ed34f4..70d9cb138 100644 --- a/lib/routes/njglyy/ygbjypx.ts +++ b/lib/routes/njglyy/ygbjypx.ts @@ -1,18 +1,40 @@ +import { Route } from '@/types'; import { getNoticeList } from './utils'; const url = 'https://njglyy.com/ygb/jypx/jypx.aspx'; const host = 'https://njglyy.com/ygb/jypx/'; -export default async (ctx) => { +export const route: Route = { + path: '/ygbjypx', + categories: ['study'], + example: '/njglyy/ygbjypx', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['njglyy.com/ygb/jypx/jypx.aspx', 'njglyy.com/'], + }, + name: '员工版教育培训', + maintainers: ['real-jiakai'], + handler, +}; + +async function handler(ctx) { const out = await getNoticeList(ctx, url, host, '.mtbd-list > dl', 'a', 'dt', { title: '.detail', content: '.detail2', date: 'span:contains("发布时间")', }); - ctx.set('data', { + return { title: '南京鼓楼医院 -- 员工版教育培训', link: url, item: out, - }); -}; + }; +} diff --git a/lib/routes/njit/jwc.ts b/lib/routes/njit/jwc.ts index 1d3d28671..a5849b292 100644 --- a/lib/routes/njit/jwc.ts +++ b/lib/routes/njit/jwc.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 host = 'https://jwc.njit.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/jwc/:type?', + categories: ['forecast'], + example: '/njit/jwc/jx', + parameters: { type: '默认为 `jx`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '南京工程学院教务处', + maintainers: ['zefengdaguo'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') ?? 'jx'; const link = host + '/index/' + type + '.htm'; const response = await got({ @@ -99,9 +118,9 @@ export default async (ctx) => { info = '教学'; break; } - ctx.set('data', { + return { title: '南京工程学院教务处 -- ' + info, link, item: out, - }); -}; + }; +} diff --git a/lib/routes/njit/tzgg.ts b/lib/routes/njit/tzgg.ts index 6175a6e71..df12fe823 100644 --- a/lib/routes/njit/tzgg.ts +++ b/lib/routes/njit/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 url = 'https://www.njit.edu.cn/index/tzgg.htm'; const host = 'https://www.njit.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/tzgg', + categories: ['forecast'], + example: '/njit/tzgg', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.njit.edu.cn/'], + }, + name: '南京工程学院通知公告', + maintainers: ['zefengdaguo'], + handler, +}; + +async function handler() { const response = await got({ method: 'get', url, @@ -67,9 +89,9 @@ export default async (ctx) => { } }) ); - ctx.set('data', { + return { title: '南京工程学院 -- 通知公告', url, item: out, - }); -}; + }; +} diff --git a/lib/routes/njnu/ceai/ceai.ts b/lib/routes/njnu/ceai/ceai.ts index 437c92a43..4393a7069 100644 --- a/lib/routes/njnu/ceai/ceai.ts +++ b/lib/routes/njnu/ceai/ceai.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import util from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/ceai/:type', + categories: ['forecast'], + example: '/njnu/ceai/xszx', + parameters: { type: '分类名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '计算机与电子信息学院 - 人工智能学院', + maintainers: ['Shujakuinkuraudo'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); let title, path; switch (type) { @@ -36,10 +55,10 @@ export default async (ctx) => { const result = await util.ProcessFeed(list, cache); - ctx.set('data', { + return { title: '南京师范大学计电人院 - ' + title, link: 'http://ceai.njnu.edu.cn/', description: '南京师范大学计电人院', item: result, - }); -}; + }; +} diff --git a/lib/routes/njnu/jwc/jwc.ts b/lib/routes/njnu/jwc/jwc.ts index a68f52068..b5fa7dcec 100644 --- a/lib/routes/njnu/jwc/jwc.ts +++ b/lib/routes/njnu/jwc/jwc.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import util from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/jwc/:type', + categories: ['forecast'], + example: '/njnu/jwc/xstz', + parameters: { type: '分类名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '教务通知', + maintainers: ['Shujakuinkuraudo'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); let title, path; switch (type) { @@ -35,10 +54,10 @@ export default async (ctx) => { const result = await util.ProcessFeed(list, cache); - ctx.set('data', { + return { title: '南京师范大学教务处 - ' + title, link: 'http://jwc.njnu.edu.cn/', description: '南京师范大学教务处', item: result, - }); -}; + }; +} diff --git a/lib/routes/nju/admission.ts b/lib/routes/nju/admission.ts index f6020b6a0..ab177d2c2 100644 --- a/lib/routes/nju/admission.ts +++ b/lib/routes/nju/admission.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: '/admission', + categories: ['forecast'], + example: '/nju/admission', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['admission.nju.edu.cn/tzgg/index.html', 'admission.nju.edu.cn/tzgg', 'admission.nju.edu.cn/'], + }, + name: '本科迎新', + maintainers: ['ret-1'], + handler, +}; + +async function handler() { const category_dict = { tzgg: '通知公告', }; @@ -31,9 +53,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: '本科迎新-通知公告', link: 'https://admission.nju.edu.cn/tzgg', item: items[0], - }); -}; + }; +} diff --git a/lib/routes/nju/dafls.ts b/lib/routes/nju/dafls.ts index c60cd495c..057161652 100644 --- a/lib/routes/nju/dafls.ts +++ b/lib/routes/nju/dafls.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: '/dafls', + categories: ['forecast'], + example: '/nju/dafls', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['dafls.nju.edu.cn/13167/list.html', 'dafls.nju.edu.cn/'], + }, + name: '大学外语部', + maintainers: ['ret-1'], + handler, +}; + +async function handler() { const category_dict = { tzgg: '通知公告', }; @@ -29,9 +51,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: '大外部-通知公告', link: 'https://dafls.nju.edu.cn/13167/list.htm', item: [...items[0]], - }); -}; + }; +} diff --git a/lib/routes/nju/exchangesys.ts b/lib/routes/nju/exchangesys.ts index 32b81bc34..33172cce0 100644 --- a/lib/routes/nju/exchangesys.ts +++ b/lib/routes/nju/exchangesys.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import dayjs from 'dayjs'; -export default async (ctx) => { +export const route: Route = { + path: '/exchangesys/:type', + categories: ['forecast'], + example: '/nju/exchangesys/proj', + 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'); const type_dict = { news: ['http://elite.nju.edu.cn/exchangesystem/index/moreList', 'http://elite.nju.edu.cn/exchangesystem/index/more?type=xw', '新闻通知'], @@ -16,7 +35,7 @@ export default async (ctx) => { }, }); - ctx.set('data', { + return { title: `本科生交换生管理系统-${type_dict[type][2]}`, link: type_dict[type][1], item: @@ -42,5 +61,5 @@ export default async (ctx) => { } return null; }), - }); -}; + }; +} diff --git a/lib/routes/nju/gra.ts b/lib/routes/nju/gra.ts index 10cfa6114..f6c59eaf7 100644 --- a/lib/routes/nju/gra.ts +++ b/lib/routes/nju/gra.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: '/gra', + categories: ['forecast'], + example: '/nju/gra', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['grawww.nju.edu.cn/main.htm', 'grawww.nju.edu.cn/'], + }, + name: '研究生院', + maintainers: ['ret-1'], + handler, +}; + +async function handler() { const response = await got({ method: 'get', url: 'https://grawww.nju.edu.cn/905/list.htm', @@ -14,7 +36,7 @@ export default async (ctx) => { const $ = load(data); const list = $('li.news'); - ctx.set('data', { + return { title: '研究生院-动态通知', link: 'https://grawww.nju.edu.cn/905/list.htm', item: list @@ -35,5 +57,5 @@ export default async (ctx) => { }) .get() .filter(Boolean), - }); -}; + }; +} diff --git a/lib/routes/nju/hosptial.ts b/lib/routes/nju/hosptial.ts index 5b7699cbb..3233b784c 100644 --- a/lib/routes/nju/hosptial.ts +++ b/lib/routes/nju/hosptial.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: '/hospital', + categories: ['forecast'], + example: '/nju/hospital', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['hospital.nju.edu.cn/ggtz/index.html'], + }, + name: '校医院', + maintainers: ['ret-1'], + handler, +}; + +async function handler() { const category_dict = { ggtz: '公告通知', }; @@ -31,9 +53,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: '校医院-公告通知', link: 'https://hospital.nju.edu.cn/ggtz/index.html', item: items[0], - }); -}; + }; +} diff --git a/lib/routes/nju/hqjt.ts b/lib/routes/nju/hqjt.ts index 2b0f14dc0..1bffe1dd0 100644 --- a/lib/routes/nju/hqjt.ts +++ b/lib/routes/nju/hqjt.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: '/hqjt', + categories: ['forecast'], + example: '/nju/hqjt', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['webplus.nju.edu.cn/_s25/main.psp'], + }, + name: '后勤集团', + maintainers: ['ret-1'], + handler, +}; + +async function handler() { const category_dict = { zbcg: '招标采购', }; @@ -29,9 +51,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: '后勤集团-招标采购', link: 'https://webplus.nju.edu.cn/_s25/zbcg/list.psp', item: [...items[0]], - }); -}; + }; +} diff --git a/lib/routes/nju/itsc.ts b/lib/routes/nju/itsc.ts index 632215068..714844568 100644 --- a/lib/routes/nju/itsc.ts +++ b/lib/routes/nju/itsc.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: '/itsc', + categories: ['forecast'], + example: '/nju/itsc', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['itsc.nju.edu.cn/tzgg/list.htm'], + }, + name: 'ITSC 信息中心', + maintainers: ['ret-1'], + handler, +}; + +async function handler() { const category_dict = { tzgg: '通知公告', }; @@ -36,9 +58,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: 'ITSC-公告通知', link: 'https://itsc.nju.edu.cn/tzgg/list.htm', item: items[0], - }); -}; + }; +} diff --git a/lib/routes/nju/jjc.ts b/lib/routes/nju/jjc.ts index 803dcdb32..89d8f73de 100644 --- a/lib/routes/nju/jjc.ts +++ b/lib/routes/nju/jjc.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: '/jjc', + categories: ['forecast'], + example: '/nju/jjc', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['jjc.nju.edu.cn/main.htm', 'jjc.nju.edu.cn/'], + }, + name: '基建处', + maintainers: ['ret-1'], + handler, +}; + +async function handler() { const category_dict = { cgxx: '采购信息', cjgg: '成交公告', @@ -32,9 +54,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `南京大学基建处`, link: 'https://jjc.nju.edu.cn/main.htm', item: [...items[0], ...items[1], ...items[2]], - }); -}; + }; +} diff --git a/lib/routes/nju/jw.ts b/lib/routes/nju/jw.ts index e411b2bc9..220609ed6 100644 --- a/lib/routes/nju/jw.ts +++ b/lib/routes/nju/jw.ts @@ -1,8 +1,30 @@ +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: '/jw/:type', + categories: ['forecast'], + example: '/nju/jw/ggtz', + parameters: { type: '分类名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['jw.nju.edu.cn/:type/list.htm'], + }, + name: '本科生院', + maintainers: ['cqjjjzr'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); const type_dict = { ggtz: [ @@ -37,7 +59,7 @@ export default async (ctx) => { }, }); - ctx.set('data', { + return { title: `本科生院-${type_dict[type][1]}`, link: type_dict[type][4], item: @@ -55,5 +77,5 @@ export default async (ctx) => { } return ret; }), - }); -}; + }; +} diff --git a/lib/routes/nju/rczp.ts b/lib/routes/nju/rczp.ts index b7c14ea5c..1b76d3753 100644 --- a/lib/routes/nju/rczp.ts +++ b/lib/routes/nju/rczp.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 { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/rczp/:type', + categories: ['forecast'], + example: '/nju/rczp/xxfb', + parameters: { type: '分类名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['rczp.nju.edu.cn/sylm/:type/index.html'], + }, + name: '人才招聘网', + maintainers: ['ret-1'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); const title_dict = { xxfb: { title: '信息发布', channelid: '9531,9532,9533,9534,9535,9419' }, @@ -41,9 +63,9 @@ export default async (ctx) => { author: item.username, })); - ctx.set('data', { + return { title: `人才招聘-${title_dict[type].title}`, link, item: items, - }); -}; + }; +} diff --git a/lib/routes/nju/scit.ts b/lib/routes/nju/scit.ts index 96e56baa2..c76b12f8d 100644 --- a/lib/routes/nju/scit.ts +++ b/lib/routes/nju/scit.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: '/scit/:type', + categories: ['forecast'], + example: '/nju/scit/tzgg', + parameters: { type: '分类名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '科学技术处', + maintainers: ['ret-1'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); const type_dict = { tzgg: ['https://scit.nju.edu.cn/10916/list.htm', '通知公告'], @@ -19,7 +38,7 @@ export default async (ctx) => { const $ = load(data); const list = $('li.list_item'); - ctx.set('data', { + return { title: `科学技术处-${type_dict[type][1]}`, link: type_dict[type][0], item: list @@ -32,5 +51,5 @@ export default async (ctx) => { }; }) .get(), - }); -}; + }; +} diff --git a/lib/routes/nju/zbb.ts b/lib/routes/nju/zbb.ts index 26948cd0b..84e153e2a 100644 --- a/lib/routes/nju/zbb.ts +++ b/lib/routes/nju/zbb.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: '/zbb/:type', + categories: ['forecast'], + example: '/nju/zbb/cgxx', + parameters: { type: '分类名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '招标办公室', + maintainers: ['ret-1'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); if (type === 'zfcgyxgk') { const url = `https://zbb.nju.edu.cn/zfcgyxgk/index.chtml`; @@ -18,7 +37,7 @@ export default async (ctx) => { const $ = load(data); const list = $('dd[cid]'); - ctx.set('data', { + return { title: '政府采购意向公开', link: url, item: list @@ -32,7 +51,7 @@ export default async (ctx) => { }; }) .get(), - }); + }; } else { const title_dict = { cgxx: '采购信息', @@ -70,10 +89,10 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: title_dict[type], link: `https://zbb.nju.edu.cn/${type}hw/index.chtml`, item: [...items[0], ...items[1], ...items[2]], - }); + }; } -}; +} diff --git a/lib/routes/nju/zcc.ts b/lib/routes/nju/zcc.ts index 5482efef2..b100e42d4 100644 --- a/lib/routes/nju/zcc.ts +++ b/lib/routes/nju/zcc.ts @@ -1,7 +1,29 @@ +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: '/zcc', + categories: ['forecast'], + example: '/nju/zcc', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['zcc.nju.edu.cn/tzgg/gyfytdglk/index.html', 'zcc.nju.edu.cn/tzgg/index.html', 'zcc.nju.edu.cn/'], + }, + name: '资产管理处', + maintainers: ['ret-1'], + handler, +}; + +async function handler() { const category_dict = { ggtz: '公告通知', }; @@ -30,9 +52,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: '资产管理处-公告通知', link: 'https://zcc.nju.edu.cn/sy/tzzhxx/index.html', item: items[0], - }); -}; + }; +} diff --git a/lib/routes/njucm/grabs.ts b/lib/routes/njucm/grabs.ts index f265a71eb..9df8bc29f 100644 --- a/lib/routes/njucm/grabs.ts +++ b/lib/routes/njucm/grabs.ts @@ -1,18 +1,40 @@ +import { Route } from '@/types'; import { getNoticeList } from './utils'; const url = 'https://gra.njucm.edu.cn/2899/list.htm'; const host = 'https://gra.njucm.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/grabszs', + categories: ['forecast'], + example: '/njucm/grabszs', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['lib.njucm.edu.cn/2899/list.htm', 'lib.njucm.edu.cn/'], + }, + name: '研究生院博士招生', + maintainers: ['real-jiakai'], + handler, +}; + +async function handler(ctx) { const out = await getNoticeList(ctx, url, host, '#wp_news_w3 > table > tbody > tr', 'a', { title: '.Article_Title', content: '.Article_Content', date: '.Article_PublishDate', }); - ctx.set('data', { + return { title: '南京中医药大学 -- 博士招生', link: url, item: out, - }); -}; + }; +} diff --git a/lib/routes/njupt/jwc.ts b/lib/routes/njupt/jwc.ts index 1362ea35a..35ce9dd7e 100644 --- a/lib/routes/njupt/jwc.ts +++ b/lib/routes/njupt/jwc.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,25 @@ const map = { news: '/1596', }; -export default async (ctx) => { +export const route: Route = { + path: '/jwc/:type?', + categories: ['forecast'], + example: '/njupt/jwc/notice', + parameters: { type: '默认为 `notice`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '教务处通知与新闻', + maintainers: ['shaoye'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') ?? 'notice'; const link = host + map[type] + '/list.htm'; const response = await got({ @@ -83,9 +102,9 @@ export default async (ctx) => { if (type === 'news') { info = '教务快讯'; } - ctx.set('data', { + return { title: '南京邮电大学 -- ' + info, link, item: out, - }); -}; + }; +} diff --git a/lib/routes/njust/cwc.ts b/lib/routes/njust/cwc.ts index 13f5a61bf..6a5ff4085 100644 --- a/lib/routes/njust/cwc.ts +++ b/lib/routes/njust/cwc.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; @@ -10,7 +11,25 @@ const map = new Map([ const host = 'https://cwc.njust.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/cwc/:type?', + categories: ['forecast'], + example: '/njust/cwc/tzgg', + parameters: { type: '分类名,见下表,默认为通知公告' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '财务处', + maintainers: ['MilkShakeYoung', 'jasongzy'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') ?? 'tzgg'; const info = map.get(type); if (!info) { @@ -23,7 +42,7 @@ export default async (ctx) => { const $ = load(html); const list = $('ul.news_list').find('li'); - ctx.set('data', { + return { title: info.title, link: siteUrl, item: @@ -35,5 +54,5 @@ export default async (ctx) => { link: $(item).find('a').attr('href'), })) .get(), - }); -}; + }; +} diff --git a/lib/routes/njust/dgxg.ts b/lib/routes/njust/dgxg.ts index e4bb32aa2..2c7f5734d 100644 --- a/lib/routes/njust/dgxg.ts +++ b/lib/routes/njust/dgxg.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; @@ -11,7 +12,25 @@ const map = new Map([ const host = 'https://dgxg.njust.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/dgxg/:type?', + categories: ['forecast'], + example: '/njust/dgxg/gstz', + parameters: { type: '分类名,见下表,默认为公示通知' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '电光学院研学网', + maintainers: ['jasongzy'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') ?? 'gstz'; const info = map.get(type); if (!info) { @@ -24,7 +43,7 @@ export default async (ctx) => { const $ = load(html); const list = $('ul.wp_article_list').find('li'); - ctx.set('data', { + return { title: info.title, link: siteUrl, item: @@ -36,5 +55,5 @@ export default async (ctx) => { link: $(item).find('a').attr('href'), })) .get(), - }); -}; + }; +} diff --git a/lib/routes/njust/eo.ts b/lib/routes/njust/eo.ts index 2fe977734..d33ee4bdd 100644 --- a/lib/routes/njust/eo.ts +++ b/lib/routes/njust/eo.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; @@ -16,7 +17,28 @@ const map = new Map([ const host = 'https://dgxg.njust.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/eo/:grade?/:type?', + categories: ['forecast'], + example: '/njust/eo/17/tz', + parameters: { + grade: '年级,见下表,默认为本科 2017 级,未列出的年级所对应的参数可以从级网二级页面的 URL Path 中找到,例如:本科 2020 级为 `_t1316`', + type: '类别,见下表,默认为年级通知(通知公告),未列出的类别所对应的参数可以从级网二级页面的 URL Path 中找到,例如:电光 20 的通知公告为 `tzgg_12969`', + }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '电光学院年级网站', + maintainers: ['jasongzy'], + handler, +}; + +async function handler(ctx) { const grade = ctx.req.param('grade') ?? '17'; const type = ctx.req.param('type') ?? 'tz'; let info = map.get(grade + type); @@ -34,7 +56,7 @@ export default async (ctx) => { } const list = $('li.list_item'); - ctx.set('data', { + return { title: info.title, link: siteUrl, item: @@ -46,5 +68,5 @@ export default async (ctx) => { link: $(item).find('a').attr('href'), })) .get(), - }); -}; + }; +} diff --git a/lib/routes/njust/eoe.ts b/lib/routes/njust/eoe.ts index 976f5ce43..96f832a76 100644 --- a/lib/routes/njust/eoe.ts +++ b/lib/routes/njust/eoe.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; @@ -10,7 +11,25 @@ const map = new Map([ const host = 'https://eoe.njust.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/eoe/:type?', + categories: ['forecast'], + example: '/njust/eoe/tzgg', + parameters: { type: '分类名,见下表,默认为通知公告' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '电光学院', + maintainers: ['jasongzy'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') ?? 'tzgg'; const info = map.get(type); if (!info) { @@ -23,7 +42,7 @@ export default async (ctx) => { const $ = load(html); const list = $('ul.news_ul').find('li'); - ctx.set('data', { + return { title: info.title, link: siteUrl, item: @@ -35,5 +54,5 @@ export default async (ctx) => { link: $(item).find('a').attr('href'), })) .get(), - }); -}; + }; +} diff --git a/lib/routes/njust/gs.ts b/lib/routes/njust/gs.ts index a01bd76d4..1fd51ccbd 100644 --- a/lib/routes/njust/gs.ts +++ b/lib/routes/njust/gs.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; @@ -5,7 +6,29 @@ import { getContent } from './utils'; const host = 'https://gs.njust.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/gs/:type?', + categories: ['forecast'], + example: '/njust/gs/sytzgg_4568', + parameters: { type: '分类 ID,部分示例参数见下表,默认为首页通知公告,其他分类 ID 可以从网站 URL Path 中找到,如国际交流为 `gjjl`' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['gs.njust.edu.cn/:type/list.htm'], + target: '/gs/:type', + }, + name: '研究生院', + maintainers: ['MilkShakeYoung', 'jasongzy'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') ?? 'sytzgg_4568'; const id = '/' + type; const siteUrl = host + id + '/list.htm'; @@ -33,9 +56,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title, link: siteUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/njust/jwc.ts b/lib/routes/njust/jwc.ts index e60bc95a9..c22c7dc11 100644 --- a/lib/routes/njust/jwc.ts +++ b/lib/routes/njust/jwc.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; @@ -12,7 +13,25 @@ const map = new Map([ const host = 'https://jwc.njust.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/jwc/:type?', + categories: ['forecast'], + example: '/njust/jwc/xstz', + parameters: { type: '分类名,见下表,默认为学生通知' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '教务处', + maintainers: ['MilkShakeYoung', 'jasongzy'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') ?? 'xstz'; const info = map.get(type); if (!info) { @@ -25,7 +44,7 @@ export default async (ctx) => { const $ = load(html); const list = $('div#wp_news_w3').find('tr'); - ctx.set('data', { + return { title: info.title, link: siteUrl, item: @@ -37,5 +56,5 @@ export default async (ctx) => { link: $(item).find('a').attr('href'), })) .get(), - }); -}; + }; +} diff --git a/lib/routes/njxzc/home.ts b/lib/routes/njxzc/home.ts index 94de93fc0..be9859586 100644 --- a/lib/routes/njxzc/home.ts +++ b/lib/routes/njxzc/home.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import { getNoticeList } from './utils'; const url = 'https://www.njxzc.edu.cn/89/list.htm'; const host = 'https://www.njxzc.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/tzgg', + categories: ['forecast'], + example: '/njxzc/tzgg', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.njxzc.edu.cn/89/list.htm', 'www.njxzc.edu.cn/'], + }, + name: '官网通知公告', + maintainers: ['real-jiakai'], + handler, +}; + +async function handler(ctx) { const out = await getNoticeList( ctx, url, @@ -18,9 +40,9 @@ export default async (ctx) => { '.news_list .news' ); - ctx.set('data', { + return { title: '南京晓庄学院 -- 通知公告', link: url, item: out, - }); -}; + }; +} diff --git a/lib/routes/njxzc/lib.ts b/lib/routes/njxzc/lib.ts index b18c2a5f7..5fffa6b01 100644 --- a/lib/routes/njxzc/lib.ts +++ b/lib/routes/njxzc/lib.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import { getNoticeList } from './utils'; const url = 'https://lib.njxzc.edu.cn/pxyhd/list.htm'; const host = 'https://lib.njxzc.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/libtzgg', + categories: ['forecast'], + example: '/njxzc/libtzgg', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['lib.njxzc.edu.cn/pxyhd/list.htm', 'lib.njxzc.edu.cn/'], + }, + name: '图书馆通知公告', + maintainers: ['real-jiakai'], + handler, +}; + +async function handler(ctx) { const out = await getNoticeList( ctx, url, @@ -18,9 +40,9 @@ export default async (ctx) => { '.news' ); - ctx.set('data', { + return { title: '南京晓庄学院 -- 图书馆通知公告', link: url, item: out, - }); -}; + }; +} diff --git a/lib/routes/nlc/read.ts b/lib/routes/nlc/read.ts index f9afd6468..002387d58 100644 --- a/lib/routes/nlc/read.ts +++ b/lib/routes/nlc/read.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/read/:type?', + categories: ['other'], + example: '/nlc/read/电子图书', + parameters: { type: '分类,见下表,默认为电子图书' }, + 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(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 15; @@ -55,7 +74,7 @@ export default async (ctx) => { const image = new URL('static/style/css/images/YMH_home_main_logo.png', rootUrl).href; const icon = new URL($('link[rel="shortcut icon"]').prop('href'), rootUrl).href; - ctx.set('data', { + return { item: items, title: `${$('title').text()} - ${type}`, link: currentUrl, @@ -67,5 +86,5 @@ export default async (ctx) => { subtitle: type, author, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/nltimes/news.ts b/lib/routes/nltimes/news.ts index 0bcb9439d..5f5b326b2 100644 --- a/lib/routes/nltimes/news.ts +++ b/lib/routes/nltimes/news.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,29 @@ const map = new Map([ ['1-1-2', { title: 'NL Times -- 1-1-2', suffix: '/categories/1-1-2' }], ]); -export default async (ctx) => { +export const route: Route = { + path: '/news/:category?', + categories: ['traditional-media'], + example: '/nltimes/news/top-stories', + parameters: { category: 'category' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['nltimes.nl/categories/:category'], + target: '/news/:category', + }, + name: 'News', + maintainers: ['Hivol'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'top-stories'; const suffix = map.get(category).suffix; @@ -80,11 +103,11 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: map.get(category).title, language: 'en', link: apiUrl, description: map.get(category).title, item: items, - }); -}; + }; +} diff --git a/lib/routes/nmc/weatheralarm.ts b/lib/routes/nmc/weatheralarm.ts index 825d35650..4e88c2bbb 100644 --- a/lib/routes/nmc/weatheralarm.ts +++ b/lib/routes/nmc/weatheralarm.ts @@ -1,10 +1,33 @@ +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 * as cheerio from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/weatheralarm/:province?', + categories: ['travel'], + example: '/nmc/weatheralarm/广东省', + parameters: { province: '省份' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['nmc.cn/publish/alarm.html', 'nmc.cn/'], + target: '/weatheralarm', + }, + name: 'Unknown', + maintainers: ['ylc395'], + handler, +}; + +async function handler(ctx) { const { province } = ctx.req.param(); const alarmInfoURL = `http://www.nmc.cn/rest/findAlarm`; const { data: response } = await got(alarmInfoURL, { @@ -41,9 +64,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '中央气象台全国气象预警', link: 'http://www.nmc.cn/publish/alarm.html', item: items, - }); -}; + }; +} diff --git a/lib/routes/nmtv/column.ts b/lib/routes/nmtv/column.ts index 9cc4423fc..c8c4cea98 100644 --- a/lib/routes/nmtv/column.ts +++ b/lib/routes/nmtv/column.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 { 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: '/column/:id?', + categories: ['bbs'], + example: '/nmtv/column/877', + 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') ?? '877'; const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 100; @@ -46,11 +65,11 @@ export default async (ctx) => { const author = data[0].column_name; const imageUrl = data[0].column_info.indexpic; - ctx.set('data', { + return { title: `内蒙古广播电视台 - ${author}`, link: items[0].link.split(/\/\d{4}-\d{2}-\d{2}\//)[0], item: items, image: `${imageUrl.host}${imageUrl.filepath}${imageUrl.filename}`, itunes_author: author, - }); -}; + }; +} diff --git a/lib/routes/nodejs/blog.ts b/lib/routes/nodejs/blog.ts index bfa010c21..537c03536 100644 --- a/lib/routes/nodejs/blog.ts +++ b/lib/routes/nodejs/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/:language?', + categories: ['design'], + example: '/nodejs/blog', + parameters: { language: 'Language, see below, en by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['nodejs.org/:language/blog', 'nodejs.org/'], + }, + name: 'News', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const language = ctx.req.param('language') ?? 'en'; const rootUrl = 'https://nodejs.org'; @@ -53,9 +75,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'News - Node.js', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/nogizaka46/blog.ts b/lib/routes/nogizaka46/blog.ts index 7c9c128e1..7f050bcad 100644 --- a/lib/routes/nogizaka46/blog.ts +++ b/lib/routes/nogizaka46/blog.ts @@ -1,9 +1,32 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; const rootUrl = 'https://www.nogizaka46.com'; -export default async (ctx) => { +export const route: Route = { + path: '/blog/:id?', + categories: ['traditional-media'], + example: '/nogizaka46/blog', + parameters: { id: 'Member ID, see below, `all` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['blog.nogizaka46.com/s/n46/diary/MEMBER'], + target: '/blog', + }, + name: 'Nogizaka46 Blog 乃木坂 46 博客', + maintainers: ['Kasper4649', 'akashigakki'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id') ?? 'all'; const params = id === 'all' ? '' : `?ct=${id}`; const currentUrl = `${rootUrl}/s/n46/api/list/blog${params}`; @@ -15,7 +38,7 @@ export default async (ctx) => { const list = JSON.parse(response.data.slice(4).slice(0, -2)).data; - ctx.set('data', { + return { allowEmpty: true, title: '乃木坂46 公式ブログ', link: 'https://www.nogizaka46.com/s/n46/diary/MEMBER', @@ -29,5 +52,5 @@ export default async (ctx) => { description: item.text, guid: rootUrl + new URL(item.link).pathname, })), - }); -}; + }; +} diff --git a/lib/routes/nogizaka46/news.ts b/lib/routes/nogizaka46/news.ts index 1f035eaee..2ac240700 100644 --- a/lib/routes/nogizaka46/news.ts +++ b/lib/routes/nogizaka46/news.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; const rootUrl = 'https://www.nogizaka46.com'; -export default async (ctx) => { +export const route: Route = { + path: '/news', + categories: ['traditional-media'], + example: '/nogizaka46/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['news.nogizaka46.com/s/n46/news/list'], + }, + name: 'Nogizaka46 News 乃木坂 46 新闻', + maintainers: ['crispgm', 'Fatpandac'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 25; const response = await got({ @@ -24,10 +46,10 @@ export default async (ctx) => { guid: rootUrl + new URL(item.link_url).pathname, })); - ctx.set('data', { + return { allowEmpty: true, title: '乃木坂46官网 NEWS', link: 'http://www.nogizaka46.com/news/', item: items, - }); -}; + }; +} diff --git a/lib/routes/notateslaapp/update.ts b/lib/routes/notateslaapp/update.ts index dfa98abf2..176220233 100644 --- a/lib/routes/notateslaapp/update.ts +++ b/lib/routes/notateslaapp/update.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: '/ota', + categories: ['university'], + example: '/notateslaapp/ota', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['notateslaapp.com/software-updates/history', 'notateslaapp.com/software-updates', 'notateslaapp.com/'], + }, + name: 'Tesla Software Updates', + maintainers: ['mrbruce516'], + handler, +}; + +async function handler() { const response = await got({ method: 'get', url: 'https://www.notateslaapp.com/software-updates/history/', @@ -15,7 +37,7 @@ export default async (ctx) => { const $ = load(data); const list = $('article[id]'); - ctx.set('data', { + return { title: '特斯拉系统更新', link: 'https://www.notateslaapp.com/software-updates/history/', description: '特斯拉系统更新 - 最新发布', @@ -32,5 +54,5 @@ export default async (ctx) => { }; }) .get(), - }); -}; + }; +} diff --git a/lib/routes/notefolio/search.ts b/lib/routes/notefolio/search.ts index c3d7aca0b..058f40521 100644 --- a/lib/routes/notefolio/search.ts +++ b/lib/routes/notefolio/search.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -67,7 +68,33 @@ const categoryMap = [ * @param query 搜索词 * @query limit 分页,默认20 */ -export default async (ctx) => { +export const route: Route = { + path: '/search/:category?/:order?/:time?/:query?', + categories: ['live'], + example: '/notefolio/search/1/pick/all/life', + parameters: { + category: 'Category, see below, `all` by default', + order: 'Order, `pick` as Notefolio Pick, `published` as Newest, `like` as like, `pick` by default', + time: 'Time, `all` as All the time, `one-day` as Latest 24 hours, `week` as Latest week, `month` as Latest month, `three-month` as Latest 3 months, `all` by default', + query: 'Keyword, empty by default', + }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['notefolio.net/search'], + }, + name: 'Works', + maintainers: ['BianTan'], + handler, +}; + +async function handler(ctx) { // 分类、排序、时间范围、搜索词 const { category = 'all', order = 'pick', time = 'all', query = '' } = ctx.req.param(); // 分页 @@ -136,12 +163,12 @@ export default async (ctx) => { }; }) || []; - ctx.set('data', { + return { // 源标题 title: `${category}/${order}/${time}/${query} search`, // 源链接 // link: '', // 源文章 item: items, - }); -}; + }; +} diff --git a/lib/routes/notion/database.ts b/lib/routes/notion/database.ts index 9783b286a..f6a6edde0 100644 --- a/lib/routes/notion/database.ts +++ b/lib/routes/notion/database.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { Client, isNotionClientError, APIErrorCode } from '@notionhq/client'; import logger from '@/utils/logger'; @@ -12,7 +13,29 @@ const md = MarkdownIt({ linkify: true, }); -export default async (ctx) => { +export const route: Route = { + path: '/database/:databaseId', + categories: ['other'], + example: '/notion/database/a7cc133b68454f138011f1530a13531e', + parameters: { databaseId: 'Database ID' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['notion.so/:id'], + target: '/database/:id', + }, + name: 'Database', + maintainers: ['curly210102'], + handler, +}; + +async function handler(ctx) { if (!config.notion.key) { throw new Error('Notion RSS is disabled due to the lack of NOTION_TOKEN(relevant config)'); } @@ -107,14 +130,14 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `Notion - ${title}`, link, description, image, item: items, allowEmpty: true, - }); + }; } catch (error) { logger.error(error); @@ -130,7 +153,7 @@ export default async (ctx) => { ctx.throw(error); } } -}; +} function parseCustomQuery(queryString) { try { diff --git a/lib/routes/now/news.ts b/lib/routes/now/news.ts index 1581c1c5c..2c4b2b958 100644 --- a/lib/routes/now/news.ts +++ b/lib/routes/now/news.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -9,7 +10,28 @@ const categories = { opinion: 125, }; -export default async (ctx) => { +export const route: Route = { + path: '/news/:category?/:id?', + categories: ['bbs'], + example: '/now/news', + parameters: { category: '分类,见下表,默认为首页', id: '编号,可在对应专题/节目页 URL 中找到 topicId' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['news.now.com/'], + }, + name: '新聞', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') || ''; const id = ctx.req.param('id') || ''; @@ -54,9 +76,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: String(Object.hasOwn(categories, category) ? $('title').text() : ($('.smallSpace.active').text() || '首頁') + ' | Now 新聞'), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/nowcoder/discuss.ts b/lib/routes/nowcoder/discuss.ts index 2b85d3b87..9ffd03bb3 100644 --- a/lib/routes/nowcoder/discuss.ts +++ b/lib/routes/nowcoder/discuss.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 host = 'https://www.nowcoder.com'; -export default async (ctx) => { +export const route: Route = { + path: '/discuss/:type/:order', + categories: ['blog'], + example: '/nowcoder/discuss/2/4', + parameters: { type: '讨论区分区id 在 URL 中可以找到', order: '排序方式' }, + 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 order = ctx.req.param('order'); @@ -50,9 +69,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `${type_name}${order_name}——牛客网讨论区`, link, item: out, - }); -}; + }; +} diff --git a/lib/routes/nowcoder/experience.ts b/lib/routes/nowcoder/experience.ts index 4aa10bfd6..de82838f5 100644 --- a/lib/routes/nowcoder/experience.ts +++ b/lib/routes/nowcoder/experience.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 host = 'https://www.nowcoder.com'; -export default async (ctx) => { +export const route: Route = { + path: '/experience/:tagId', + categories: ['blog'], + example: '/nowcoder/experience/639?order=3&companyId=665&phaseId=0', + parameters: { tagId: '职位id [🔗查询链接](https://www.nowcoder.com/profile/all-jobs)复制打开' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['nowcoder.com/'], + target: '/experience', + }, + name: '面经', + maintainers: ['huyyi'], + handler, +}; + +async function handler(ctx) { const params = new URLSearchParams(ctx.req.query()); params.append('tagId', ctx.req.param('tagId')); @@ -41,9 +64,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `牛客面经Tag${ctx.req.param('tagId')}`, link: link.href, item: out, - }); -}; + }; +} diff --git a/lib/routes/nowcoder/jobcenter.ts b/lib/routes/nowcoder/jobcenter.ts index 743cdc3a4..a22e4459f 100644 --- a/lib/routes/nowcoder/jobcenter.ts +++ b/lib/routes/nowcoder/jobcenter.ts @@ -1,9 +1,38 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import * as url from 'node:url'; import got from '@/utils/got'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/jobcenter/:recruitType?/:city?/:type?/:order?/:latest?', + categories: ['blog'], + example: '/nowcoder/jobcenter/1/北京/1/1/true', + parameters: { + recruitType: '招聘分类,`1` 指 实习广场,`2` 指 社招广场,默认为 `1`', + city: '所在城市,可选城市见下表,若空则为 `全国`', + type: '职位类型,可选职位代码见下表,若空则为 `全部`', + order: '排序参数,可选排序参数代码见下表,若空则为 `默认`', + latest: '是否仅查看最近一周,可选 `true` 和 `false`,默认为 `false`', + }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['nowcoder.com/'], + target: '/jobcenter', + }, + name: '实习广场 & 社招广场', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const rootUrl = `https://www.nowcoder.com/job/center/`; const currentUrl = `${rootUrl}?${ctx.req.param('type') ? 'type=' + ctx.req.param('type') : ''}${ctx.req.param('city') ? '&city=' + ctx.req.param('city') : ''}${ctx.req.param('order') ? '&order=' + ctx.req.param('order') : ''}${ ctx.req.param('recruitType') ? '&recruitType=' + ctx.req.param('recruitType') : '' @@ -50,9 +79,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${ctx.req.param('recruitType') ? (ctx.req.param('recruitType') === '2' ? '社招广场' : '实习广场') : '实习广场'} - 牛客网`, link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/nowcoder/recommend.ts b/lib/routes/nowcoder/recommend.ts index 5cc704c12..d906dc193 100644 --- a/lib/routes/nowcoder/recommend.ts +++ b/lib/routes/nowcoder/recommend.ts @@ -1,13 +1,35 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/recommend', + categories: ['blog'], + example: '/nowcoder/recommend', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['nowcoder.com/'], + }, + name: '求职推荐', + maintainers: ['junfengP'], + handler, +}; + +async function handler() { const link = `https://www.nowcoder.com/recommand/activity?token=&type=3&_=${Date.now()}`; const responseBody = (await got.get(link)).data; if (responseBody.code !== 0) { throw new Error(`接口错误,错误代码:${responseBody.code},错误原因:${responseBody.msg}`); } const data = responseBody.data.activitys; - ctx.set('data', { + return { title: '牛客网-推荐', link: 'https://www.nowcoder.com/recommend', description: '牛客网-推荐', @@ -16,5 +38,5 @@ export default async (ctx) => { description: ``, link: `https://www.nowcoder.com${item.url}`, })), - }); -}; + }; +} diff --git a/lib/routes/nowcoder/schedule.ts b/lib/routes/nowcoder/schedule.ts index 5e2ff6836..c99b9a1ef 100644 --- a/lib/routes/nowcoder/schedule.ts +++ b/lib/routes/nowcoder/schedule.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: '/schedule/:propertyId?/:typeId?', + categories: ['blog'], + example: '/nowcoder/schedule', + parameters: { propertyId: '行业, 在控制台中抓取接口,可获得行业id,默认0', typeId: '类别,同上' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['nowcoder.com/'], + target: '/schedule', + }, + name: '校招日程', + maintainers: ['junfengP'], + handler, +}; + +async function handler(ctx) { const propertyId = ctx.req.param('propertyId') ?? 0; const typeId = ctx.req.param('typeId') ?? 0; @@ -12,7 +35,7 @@ export default async (ctx) => { } const data = responseBody.data.companyList; - ctx.set('data', { + return { title: '名企校招日程', link: 'https://www.nowcoder.com/school/schedule', description: '名企校招日程', @@ -28,5 +51,5 @@ export default async (ctx) => { link: `https://www.nowcoder.com/school/schedule/${item.id}`, }; }), - }); -}; + }; +} diff --git a/lib/routes/npm/package.ts b/lib/routes/npm/package.ts index 3dbc597a4..b9df3b328 100644 --- a/lib/routes/npm/package.ts +++ b/lib/routes/npm/package.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -5,7 +6,14 @@ 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: 'package/:name{(@[a-z0-9-~][a-z0-9-._~]*/)?[a-z0-9-~][a-z0-9-._~]*}', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const name = ctx.req.param('name'); const packageDownloadLastMonthAPI = `https://api.npmjs.org/downloads/point/last-month/${name}`; // 按月统计 const packageDownloadLastWeekAPI = `https://api.npmjs.org/downloads/point/last-week/${name}`; // 按周统计 @@ -25,7 +33,7 @@ export default async (ctx) => { })) .toReversed(); - ctx.set('data', { + return { title: `${name} - npm`, link: `https://www.npmjs.com/package/${name}`, description: `${name} - npm`, @@ -42,5 +50,5 @@ export default async (ctx) => { guid: `https://www.npmjs.com/package/${name}${packageVersion.modified}`, }, ], - }); -}; + }; +} diff --git a/lib/routes/npr/full.ts b/lib/routes/npr/full.ts index 3b6c5f1c3..177b0d3b2 100644 --- a/lib/routes/npr/full.ts +++ b/lib/routes/npr/full.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'; @@ -58,7 +59,25 @@ const getArticleDetail = (link) => return { article, categories }; }); -export default async (ctx) => { +export const route: Route = { + path: '/:endpoint?', + categories: ['bbs'], + example: '/npr/1001', + parameters: { endpoint: 'Channel ID, can be found in Official RSS URL, `1001` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'News', + maintainers: ['bennyyip'], + handler, +}; + +async function handler(ctx) { const endpoint = ctx.req.param('endpoint') || '1001'; const feed = await parser.parseURL(`https://feeds.npr.org/${endpoint}/rss.xml`); @@ -78,11 +97,11 @@ export default async (ctx) => { ) ).filter(Boolean); - ctx.set('data', { + return { title: feed.title, link: feed.link, description: feed.description, icon: 'https://media.npr.org/images/podcasts/primary/npr_generic_image_300.jpg?s=200', item: items, - }); -}; + }; +} diff --git a/lib/routes/ntdtv/channel.ts b/lib/routes/ntdtv/channel.ts index 8aff1a5f9..15b8d3252 100644 --- a/lib/routes/ntdtv/channel.ts +++ b/lib/routes/ntdtv/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.ntdtv.com'; -export default async (ctx) => { +export const route: Route = { + path: '/:language/:id', + categories: ['bbs'], + example: '/ntdtv/b5/prog1201', + parameters: { language: '语言,简体为`gb`,繁体为`b5`', id: '子频道名称' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.ntdtv.com/:language/:id'], + }, + name: '频道', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const language = ctx.req.param('language'); const id = ctx.req.param('id'); const url = `${host}/${language}/${id}`; @@ -37,9 +59,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `新唐人电视台 - ${title}`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/nua/dc.ts b/lib/routes/nua/dc.ts index d0db9ac0d..ed0412eff 100644 --- a/lib/routes/nua/dc.ts +++ b/lib/routes/nua/dc.ts @@ -1,6 +1,28 @@ +import { Route } from '@/types'; import util from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/dc/:type', + categories: ['forecast'], + example: '/nua/dc/news', + parameters: { type: 'News Type' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['dc.nua.edu.cn/:type/list.htm'], + }, + name: 'School of Design', + maintainers: ['evnydd0sf'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); const baseUrl = 'https://dc.nua.edu.cn'; @@ -54,10 +76,10 @@ export default async (ctx) => { const items = await util.ProcessList(baseUrl, baseUrl, listName, listDate, webPageName); const results = await util.ProcessFeed(items[0], artiContent); - ctx.set('data', { + return { title: 'NUA-设计学院-' + items[1], link: baseUrl, description: '南京艺术学院 设计学院 ' + items[1], item: results, - }); -}; + }; +} diff --git a/lib/routes/nua/gra.ts b/lib/routes/nua/gra.ts index 8920d4c0c..e49d5599f 100644 --- a/lib/routes/nua/gra.ts +++ b/lib/routes/nua/gra.ts @@ -1,7 +1,29 @@ +import { Route } from '@/types'; import util from './utils'; const baseUrl = 'https://grad.nua.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/gra/:type', + categories: ['forecast'], + example: '/nua/gra/1959', + parameters: { type: 'News Type' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['grad.nua.edu.cn/:type/list.htm'], + }, + name: 'Graduate Institute', + maintainers: ['evnydd0sf'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); const newsUrl = `${baseUrl}/${type}/list.htm`; const listName = 'li.list_item'; @@ -12,10 +34,10 @@ export default async (ctx) => { const items = await util.ProcessList(newsUrl, baseUrl, listName, listDate, webPageName); const results = await util.ProcessFeed(items[0], artiContent); - ctx.set('data', { + return { title: 'NUA-研究生处-' + items[1], link: `${baseUrl}/${type}/list.htm`, description: '南京艺术学院 研究生处 ' + items[1], item: results, - }); -}; + }; +} diff --git a/lib/routes/nua/index.ts b/lib/routes/nua/index.ts index 2b2ccda47..8632274db 100644 --- a/lib/routes/nua/index.ts +++ b/lib/routes/nua/index.ts @@ -1,6 +1,28 @@ +import { Route } from '@/types'; import util from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/index/:type', + categories: ['forecast'], + example: '/nua/index/346', + parameters: { type: 'News Type' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['index.nua.edu.cn/:type/list.htm'], + }, + name: 'Official Information', + maintainers: ['evnydd0sf'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); const baseUrl = 'https://www.nua.edu.cn'; @@ -13,10 +35,10 @@ export default async (ctx) => { const items = await util.ProcessList(newsUrl, baseUrl, listName, listDate, webPageName); const results = await util.ProcessFeed(items[0], artiContent); - ctx.set('data', { + return { title: 'NUA-' + items[1], link: newsUrl, description: '南京艺术学院 ' + items[1], item: results, - }); -}; + }; +} diff --git a/lib/routes/nua/lib.ts b/lib/routes/nua/lib.ts index 0f6c14561..00dd716ac 100644 --- a/lib/routes/nua/lib.ts +++ b/lib/routes/nua/lib.ts @@ -1,7 +1,29 @@ +import { Route } from '@/types'; import util from './utils'; const baseUrl = 'https://lib.nua.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/lib/:type', + categories: ['forecast'], + example: '/nua/lib/xwdt', + parameters: { type: 'News Type' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['lib.nua.edu.cn/:type/list.htm'], + }, + name: 'Library', + maintainers: ['evnydd0sf'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); let webPageName; @@ -30,10 +52,10 @@ export default async (ctx) => { const items = await util.ProcessList(newsUrl, baseUrl, listName, listDate, webPageName); const results = await util.ProcessFeed(items[0], artiContent); - ctx.set('data', { + return { title: 'NUA-图书馆-' + items[1], link: `${baseUrl}/${type}/list.htm`, description: '南京艺术学院 图书馆 ' + items[1], item: results, - }); -}; + }; +} diff --git a/lib/routes/nua/sxw.ts b/lib/routes/nua/sxw.ts index fd6d5a163..8c260e4a1 100644 --- a/lib/routes/nua/sxw.ts +++ b/lib/routes/nua/sxw.ts @@ -1,6 +1,28 @@ +import { Route } from '@/types'; import util from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/sxw/:type', + categories: ['forecast'], + example: '/nua/sxw/230', + parameters: { type: 'News Type' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['sxw.nua.edu.cn/:type/list.htm'], + }, + name: 'Shuangxing Information', + maintainers: ['evnydd0sf'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); const baseUrl = 'https://sxw.nua.edu.cn'; @@ -13,10 +35,10 @@ export default async (ctx) => { const items = await util.ProcessList(newsUrl, baseUrl, listName, listDate, webPageName); const results = await util.ProcessFeed(items[0], artiContent); - ctx.set('data', { + return { title: 'NUA-双馨网-' + items[1], link: newsUrl, description: '南京艺术学院 双馨网 ' + items[1], item: results, - }); -}; + }; +} diff --git a/lib/routes/nuaa/college/cae.ts b/lib/routes/nuaa/college/cae.ts index f0f5a51ea..4340975d4 100644 --- a/lib/routes/nuaa/college/cae.ts +++ b/lib/routes/nuaa/college/cae.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,14 @@ const map = new Map([ ['dbgg', { title: '答辩公告 | 南京航空航天大学自动化学院', suffix: 'dbgg/list.htm' }], ]); -export default async (ctx) => { +export const route: Route = { + path: '/cae/:type/:getDescription?', + name: 'Unknown', + maintainers: ['Xm798'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); const suffix = map.get(type).suffix; const getDescription = Boolean(ctx.req.param('getDescription')) || false; @@ -71,10 +79,10 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: map.get(type).title, link, description: '南京航空航天大学自动化学院RSS', item: out, - }); -}; + }; +} diff --git a/lib/routes/nuaa/college/cs.ts b/lib/routes/nuaa/college/cs.ts index cfe4731b2..d7a9b46d3 100644 --- a/lib/routes/nuaa/college/cs.ts +++ b/lib/routes/nuaa/college/cs.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 map = new Map([ ['xsgz', { title: '学生工作 | 南京航空航天大学计算机科学与技术学院', suffix: '1959/list.htm' }], ]); -export default async (ctx) => { +export const route: Route = { + path: '/cs/:type/:getDescription?', + categories: ['forecast'], + example: '/nuaa/cs/jxdt', + parameters: { type: '分类名,见下表', getDescription: '是否获取全文' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '计算机科学与技术学院', + maintainers: ['LogicJake', 'Seiry', 'qrzbing', 'Xm798'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); const getDescription = Boolean(ctx.req.param('getDescription')) || false; const suffix = map.get(type).suffix; @@ -70,10 +89,10 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: map.get(type).title, link, description: '南京航空航天大学计算机科学与技术学院RSS', item: out, - }); -}; + }; +} diff --git a/lib/routes/nuaa/jwc/jwc.ts b/lib/routes/nuaa/jwc/jwc.ts index 7bd79335d..7ffef22eb 100644 --- a/lib/routes/nuaa/jwc/jwc.ts +++ b/lib/routes/nuaa/jwc/jwc.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,25 @@ const map = new Map([ ['jxzy', { title: '教学资源 | 南京航空航天大学教务处', suffix: '8233/list.htm' }], ]); -export default async (ctx) => { +export const route: Route = { + path: '/jwc/:type/:getDescription?', + categories: ['forecast'], + example: '/nuaa/jwc/tzgg/getDescription', + parameters: { type: '分类名,见下表', getDescription: '是否获取全文' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '教务处', + maintainers: ['arcosx', 'Seiry', 'qrzbing', 'Xm798'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); const suffix = map.get(type).suffix; const getDescription = Boolean(ctx.req.param('getDescription')) || false; @@ -68,10 +87,10 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: map.get(type).title, link, description: '南京航空航天大学教务处RSS', item: out, - }); -}; + }; +} diff --git a/lib/routes/nuaa/yjsy/yjsy.ts b/lib/routes/nuaa/yjsy/yjsy.ts index e2fa4b759..ec10197c0 100644 --- a/lib/routes/nuaa/yjsy/yjsy.ts +++ b/lib/routes/nuaa/yjsy/yjsy.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,25 @@ const map = new Map([ ['ssfc', { title: '师生风采 | 南京航空航天大学研究生院', suffix: '13278/list.htm' }], ]); -export default async (ctx) => { +export const route: Route = { + path: '/yjsy/:type/:getDescription?', + categories: ['forecast'], + example: '/nuaa/yjsy/tzgg/getDescription', + parameters: { type: '分类名,见下表', getDescription: '是否获取全文' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '研究生院', + maintainers: ['junfengP', 'Seiry', 'Xm798'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); const suffix = map.get(type).suffix; const getDescription = Boolean(ctx.req.param('getDescription')) || false; @@ -66,10 +85,10 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: map.get(type).title, link, description: '南京航空航天大学研究生院RSS', item: out, - }); -}; + }; +} diff --git a/lib/routes/nuist/bulletin.ts b/lib/routes/nuist/bulletin.ts index bc787602c..701864233 100644 --- a/lib/routes/nuist/bulletin.ts +++ b/lib/routes/nuist/bulletin.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; @@ -21,7 +22,29 @@ const map = { qt: '其他', }; -export default async (ctx) => { +export const route: Route = { + path: '/bulletin/:category?', + categories: ['forecast'], + example: '/nuist/bulletin/791', + parameters: { category: '默认为 `791`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bulletin.nuist.edu.cn/:category/list.htm'], + target: '/bulletin/:category', + }, + name: '南信大信息公告栏', + maintainers: ['gylidian'], + handler, +}; + +async function handler(ctx) { const category = Object.hasOwn(map, ctx.req.param('category')) ? ctx.req.param('category') : '791'; const link = `${baseUrl}/${category}/list.htm`; @@ -29,7 +52,7 @@ export default async (ctx) => { const $ = load(response.data); const list = $('.news_list').find('.news'); - ctx.set('data', { + return { title: baseTitle + (category === '791' ? '' : ':' + map[category]), link, item: list @@ -57,5 +80,5 @@ export default async (ctx) => { }; }) .get(), - }); -}; + }; +} diff --git a/lib/routes/nuist/cas.ts b/lib/routes/nuist/cas.ts index 83a87bd24..18726998c 100644 --- a/lib/routes/nuist/cas.ts +++ b/lib/routes/nuist/cas.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,25 @@ import timezone from '@/utils/timezone'; const baseTitle = 'NUIST AS(南信大大气科学学院)'; const baseUrl = 'https://cas.nuist.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/cas/:category?', + categories: ['forecast'], + example: '/nuist/cas/xxgg', + parameters: { category: '默认为信息公告' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'NUIST AS(南信大大气科学学院)', + maintainers: ['gylidian'], + handler, +}; + +async function handler(ctx) { const { category = 'xxgg' } = ctx.req.param(); const link = `${baseUrl}/index/${category}.htm`; @@ -63,10 +82,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: baseTitle + ':' + $('.dqlm').find('a').eq(1).text(), description: $('meta[name=description]').attr('content'), link, item: items, - }); -}; + }; +} diff --git a/lib/routes/nuist/jwc.ts b/lib/routes/nuist/jwc.ts index bfafdb4b4..efde2e51d 100644 --- a/lib/routes/nuist/jwc.ts +++ b/lib/routes/nuist/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,25 @@ import { parseDate } from '@/utils/parse-date'; const baseTitle = '南京信息工程大学-教务处'; const baseUrl = 'https://jwc.nuist.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/jwc/:category?', + categories: ['forecast'], + example: '/nuist/jwc/jxyw', + parameters: { category: '默认为教学要闻' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '教务处', + maintainers: ['gylidian'], + handler, +}; + +async function handler(ctx) { const { category = 'jxyw' } = ctx.req.param(); const link = `${baseUrl}/${category === 'jxyw' || category === 'xyjx' ? 'index' : 'xxtz'}/${category}.htm`; @@ -36,9 +55,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: baseTitle + ':' + $('.dqwz').find('a').eq(1).text(), link, item: items, - }); -}; + }; +} diff --git a/lib/routes/nuist/library/lib.ts b/lib/routes/nuist/library/lib.ts index 9585dba88..d2319def8 100644 --- a/lib/routes/nuist/library/lib.ts +++ b/lib/routes/nuist/library/lib.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,17 @@ import { parseDate } from '@/utils/parse-date'; const baseTitle = '南京信息工程大学图书馆通知'; const baseUrl = 'https://lib.nuist.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/lib', + radar: { + source: ['lib.nuist.edu.cn/', 'lib.nuist.edu.cn/index/tzgg.htm'], + }, + name: 'Unknown', + maintainers: ['gylidian'], + handler, +}; + +async function handler() { const link = baseUrl + '/index/tzgg.htm'; const response = await got(link); @@ -36,9 +47,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: baseTitle, link, item: items, - }); -}; + }; +} diff --git a/lib/routes/nuist/scs.ts b/lib/routes/nuist/scs.ts index b4744aa80..1bcaa97cf 100644 --- a/lib/routes/nuist/scs.ts +++ b/lib/routes/nuist/scs.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 baseTitle = 'NUIST CS(南信大计软院)'; const baseUrl = 'https://scs.nuist.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/scs/:category?', + categories: ['forecast'], + example: '/nuist/scs/xwkx', + parameters: { category: '默认为新闻快讯' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['scs.nuist.edu.cn/:category/list.htm'], + target: '/scs/:category', + }, + name: 'NUIST CS(南信大计软院)', + maintainers: ['gylidian'], + handler, +}; + +async function handler(ctx) { const { category = 'xwkx' } = ctx.req.param(); const link = `${baseUrl}/${category}/list.htm`; @@ -41,10 +64,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: baseTitle + ':' + $('head title').text(), description: $('meta[name=description]').attr('content'), link, item: items, - }); -}; + }; +} diff --git a/lib/routes/nuist/sese.ts b/lib/routes/nuist/sese.ts index aa0b759f0..5942b4057 100644 --- a/lib/routes/nuist/sese.ts +++ b/lib/routes/nuist/sese.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'; const baseTitle = 'NUIST ESE(南信大环科院)'; const baseUrl = 'https://sese.nuist.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/sese/:category?', + categories: ['forecast'], + example: '/nuist/sese/tzgg1', + parameters: { category: '默认为通知公告' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'NUIST ESE(南信大环科院)', + maintainers: ['gylidian'], + handler, +}; + +async function handler(ctx) { const { category = 'tzgg1' } = ctx.req.param(); const link = `${baseUrl}/${category}.htm`; @@ -37,10 +56,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: baseTitle + ':' + $('.lmtitle').text(), description: $('meta[name=description]').attr('content'), link, item: items, - }); -}; + }; +} diff --git a/lib/routes/nuist/xgc.ts b/lib/routes/nuist/xgc.ts index ba30f0181..3ce7cc939 100644 --- a/lib/routes/nuist/xgc.ts +++ b/lib/routes/nuist/xgc.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 baseTitle = '南信大学生工作处'; const baseUrl = 'https://xgc.nuist.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/xgc', + categories: ['forecast'], + example: '/nuist/xgc', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['xgc.nuist.edu.cn/', 'xgc.nuist.edu.cn/419/list.htm'], + }, + name: '南信大学生工作处', + maintainers: ['gylidian'], + handler, +}; + +async function handler() { const link = baseUrl + '/419/list.htm'; const response = await got(link); @@ -42,9 +64,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: baseTitle, link, item: items.filter(Boolean), - }); -}; + }; +} diff --git a/lib/routes/nuist/yjs.ts b/lib/routes/nuist/yjs.ts index 5ee986836..c3306fc48 100644 --- a/lib/routes/nuist/yjs.ts +++ b/lib/routes/nuist/yjs.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: '/yjs/*', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const path = getSubPath(ctx) === '/yjs/' ? 'index/tzgg' : getSubPath(ctx).replace(/^\/yjs\//, ''); const rootUrl = 'https://yjs.nuist.edu.cn'; @@ -54,9 +62,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/nwafu/all.ts b/lib/routes/nwafu/all.ts index d996e6b9f..092890a2c 100644 --- a/lib/routes/nwafu/all.ts +++ b/lib/routes/nwafu/all.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 { nwafuMap } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/:type?', + categories: ['forecast'], + example: '/nwafu/lib', + parameters: { type: '默认为 `jiaowu`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '校园要闻', + maintainers: ['karinido'], + handler, +}; + +async function handler(ctx) { const { type = 'jiaowu' } = ctx.req.param(); const response = await got.get(nwafuMap.get(type)[0]); const $ = load(response.data); @@ -35,10 +54,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: nwafuMap.get(type)[4], link: nwafuMap.get(type)[0], description: nwafuMap.get(type)[4], item: out, - }); -}; + }; +} diff --git a/lib/routes/nyaa/main.ts b/lib/routes/nyaa/main.ts index d5e05e0f2..805d106d6 100644 --- a/lib/routes/nyaa/main.ts +++ b/lib/routes/nyaa/main.ts @@ -1,7 +1,26 @@ +import { Route } from '@/types'; import { config } from '@/config'; import Parser from 'rss-parser'; -export default async (ctx) => { +export const route: Route = { + path: ['/search/:query?', '/user/:username?', '/user/:username/search/:query?', '/sukebei/search/:query?', '/sukebei/user/:username?', '/sukebei/user/:username/search/:query?'], + categories: ['picture'], + example: '/nyaa/search/psycho-pass', + parameters: { query: 'Search keyword' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: true, + supportPodcast: false, + supportScihub: false, + }, + name: 'Search Result', + maintainers: ['Lava-Swimmer', 'noname1776'], + handler, +}; + +async function handler(ctx) { const parser = new Parser({ customFields: { item: ['magnet', ['nyaa:infoHash', 'infoHash']], @@ -36,10 +55,10 @@ export default async (ctx) => { return item; }); - ctx.set('data', { + return { title: feed.title, link: currentLink, description: feed.description, item: feed.items, - }); -}; + }; +} diff --git a/lib/routes/nytimes/book.ts b/lib/routes/nytimes/book.ts index 90dfef9a4..b75b10718 100644 --- a/lib/routes/nytimes/book.ts +++ b/lib/routes/nytimes/book.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; @@ -15,7 +16,29 @@ const categoryList = { 'young-adult-hardcover': '青少年', }; -export default async (ctx) => { +export const route: Route = { + path: '/book/:category?', + categories: ['bbs'], + example: '/nytimes/book/combined-print-and-e-book-nonfiction', + parameters: { category: 'N' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['nytimes.com/'], + target: '', + }, + name: 'Best Seller Books', + maintainers: ['melvinto'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'combined-print-and-e-book-nonfiction'; const url = `https://www.nytimes.com/books/best-sellers/${category}`; @@ -63,9 +86,9 @@ export default async (ctx) => { .get(); } - ctx.set('data', { + return { title: `The New York Times Best Sellers - ${dataTitle}`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/nytimes/daily-briefing-chinese.ts b/lib/routes/nytimes/daily-briefing-chinese.ts index 20764ef42..d8fdbea30 100644 --- a/lib/routes/nytimes/daily-briefing-chinese.ts +++ b/lib/routes/nytimes/daily-briefing-chinese.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: '/daily_briefing_chinese', + categories: ['bbs'], + example: '/nytimes/daily_briefing_chinese', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['nytimes.com/'], + target: '', + }, + name: '新闻简报', + maintainers: ['yueyericardo', 'nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://www.nytimes.com'; const currentUrl = `${rootUrl}/zh-hans/series/daily-briefing-chinese`; @@ -63,9 +86,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '新闻简报 - The New York Times', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/nytimes/index.ts b/lib/routes/nytimes/index.ts index 1f6545b69..7d8532932 100644 --- a/lib/routes/nytimes/index.ts +++ b/lib/routes/nytimes/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import parser from '@/utils/rss-parser'; @@ -5,7 +6,29 @@ import utils from './utils'; import { load } from 'cheerio'; import puppeteer from '@/utils/puppeteer'; -export default async (ctx) => { +export const route: Route = { + path: '/:lang?', + categories: ['bbs'], + example: '/nytimes/dual', + parameters: { lang: 'language, default to Chinese' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['nytimes.com/'], + target: '', + }, + name: 'News', + maintainers: ['HenryQW'], + handler, +}; + +async function handler(ctx) { let { lang = '' } = ctx.req.param(); lang = lang.toLowerCase(); @@ -115,10 +138,10 @@ export default async (ctx) => { browser.close(); - ctx.set('data', { + return { title, link: 'https://cn.nytimes.com', description: title, item: items, - }); -}; + }; +} diff --git a/lib/routes/oceanengine/arithmetic-index.ts b/lib/routes/oceanengine/arithmetic-index.ts index e0d51d7f1..e2f932871 100644 --- a/lib/routes/oceanengine/arithmetic-index.ts +++ b/lib/routes/oceanengine/arithmetic-index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -81,7 +82,14 @@ const createContent = (keyword, queryList, queryListText) => })), }); -export default async (ctx) => { +export const route: Route = { + path: '/index/:keyword/:channel?', + name: 'Unknown', + maintainers: ['Jkker'], + handler, +}; + +async function handler(ctx) { const now = dayjs(); const start_date = now.subtract(DEFAULT_FETCH_DURATION_MONTH, 'month').format('YYYYMMDD'); const end_date = now.format('YYYYMMDD'); @@ -140,11 +148,11 @@ export default async (ctx) => { false ); - ctx.set('data', { + return { title: `${keyword} - ${channelName}指数波峰`, link, description: `巨量算数 - ${channelName}算数指数 | 关键词: ${keyword}`, language: 'zh-cn', item, - }); -}; + }; +} diff --git a/lib/routes/odaily/activity.ts b/lib/routes/odaily/activity.ts index 629c82e4d..fc2dadc98 100644 --- a/lib/routes/odaily/activity.ts +++ b/lib/routes/odaily/activity.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: '/activity', + categories: ['traditional-media'], + example: '/odaily/activity', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['0daily.com/activityPage', '0daily.com/'], + }, + name: '活动', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const currentUrl = `${rootUrl}/service/scheme/group/8?page=1&per_page=${ctx.req.query('limit') ?? 25}`; const response = await got({ @@ -40,9 +62,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '活动 - Odaily星球日报', link: `${rootUrl}/activityPage`, item: items, - }); -}; + }; +} diff --git a/lib/routes/odaily/newsflash.ts b/lib/routes/odaily/newsflash.ts index 8ead5db58..4df8964b3 100644 --- a/lib/routes/odaily/newsflash.ts +++ b/lib/routes/odaily/newsflash.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: '/newsflash', + categories: ['traditional-media'], + example: '/odaily/newsflash', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['0daily.com/newsflash', '0daily.com/'], + }, + name: '快讯', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const currentUrl = `${rootUrl}/api/pp/api/info-flow/newsflash_columns/newsflashes?b_id=&per_page=${ctx.req.query('limit') ?? 100}`; const response = await got({ @@ -18,9 +40,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()}

    Firmware OpenWrt Upgrade: ${$('.firmware_openwrt_upgrade_url').html()}'`, }, ], - }); -}; + }; +} diff --git a/lib/routes/orcid/index.ts b/lib/routes/orcid/index.ts index b9e1758d1..8f6e953cc 100644 --- a/lib/routes/orcid/index.ts +++ b/lib/routes/orcid/index.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: '/:id', + categories: ['journal'], + example: '/orcid/0000-0002-4731-9700', + parameters: { id: 'Open Researcher and Contributor ID' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Works List', + maintainers: ['OrangeEd1t'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const rootUrl = 'https://orcid.org/'; const currentUrl = `${rootUrl}${id}/worksPage.json?offset=0&sort=date&sortAsc=false&pageSize=20`; @@ -47,9 +66,9 @@ export default async (ctx) => { return info; }); - ctx.set('data', { + return { title: 'ORCID Works List' + id, link: currentUrl, item: out, - }); -}; + }; +} diff --git a/lib/routes/oreno3d/main.ts b/lib/routes/oreno3d/main.ts index ef2f5f7fd..76f6c44a4 100644 --- a/lib/routes/oreno3d/main.ts +++ b/lib/routes/oreno3d/main.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -38,7 +39,25 @@ function get_user_url(rootUrl, ctx, sort) { } return userUrl; } -export default async (ctx) => { +export const route: Route = { + path: ['/authors/:authorid/:sort/:pagelimit?', '/characters/:characterid/:sort/:pagelimit?', '/origins/:originid/:sort/:pagelimit?', '/search/:keyword/:sort/:pagelimit?', '/tags/:tagid/:sort/:pagelimit?'], + categories: ['program-update'], + example: '/oreno3d/authors/3189/latest/1', + parameters: { authorid: 'Author id, can be found in URL', sort: 'Sort method, see the table above', pagelimit: 'The maximum number of pages to be crawled, the default is 1' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Author Search', + maintainers: ['xueli_sherryli'], + handler, +}; + +async function handler(ctx) { // 获取视频链接列表及标题 function getLinksTitle(response) { const selector = 'a.box'; @@ -153,9 +172,9 @@ export default async (ctx) => { item: realItem, }; // 生成RSS源码 - ctx.set('data', { + return { title: `${data.title} - ${sortRename[sort]}(Page 1-${pagelimit})`, link: userUrl, item: data.item, - }); -}; + }; +} diff --git a/lib/routes/oschina/news.ts b/lib/routes/oschina/news.ts index 877fd72fe..1bcf4cf1d 100644 --- a/lib/routes/oschina/news.ts +++ b/lib/routes/oschina/news.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate, parseRelativeDate } from '@/utils/parse-date'; @@ -32,7 +33,29 @@ const configs = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/news/:category?', + categories: ['design'], + example: '/oschina/news/project', + parameters: { category: '板块名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['oschina.net/news/:category'], + target: '/news/:category', + }, + name: '资讯', + maintainers: ['tgly307', 'zengxs'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'all'; const config = configs[category]; @@ -87,9 +110,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `开源中国-${config.name}`, link: config.link, item: resultItem, - }); -}; + }; +} diff --git a/lib/routes/oschina/topic.ts b/lib/routes/oschina/topic.ts index c2e1ef675..8364ef39d 100644 --- a/lib/routes/oschina/topic.ts +++ b/lib/routes/oschina/topic.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { parseDate, parseRelativeDate } from '@/utils/parse-date'; @@ -10,7 +11,28 @@ async function loadContent(link) { return content; } -export default async (ctx) => { +export const route: Route = { + path: '/topic/:topic', + categories: ['design'], + example: '/oschina/topic/weekly-news', + parameters: { topic: '主题名,可从 [全部主题](https://www.oschina.net/question/topics) 进入主题页,在 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['oschina.net/question/topic/:topic'], + }, + name: '问答主题', + maintainers: ['loveely7'], + handler, +}; + +async function handler(ctx) { const topic = ctx.req.param('topic'); const topicUrl = `https://www.oschina.net/question/topic/${topic}?show=time`; @@ -45,10 +67,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `开源中国-${topicName}`, description: $('.topic-introduction').text(), link: topicUrl, item: resultItem, - }); -}; + }; +} diff --git a/lib/routes/oschina/user.ts b/lib/routes/oschina/user.ts index 4ab3a3417..f40ce4967 100644 --- a/lib/routes/oschina/user.ts +++ b/lib/routes/oschina/user.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, parseRelativeDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -export default async (ctx) => { +export const route: Route = { + path: ['/u/:uid', '/user/:id'], + categories: ['design'], + example: '/oschina/u/3920392', + parameters: { uid: '用户 id,可通过查看用户博客网址得到,以 u/数字结尾,数字即为 id' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['my.oschina.net/u/:uid'], + }, + name: '数字型账号用户博客', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { id, uid } = ctx.req.param(); const res = id ? await got(`https://my.oschina.net/${id}`) : await got(`https://my.oschina.net/u/${uid}`); const $ = load(res.data); @@ -42,10 +64,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: author + '的博客', description: $('.user-text .user-signature').text(), link: `https://my.oschina.net/${id ?? uid}`, item: resultItem, - }); -}; + }; +} diff --git a/lib/routes/oshwhub/explore.ts b/lib/routes/oshwhub/explore.ts index 41d985cfe..1d9a41dd1 100644 --- a/lib/routes/oshwhub/explore.ts +++ b/lib/routes/oshwhub/explore.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -52,7 +53,25 @@ const requestImages = (url, tryGet) => return { images, boms }; }); -export default async (ctx) => { +export const route: Route = { + path: '/:sortType?', + categories: ['other'], + example: '/oshwhub', + parameters: { sortType: 'sortType' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'OpenSource Square', + maintainers: ['tylinux'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://oshwhub.com'; const sortType = ctx.req.param('sortType') ?? 'updatedTime'; @@ -117,10 +136,10 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title, link: url, description: title, item: items, - }); -}; + }; +} diff --git a/lib/routes/osu/beatmaps/packs.ts b/lib/routes/osu/beatmaps/packs.ts index d36ca5ce2..35c70a9e2 100644 --- a/lib/routes/osu/beatmaps/packs.ts +++ b/lib/routes/osu/beatmaps/packs.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: '/packs/:type?', + categories: ['reading'], + example: '/osu/packs', + parameters: { type: 'pack type, default to `standard`, can choose from `featured`, `tournament`, `loved`, `chart`, `theme` and `artist`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Beatmap Packs', + maintainers: ['JimenezLi'], + handler, +}; + +async function handler(ctx) { const { type = 'standard' } = ctx.req.param(); const link = `https://osu.ppy.sh/beatmaps/packs?type=${type}`; @@ -11,7 +30,7 @@ export default async (ctx) => { const $ = load(response.data); const itemList = $('.beatmap-pack'); - ctx.set('data', { + return { title: `osu! Beatmap Pack - ${type}`, link, item: itemList.toArray().map((element) => { @@ -31,5 +50,5 @@ export default async (ctx) => { author, }; }), - }); -}; + }; +} diff --git a/lib/routes/otobanana/cast.ts b/lib/routes/otobanana/cast.ts index ca0f2ea5c..546caacc6 100644 --- a/lib/routes/otobanana/cast.ts +++ b/lib/routes/otobanana/cast.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { apiBase, baseUrl, getUserInfo, renderCast } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/user/:id/cast', + categories: ['picture'], + example: '/otobanana/user/cee16401-96b1-420f-8188-abd4d33093f1/cast', + parameters: { id: 'User ID, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: true, + supportScihub: false, + }, + radar: { + source: ['otobanana.com/user/:id/cast', 'otobanana.com/user/:id'], + }, + name: 'Cast 音声投稿', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const userInfo = await getUserInfo(id, cache.tryGet); @@ -10,7 +32,7 @@ export default async (ctx) => { const casts = castData.results.map((item) => renderCast(item)); - ctx.set('data', { + return { title: `${userInfo.name} (@${userInfo.username}) - 音声投稿 | OTOBANANA`, description: userInfo.bio.replaceAll('\n', ' '), link: `${baseUrl}/user/${id}`, @@ -21,10 +43,10 @@ export default async (ctx) => { author: userInfo.name, itunes_author: userInfo.name, item: casts, - }); + }; ctx.set('json', { userInfo, castData, }); -}; +} diff --git a/lib/routes/otobanana/livestream.ts b/lib/routes/otobanana/livestream.ts index 2fe9d9ba9..ccd1632aa 100644 --- a/lib/routes/otobanana/livestream.ts +++ b/lib/routes/otobanana/livestream.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { apiBase, baseUrl, getUserInfo, renderLive } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/user/:id/livestream', + categories: ['picture'], + example: '/otobanana/user/cee16401-96b1-420f-8188-abd4d33093f1/livestream', + parameters: { id: 'User ID, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['otobanana.com/user/:id/livestream', 'otobanana.com/user/:id'], + }, + name: 'Livestream ライブ配信', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const userInfo = await getUserInfo(id, cache.tryGet); @@ -10,7 +32,7 @@ export default async (ctx) => { const casts = liveData.results.map((item) => renderLive(item)); - ctx.set('data', { + return { title: `${userInfo.name} (@${userInfo.username}) - ライブ配信 | OTOBANANA`, description: userInfo.bio.replaceAll('\n', ' '), link: `${baseUrl}/user/${id}`, @@ -21,10 +43,10 @@ export default async (ctx) => { author: userInfo.name, itunes_author: userInfo.name, item: casts, - }); + }; ctx.set('json', { userInfo, liveData, }); -}; +} diff --git a/lib/routes/otobanana/timeline.ts b/lib/routes/otobanana/timeline.ts index 946d3e89b..bfd750fd6 100644 --- a/lib/routes/otobanana/timeline.ts +++ b/lib/routes/otobanana/timeline.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { apiBase, baseUrl, getUserInfo, renderPost } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/user/:id', + categories: ['picture'], + example: '/otobanana/user/cee16401-96b1-420f-8188-abd4d33093f1', + parameters: { id: 'User ID, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: true, + supportScihub: false, + }, + radar: { + source: ['otobanana.com/user/:id'], + }, + name: 'Timeline タイムライン', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const userInfo = await getUserInfo(id, cache.tryGet); @@ -10,7 +32,7 @@ export default async (ctx) => { const posts = postData.results.map((item) => renderPost(item)); - ctx.set('data', { + return { title: `${userInfo.name} (@${userInfo.username}) - タイムライン | OTOBANANA`, description: userInfo.bio.replaceAll('\n', ' '), link: `${baseUrl}/user/${id}`, @@ -21,10 +43,10 @@ export default async (ctx) => { author: userInfo.name, itunes_author: userInfo.name, item: posts, - }); + }; ctx.set('json', { userInfo, postData, }); -}; +} diff --git a/lib/routes/ouc/it-postgraduate.ts b/lib/routes/ouc/it-postgraduate.ts index 89f329927..e544e17d9 100644 --- a/lib/routes/ouc/it-postgraduate.ts +++ b/lib/routes/ouc/it-postgraduate.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: '/it/postgraduate', + categories: ['forecast'], + example: '/ouc/it/postgraduate', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['it.ouc.edu.cn/_s381/16619/list.psp', 'it.ouc.edu.cn/16619/list.htm', 'it.ouc.edu.cn/'], + }, + name: '信息科学与工程学院研究生招生通知公告', + maintainers: ['shengmaosu'], + handler, +}; + +async function handler() { const link = 'https://it.ouc.edu.cn/_s381/16619/list.psp'; const response = await got(link); const $ = load(response.data); const list = $('.col_news_list .news_list 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/ouc/it-tx.ts b/lib/routes/ouc/it-tx.ts index bf761ffdc..a13142d19 100644 --- a/lib/routes/ouc/it-tx.ts +++ b/lib/routes/ouc/it-tx.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: '/it/tx/:id?', + categories: ['forecast'], + example: '/ouc/it/tx/xwdt', + parameters: { id: '默认为 `xwdt`,id过多,这里只举几个例' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['it.ouc.edu.cn/tx/:id/list.htm'], + target: '/it/tx/:id', + }, + name: '信息科学与工程学院团学工作', + maintainers: ['3401797899'], + handler, +}; + +async function handler(ctx) { const host = 'https://it.ouc.edu.cn'; const id = ctx.req.param('id') || 'xwdt'; const link = `${host}/tx/${id}/list.htm`; @@ -36,10 +59,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `信息科学与工程学院团学工作 - ${typeTitle}${title === typeTitle ? '' : title}`, description: '中国海洋大学信息科学与工程学院团学工作', link, item: out, - }); -}; + }; +} diff --git a/lib/routes/ouc/it.ts b/lib/routes/ouc/it.ts index 528558e57..0636ae4e0 100644 --- a/lib/routes/ouc/it.ts +++ b/lib/routes/ouc/it.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: '/it/:type?', + categories: ['forecast'], + example: '/ouc/it/0', + parameters: { type: '默认为 `0`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['it.ouc.edu.cn/'], + target: '/it', + }, + name: '信息科学与工程学院', + maintainers: ['GeoffreyChen777', '3401797899'], + handler, +}; + +async function handler(ctx) { const host = 'https://it.ouc.edu.cn'; const typelist = ['学院要闻', '学院公告', '学术活动']; const urlList = ['xyyw/list.htm', 'xygg/list.htm', 'xshd/list.htm']; @@ -36,9 +59,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `中国海洋大学信息科学与工程学院${typelist[type]}`, link, item: out, - }); -}; + }; +} diff --git a/lib/routes/ouc/jwc.ts b/lib/routes/ouc/jwc.ts index ba50ff5b7..52572b56c 100644 --- a/lib/routes/ouc/jwc.ts +++ b/lib/routes/ouc/jwc.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: '/jwc', + categories: ['forecast'], + example: '/ouc/jwc', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['jwc.ouc.edu.cn/', 'jwc.ouc.edu.cn/6517/list.htm'], + }, + name: '教务处', + maintainers: ['3401797899'], + handler, +}; + +async function handler() { const link = 'https://jwc.ouc.edu.cn/6517/list.htm'; const response = await got(link); const $ = load(response.data); @@ -31,10 +53,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '中国海洋大学教务处', link, description: '中国海洋大学教务处最新通知', item: out, - }); -}; + }; +} diff --git a/lib/routes/ouc/jwgl.ts b/lib/routes/ouc/jwgl.ts index 7d181856f..8b5c20b55 100644 --- a/lib/routes/ouc/jwgl.ts +++ b/lib/routes/ouc/jwgl.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: '/jwgl', + categories: ['forecast'], + example: '/ouc/jwgl', + parameters: {}, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['jwgl.ouc.edu.cn/cas/login.action', 'jwgl.ouc.edu.cn/public/SchoolNotice.jsp'], + }, + name: '选课信息教务通知', + maintainers: ['3401797899'], + handler, +}; + +async function handler() { const link = 'http://jwgl.ouc.edu.cn/public/listSchoolNotices.action?currentPage=1&recordsPerPage=15&qtitle='; const response = await got(link); const $ = load(response.data); @@ -34,10 +56,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '中国海洋大学选课信息教务通知', link, description: '中国海洋大学选课信息教务通知', item: out, - }); -}; + }; +} diff --git a/lib/routes/ouc/yjs.ts b/lib/routes/ouc/yjs.ts index 553db0cf9..12fea5226 100644 --- a/lib/routes/ouc/yjs.ts +++ b/lib/routes/ouc/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: '/ouc/yjs', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['yz.ouc.edu.cn/5926/list.htm'], + }, + name: '研究生院', + maintainers: ['shengmaosu'], + handler, +}; + +async function handler() { const link = 'https://yz.ouc.edu.cn/5926/list.htm'; const response = await got(link); const $ = load(response.data); const list = $('.wp_article_list li'); - ctx.set('data', { + return { title: '中国海洋大学研究生院', link, description: '中国海洋大学研究生院通知公告', @@ -23,5 +45,5 @@ export default async (ctx) => { pubDate: parseDate(item.find('.Article_PublishDate').text()), }; }), - }); -}; + }; +} diff --git a/lib/routes/oup/index.ts b/lib/routes/oup/index.ts index 9e5008728..911b3293f 100644 --- a/lib/routes/oup/index.ts +++ b/lib/routes/oup/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 @@ import { art } from '@/utils/render'; const rootUrl = 'https://academic.oup.com'; -export default async (ctx) => { +export const route: Route = { + path: '/journals/:name', + categories: ['finance'], + example: '/oup/journals/adaptation', + parameters: { name: 'short name for a journal, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['academic.oup.com/', 'academic.oup.com/:name/issue'], + }, + name: 'Oxford Academic', + maintainers: [], + handler, +}; + +async function handler(ctx) { const name = ctx.req.param('name'); const url = `${rootUrl}/${name}/issue`; @@ -49,9 +71,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `OUP - ${name}`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/outagereport/index.ts b/lib/routes/outagereport/index.ts index d012344d3..ca935b628 100644 --- a/lib/routes/outagereport/index.ts +++ b/lib/routes/outagereport/index.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import got from '@/utils/got'; const baseUrl = 'https://outage.report/'; -export default async (ctx) => { +export const route: Route = { + path: '/:name/:count?', + categories: ['travel'], + example: '/outagereport/ubisoft/5', + parameters: { name: 'Service name, spelling format must be consistent with URL', count: 'Counting threshold, will only be written in RSS if the number of people who report to stop serving is not less than this number' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Report', + maintainers: ['cxumol', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const serviceName = ctx.req.param('name'); // Which service do you want to monitor? Have to be the same as what it appears in URL. const watchCount = ctx.req.param('count') || 10; // How many reports is received during last 20 minutes? 10 reports as default. const url = `${baseUrl}${serviceName}`; @@ -41,11 +60,11 @@ export default async (ctx) => { } // how this RSS feed looks like - ctx.set('data', { + return { title: `Is ${serviceName} Down Right Now?`, link: url, description: rssDescribe, item: outageHistory, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/panewslab/author.ts b/lib/routes/panewslab/author.ts index 30beb1cc9..c44f20c89 100644 --- a/lib/routes/panewslab/author.ts +++ b/lib/routes/panewslab/author.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: ['/author/:id', '/column/:id'], + categories: ['traditional-media'], + example: '/panewslab/author/166', + parameters: { id: '专栏 id,可在地址栏 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['panewslab.com/'], + }, + name: '专栏', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const rootUrl = 'https://panewslab.com'; @@ -41,9 +63,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `PANews - ${items[0].author}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/panewslab/index.ts b/lib/routes/panewslab/index.ts index 66cc99ef5..e74d4540d 100644 --- a/lib/routes/panewslab/index.ts +++ b/lib/routes/panewslab/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,28 @@ const categories = { 活动: 'zqives', }; -export default async (ctx) => { +export const route: Route = { + path: '/:category?', + categories: ['traditional-media'], + example: '/panewslab', + parameters: { category: '分类,见下表,默认为精选' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['panewslab.com/'], + }, + name: '深度', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? '精选'; const rootUrl = 'https://panewslab.com'; @@ -56,9 +78,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `PANews - ${category}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/panewslab/news.ts b/lib/routes/panewslab/news.ts index 84f648516..c762db9b1 100644 --- a/lib/routes/panewslab/news.ts +++ b/lib/routes/panewslab/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', '/newsflash'], + categories: ['traditional-media'], + example: '/panewslab/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['panewslab.com/'], + }, + name: '快讯', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const rootUrl = 'https://panewslab.com'; const apiUrl = `${rootUrl}/webapi/flashnews?LId=1&Rn=${ctx.req.query('limit') ?? 50}&tw=0`; const currentUrl = `${rootUrl}/zh/news/index.html`; @@ -20,9 +42,9 @@ export default async (ctx) => { category: item.tags, })); - ctx.set('data', { + return { title: 'PANews - 快讯', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/panewslab/topic.ts b/lib/routes/panewslab/topic.ts index 93f7b3949..021da2852 100644 --- a/lib/routes/panewslab/topic.ts +++ b/lib/routes/panewslab/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/:id', + categories: ['traditional-media'], + example: '/panewslab/topic/1629365774078402', + parameters: { id: '专题 id,可在地址栏 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['panewslab.com/'], + }, + name: '专题', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const rootUrl = 'https://panewslab.com'; @@ -41,9 +63,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `PANews - ${id}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/papers/index.ts b/lib/routes/papers/index.ts index 424b0cda8..244788624 100644 --- a/lib/routes/papers/index.ts +++ b/lib/routes/papers/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -7,7 +8,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{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { category = 'arxiv/cs.CL' } = ctx.req.param(); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 150; @@ -67,7 +75,7 @@ export default async (ctx) => { const title = $('title').text(); const icon = new URL('favicon.ico', rootUrl).href; - ctx.set('data', { + return { item: items, title: title.split(/-/)[0].trim(), link: currentUrl, @@ -75,5 +83,5 @@ export default async (ctx) => { icon, logo: icon, subtitle: $('h1').first().text(), - }); -}; + }; +} diff --git a/lib/routes/paradigm/writing.ts b/lib/routes/paradigm/writing.ts index 15ca73957..60b89a8a9 100644 --- a/lib/routes/paradigm/writing.ts +++ b/lib/routes/paradigm/writing.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 baseUrl = 'https://www.paradigm.xyz'; -export default async (ctx) => { +export const route: Route = { + path: '/writing', + categories: ['other'], + example: '/paradigm/writing', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['paradigm.xyz/writing'], + }, + name: 'Writing', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler() { const url = `${baseUrl}/writing`; const response = await got(url); @@ -37,9 +59,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'Paradigm - Writing', link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/parliament/section77.ts b/lib/routes/parliament/section77.ts index 2d65202a1..a6b4b36ce 100644 --- a/lib/routes/parliament/section77.ts +++ b/lib/routes/parliament/section77.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 { CookieJar } from 'tough-cookie'; -export default async (ctx) => { +export const route: Route = { + path: '/section77/:type?', + categories: ['study'], + example: '/parliament/section77', + parameters: { type: 'Type of hearing status, see below' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: "Thailand Parliament Draft of Law's public hearing system", + maintainers: ['itpcc'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://www.parliament.go.th/section77'; const { type = '' } = ctx.req.param(); const cookieJar = new CookieJar(); @@ -159,4 +178,4 @@ export default async (ctx) => { result.item = actListFull; ctx.set('data', result); -}; +} diff --git a/lib/routes/patagonia/new-arrivals.ts b/lib/routes/patagonia/new-arrivals.ts index 6d33ec678..afc918544 100644 --- a/lib/routes/patagonia/new-arrivals.ts +++ b/lib/routes/patagonia/new-arrivals.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -18,7 +19,25 @@ function extractSfrmUrl(url) { urlObj.search = new URLSearchParams({ sfrm: sfrmValue }).toString(); return urlObj.toString(); } -export default async (ctx) => { +export const route: Route = { + path: '/new-arrivals/:category', + categories: ['game'], + example: '/patagonia/new-arrivals/mens', + parameters: { category: 'category, see below' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'New Arrivals', + maintainers: [], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category'); const url = `${host}/on/demandware.store/Sites-patagonia-us-Site/en_US/Search-LazyGrid`; const response = await got({ @@ -50,7 +69,7 @@ export default async (ctx) => { return data; }) .get(); - ctx.set('data', { + return { title: `Patagonia - New Arrivals - ${category.toUpperCase()}`, link: `${host}/shop/${categoryMap[category][1]}`, description: `Patagonia - New Arrivals - ${category.toUpperCase()}`, @@ -60,5 +79,5 @@ export default async (ctx) => { link: item.link, category: item.category, })), - }); -}; + }; +} diff --git a/lib/routes/paulgraham/article.ts b/lib/routes/paulgraham/article.ts index 1899ea143..e099fa94f 100644 --- a/lib/routes/paulgraham/article.ts +++ b/lib/routes/paulgraham/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: ['/articles', '/essays', '/'], + categories: ['programming'], + example: '/paulgraham/articles', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['paulgraham.com/articles.html'], + }, + name: 'Essays', + maintainers: ['Maecenas', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30; const rootUrl = 'http://paulgraham.com'; @@ -47,7 +69,7 @@ export default async (ctx) => { const title = $('title').text(); const icon = $('link[rel="shortcut icon"]').prop('href'); - ctx.set('data', { + return { item: items, title: `${author} - ${title}`, link: currentUrl, @@ -59,5 +81,5 @@ export default async (ctx) => { subtitle: title, author, allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/penguin-random-house/articles.ts b/lib/routes/penguin-random-house/articles.ts index 34f7103a1..c1b4acdc3 100644 --- a/lib/routes/penguin-random-house/articles.ts +++ b/lib/routes/penguin-random-house/articles.ts @@ -1,8 +1,30 @@ +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: '/articles', + categories: ['government'], + example: '/penguin-random-house/articles', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['penguinrandomhouse.com/articles'], + }, + name: 'Articles', + maintainers: ['StevenRCE0'], + handler, +}; + +async function handler(ctx) { const link = 'https://www.penguinrandomhouse.com/articles/'; const res = await got(link); const $ = load(res.data); @@ -18,10 +40,10 @@ export default async (ctx) => { const out = await utils.parseList(itemArray, ctx, utils.parseArticle); - ctx.set('data', { + return { title: 'Penguin Random House Articles', link, description: 'In-depth interviews, author essays, fascinating essays. Go deeper into the books you love.', item: out, - }); -}; + }; +} diff --git a/lib/routes/penguin-random-house/thereaddown.ts b/lib/routes/penguin-random-house/thereaddown.ts index 95e492020..bc27e3f1d 100644 --- a/lib/routes/penguin-random-house/thereaddown.ts +++ b/lib/routes/penguin-random-house/thereaddown.ts @@ -1,8 +1,30 @@ +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: '/the-read-down', + categories: ['government'], + example: '/penguin-random-house/the-read-down', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['penguinrandomhouse.com/the-read-down'], + }, + name: 'Book Lists', + maintainers: ['StevenRCE0'], + handler, +}; + +async function handler(ctx) { const link = 'https://www.penguinrandomhouse.com/the-read-down/'; const res = await got(link); const $ = load(res.data); @@ -18,10 +40,10 @@ export default async (ctx) => { const out = await utils.parseList(itemArray, ctx, utils.parseBooks); - ctx.set('data', { + return { title: 'Penguin Random House Book Lists', link, description: 'Never wonder what to read next! Check out these lists to find your next favorite book.', item: out, - }); -}; + }; +} diff --git a/lib/routes/people/index.ts b/lib/routes/people/index.ts index 363b4bb75..67bb30fa5 100644 --- a/lib/routes/people/index.ts +++ b/lib/routes/people/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,14 @@ 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: '/:site?/:category{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { site = 'www' } = ctx.req.param(); let { category = site === 'www' ? '59476' : '' } = ctx.req.param(); category = site === 'cpc' && category === '24h' ? '87228' : category; @@ -71,9 +79,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/people/liuyan.ts b/lib/routes/people/liuyan.ts index 6a66bc46e..c64cff21c 100644 --- a/lib/routes/people/liuyan.ts +++ b/lib/routes/people/liuyan.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: '/liuyan/:id/:state?', + categories: ['bbs'], + example: '/people/liuyan/539', + parameters: { id: '编号,可在对应人物页 URL 中找到', state: '状态,见下表,默认为全部' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['liuyan.people.com.cn/'], + }, + name: '领导留言板', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const fid = ctx.req.param('id'); const state = ctx.req.param('state') ?? '1'; @@ -47,9 +69,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${currentForum} - 领导留言板 - 人民网`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/people/xjpjh.ts b/lib/routes/people/xjpjh.ts index 8a7bbe7ce..bffeb68bb 100644 --- a/lib/routes/people/xjpjh.ts +++ b/lib/routes/people/xjpjh.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 * as url from 'node:url'; const host = 'http://jhsjk.people.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/xjpjh/:keyword?/:year?', + categories: ['bbs'], + example: '/people/xjpjh', + parameters: { keyword: '关键词,默认不填', year: '年份,默认 all' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['people.com.cn/'], + target: '/:site?/:category?', + }, + name: '习近平系列重要讲话', + maintainers: [], + handler, +}; + +async function handler(ctx) { let keyword = ctx.req.param('keyword') || 'all'; let year = ctx.req.param('year') || 0; @@ -61,9 +84,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title, link, item: out, - }); -}; + }; +} diff --git a/lib/routes/peopo/topic.ts b/lib/routes/peopo/topic.ts index 485a8765f..da2686be9 100644 --- a/lib/routes/peopo/topic.ts +++ b/lib/routes/peopo/topic.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'; const baseUrl = 'https://www.peopo.org'; -export default async (ctx) => { +export const route: Route = { + path: '/topic/:topicId?', + categories: ['traditional-media'], + example: '/peopo/topic/159', + parameters: { topicId: '分類 ID,見下表,默認為社會關懷' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['peopo.org/topic/:topicId'], + target: '/topic/:topicId', + }, + name: '新聞分類', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { topicId = '159' } = ctx.req.param(); const url = `${baseUrl}/topic/${topicId}`; const response = await got(url); @@ -38,10 +61,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), link: url, language: 'zh-TW', item: items, - }); -}; + }; +} diff --git a/lib/routes/phoronix/index.ts b/lib/routes/phoronix/index.ts index be09d30bd..27652a20f 100644 --- a/lib/routes/phoronix/index.ts +++ b/lib/routes/phoronix/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import parser from '@/utils/rss-parser'; import { load } from 'cheerio'; @@ -111,7 +112,31 @@ const tryFetch = async (category, topic) => { return feed; }; -export default async (ctx) => { +export const route: Route = { + path: '/:category?/:topic?', + categories: ['traditional-media'], + example: '/phoronix/linux/KDE', + parameters: { + category: 'Category', + topic: 'Topic. You may find available parameters from their navigator links. E.g. to subscribe to `https://www.phoronix.com/reviews/Operating+Systems`, fill in the path `/phoronix/reviews/Operating+Systems`', + }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['phoronix.com/:category?/:topic?'], + }, + name: 'News & Reviews', + maintainers: ['oppliate', 'Rongronggg9'], + handler, +}; + +async function handler(ctx) { const { category, topic } = ctx.req.param(); let feed; switch (category) { @@ -234,4 +259,4 @@ export default async (ctx) => { ); ctx.set('data', feed); -}; +} diff --git a/lib/routes/pianyuan/app.ts b/lib/routes/pianyuan/app.ts index d19a24077..1dcbe6fb3 100644 --- a/lib/routes/pianyuan/app.ts +++ b/lib/routes/pianyuan/app.ts @@ -1,8 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { load } from 'cheerio'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/index/:media?', + categories: ['picture'], + example: '/pianyuan/index', + parameters: { media: '类别,见下表,默认为首页' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['pianyuan.org/'], + target: '/index', + }, + name: '最新资源', + maintainers: ['greatcodeeer', 'jerry1119'], + handler, +}; + +async function handler(ctx) { const media = ctx.req.param('media') ?? -1; const link_base = 'https://pianyuan.org/'; let description = '电影和剧集'; @@ -26,10 +49,10 @@ export default async (ctx) => { detailLinks.shift(); const items = await utils.ProcessFeed(detailLinks, cache); - ctx.set('data', { + return { title: '片源网', description, link: link_base, item: items, - }); -}; + }; +} diff --git a/lib/routes/pianyuan/search.ts b/lib/routes/pianyuan/search.ts index 546fb8c31..fc098c0fc 100644 --- a/lib/routes/pianyuan/search.ts +++ b/lib/routes/pianyuan/search.ts @@ -1,8 +1,20 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { load } from 'cheerio'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/indexers/pianyuan/results/search/api', + radar: { + source: ['pianyuan.org/'], + target: '/index', + }, + name: 'Unknown', + maintainers: ['jerry1119'], + handler, +}; + +async function handler(ctx) { const link_base = 'https://pianyuan.org/'; const description = '搜索'; // 适配jackett 搜索api, eg: https://rsshub.app/pianyuan/indexers/pianyuan/results/search/api?t=test&q=halo @@ -37,10 +49,10 @@ export default async (ctx) => { const items = await utils.ProcessFeed(detailLinks, cache); - ctx.set('data', { + return { title: '片源网', description, link: link_base, item: items, - }); -}; + }; +} diff --git a/lib/routes/picnob/user.ts b/lib/routes/picnob/user.ts index 4e1b4c5fd..7e3190533 100644 --- a/lib/routes/picnob/user.ts +++ b/lib/routes/picnob/user.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -10,7 +11,17 @@ import * as path from 'node:path'; import { puppeteerGet } from './utils'; import puppeteer from '@/utils/puppeteer'; -export default async (ctx) => { +export const route: Route = { + path: '/user/:id', + radar: { + source: ['picnob.com/profile/:id/*'], + }, + name: 'Unknown', + maintainers: ['TonyRL', 'micheal-death'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://www.picnob.com'; const id = ctx.req.param('id'); const url = `${baseUrl}/profile/${id}/`; @@ -93,11 +104,11 @@ export default async (ctx) => { ); await browser.close(); - ctx.set('data', { + return { title: `${profileName} (@${id}) - Picnob`, description: $('.info .sum').text(), link: url, image: $('.ava .pic img').attr('src'), item: list, - }); -}; + }; +} diff --git a/lib/routes/picuki/profile.ts b/lib/routes/picuki/profile.ts index 75aa881bb..aa6f5cbc0 100644 --- a/lib/routes/picuki/profile.ts +++ b/lib/routes/picuki/profile.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -23,7 +24,29 @@ function deVideo(media) { return media_deVideo; } -export default async (ctx) => { +export const route: Route = { + path: '/profile/:id/:functionalFlag?', + categories: ['new-media'], + example: '/picuki/profile/stefaniejoosten', + parameters: { id: 'Instagram id', functionalFlag: 'functional flag, see the table below' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.picuki.com/profile/:id'], + target: '/profile/:id', + }, + name: 'User Profile - Picuki', + maintainers: ['hoilc', 'Rongronggg9', 'devinmugen'], + handler, +}; + +async function handler(ctx) { // use Puppeteer due to the obstacle by cloudflare challenge const browser = await puppeteer(); @@ -166,11 +189,11 @@ export default async (ctx) => { await browser.close(); - ctx.set('data', { + return { title: `${profileName} (@${id}) - Picuki`, link: profileUrl, image: profileImg, description: profileDescription, item: items, - }); -}; + }; +} diff --git a/lib/routes/pikabu/community.ts b/lib/routes/pikabu/community.ts index 62810917a..e656943ac 100644 --- a/lib/routes/pikabu/community.ts +++ b/lib/routes/pikabu/community.ts @@ -1,9 +1,17 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import { baseUrl, fixImage, fixVideo } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/:type/:name', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { type, name, sort = 'new' } = ctx.req.param(); const sortString = sort === 'default' || type === 'tag' ? '' : `/${sort}`; const { data: response } = await got(`${baseUrl}/ajax/${type}/${name}${sortString}`); @@ -28,10 +36,10 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: response.data.title, link: `${baseUrl}/${type}/${name}`, language: 'ru-RU', item: items, - }); -}; + }; +} diff --git a/lib/routes/pikabu/user.ts b/lib/routes/pikabu/user.ts index 04cd49f6c..0e73e11b8 100644 --- a/lib/routes/pikabu/user.ts +++ b/lib/routes/pikabu/user.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 { baseUrl, fixImage, fixVideo } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/user/:name', + categories: ['blog'], + example: '/pikabu/user/@bula.dragon', + parameters: { name: 'User name' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['pikabu.ru/:name'], + }, + name: 'User', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const name = ctx.req.param('name'); const link = `${baseUrl}/${name}`; const response = await got(link, { @@ -35,12 +57,12 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('meta[property="og:title"]').attr('content'), description: $('.profile__user-about-content').text(), image: $('meta[property="og:image"]').attr('content'), language: 'ru-RU', link, item: items, - }); -}; + }; +} diff --git a/lib/routes/pincong/hot.ts b/lib/routes/pincong/hot.ts index cb6e978f4..38c64b9aa 100644 --- a/lib/routes/pincong/hot.ts +++ b/lib/routes/pincong/hot.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import { baseUrl, puppeteerGet } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/hot/:category?', + categories: ['blog'], + example: '/pincong/hot', + parameters: { category: '分类,与官网分类 URL `category-` 后的数字对应,默认为全部' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '精选', + maintainers: ['zphw'], + handler, +}; + +async function handler(ctx) { const { category = '0' } = ctx.req.param(); const url = `${baseUrl}/hot/list/category-${category}`; @@ -14,7 +33,7 @@ export default async (ctx) => { const $ = load(html); const list = $('div.aw-item'); - ctx.set('data', { + return { title: '品葱 - 精选', link: `${baseUrl}/hot/${category === '0' ? '' : `category-${category}`}`, item: list @@ -25,5 +44,5 @@ export default async (ctx) => { pubDate: parseDate($(item).find('div.mod-footer .aw-small-text').text()), })) .get(), - }); -}; + }; +} diff --git a/lib/routes/pincong/index.ts b/lib/routes/pincong/index.ts index 957f50c71..836cfca1f 100644 --- a/lib/routes/pincong/index.ts +++ b/lib/routes/pincong/index.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import { baseUrl, puppeteerGet } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/category/:category?/:sort?', + categories: ['blog'], + example: '/pincong/category/1/new', + parameters: { category: '分类,与官网分类 URL `category-` 后的数字对应,默认为全部', sort: '排序方式,参数可见下表,默认为推荐' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '发现', + maintainers: ['zphw'], + handler, +}; + +async function handler(ctx) { let url = `${baseUrl}/`; const sortMap = { @@ -21,7 +40,7 @@ export default async (ctx) => { const $ = load(html); const list = $('div.aw-item'); - ctx.set('data', { + return { title: '品葱 - 发现', link: url, item: list @@ -31,5 +50,5 @@ export default async (ctx) => { pubDate: parseDate($(item).attr('data-created-at') * 1000), })) .get(), - }); -}; + }; +} diff --git a/lib/routes/pincong/topic.ts b/lib/routes/pincong/topic.ts index ab367dcd7..51e92d7b8 100644 --- a/lib/routes/pincong/topic.ts +++ b/lib/routes/pincong/topic.ts @@ -1,9 +1,20 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import { baseUrl, puppeteerGet } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/topic/:topic', + radar: { + source: ['pincong.rocks/topic/:topic'], + }, + name: 'Unknown', + maintainers: ['zphw'], + handler, +}; + +async function handler(ctx) { const url = `${baseUrl}/topic/${ctx.req.param('topic')}`; // use Puppeteer due to the obstacle by cloudflare challenge @@ -12,7 +23,7 @@ export default async (ctx) => { const $ = load(html); const list = $('div.aw-item'); - ctx.set('data', { + return { title: `品葱 - ${ctx.req.param('topic')}`, link: url, item: list @@ -22,5 +33,5 @@ export default async (ctx) => { pubDate: parseDate($(item).attr('data-created-at') * 1000), })) .get(), - }); -}; + }; +} diff --git a/lib/routes/pingwest/status.ts b/lib/routes/pingwest/status.ts index 8ae9e6487..5cc93d864 100644 --- a/lib/routes/pingwest/status.ts +++ b/lib/routes/pingwest/status.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: '/status', + categories: ['traditional-media'], + example: '/pingwest/status', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['pingwest.com/status', 'pingwest.com/'], + }, + name: '实时要闻', + maintainers: ['sanmmm'], + handler, +}; + +async function handler() { const baseUrl = 'https://www.pingwest.com'; const url = `${baseUrl}/api/state/list`; const response = await got(url, { @@ -38,10 +60,10 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: '品玩 - 实时要闻', description: '品玩 - 实时要闻', link: `${baseUrl}/status`, item: items, - }); -}; + }; +} diff --git a/lib/routes/pingwest/tag.ts b/lib/routes/pingwest/tag.ts index 35ba7ca3a..936d3737a 100644 --- a/lib/routes/pingwest/tag.ts +++ b/lib/routes/pingwest/tag.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/tag/:tag/:type/:option?', + categories: ['traditional-media'], + example: '/pingwest/tag/ChinaJoy/1', + parameters: { tag: '话题名或话题id, 可从话题页url中得到', type: '内容类型', option: '参数, 默认无' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '话题动态', + maintainers: ['sanmmm'], + handler, +}; + +async function handler(ctx) { const { tag, type, option } = ctx.req.param(); const needFullText = option === 'fulltext'; @@ -35,10 +54,10 @@ export default async (ctx) => { const $ = load(response.data.data.list); const items = await utils.articleListParser($, needFullText, cache); - ctx.set('data', { + return { title: `品玩 - ${tagName}`, description: `品玩 - ${tagName}`, link: tagUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/pingwest/user.ts b/lib/routes/pingwest/user.ts index 43f599951..235f21c17 100644 --- a/lib/routes/pingwest/user.ts +++ b/lib/routes/pingwest/user.ts @@ -1,9 +1,32 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import utils from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/user/:uid/:type?/:option?', + categories: ['traditional-media'], + example: '/pingwest/user/7781550877/article', + parameters: { uid: '用户id, 可从用户主页中得到', type: '内容类型, 默认为`article`', option: '参数' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['pingwest.com/user/:uid/:type', 'pingwest.com/'], + target: '/user/:uid/:type', + }, + name: '用户', + maintainers: ['sanmmm'], + handler, +}; + +async function handler(ctx) { const { uid, type = 'article', option } = ctx.req.param(); const baseUrl = 'https://www.pingwest.com'; const aimUrl = `${baseUrl}/user/${uid}/${type}`; @@ -50,11 +73,11 @@ export default async (ctx) => { article: '文章', state: '动态', }; - ctx.set('data', { + return { title: `品玩 - ${userName} - ${typeToLabel[type]}`, description: userSign, image: userAvatar, link: aimUrl, item, - }); -}; + }; +} diff --git a/lib/routes/pixabay/search.ts b/lib/routes/pixabay/search.ts index 136cc11e2..3ef81e2b4 100644 --- a/lib/routes/pixabay/search.ts +++ b/lib/routes/pixabay/search.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 { config } from '@/config'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/search/:q/:order?', + categories: ['anime'], + example: '/pixabay/search/cat', + parameters: { q: 'Search term', order: 'Order, `popular` or `latest`, `latest` by default' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['pixabay.com/:searchType/search/:q'], + target: '/search/:q', + }, + name: 'Search', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { q, order = 'latest' } = ctx.req.param(); const key = config.pixabay?.key ?? '7329690-bbadad6d872ba577d5a358679'; const baseUrl = 'https://pixabay.com'; @@ -43,12 +66,12 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `Search ${q} - Pixabay`, description: 'Download & use free nature stock photos in high resolution ✓ New free images everyday ✓ HD to 4K ✓ Best nature pictures for all devices on Pixabay', link: `${baseUrl}/images/search/${q}/${order === 'latest' ? '?order=latest' : ''}`, image: `https://pixabay.com/apple-touch-icon.png`, language: 'en', item: items, - }); -}; + }; +} diff --git a/lib/routes/pixiv/bookmarks.ts b/lib/routes/pixiv/bookmarks.ts index e3a4e9a24..55c2c8f06 100644 --- a/lib/routes/pixiv/bookmarks.ts +++ b/lib/routes/pixiv/bookmarks.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { getToken } from './token'; import getBookmarks from './api/get-bookmarks'; @@ -6,7 +7,28 @@ import { config } from '@/config'; import pixivUtils from './utils'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/user/bookmarks/:id', + categories: ['new-media'], + example: '/pixiv/user/bookmarks/15288095', + parameters: { id: "user id, available in user's homepage URL" }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.pixiv.net/users/:id/bookmarks/artworks'], + }, + name: 'User Bookmark', + maintainers: ['EYHN'], + handler, +}; + +async function handler(ctx) { if (!config.pixiv || !config.pixiv.refreshToken) { throw new Error('pixiv RSS is disabled due to the lack of relevant config'); } @@ -23,7 +45,7 @@ export default async (ctx) => { const illusts = bookmarksResponse.data.illusts; const username = userDetailResponse.data.user.name; - ctx.set('data', { + return { title: `${username} 的收藏`, link: `https://www.pixiv.net/users/${id}/bookmarks/artworks`, description: `${username} 的 pixiv 最新收藏`, @@ -37,5 +59,5 @@ export default async (ctx) => { link: `https://www.pixiv.net/artworks/${illust.id}`, }; }), - }); -}; + }; +} diff --git a/lib/routes/pixiv/illustfollow.ts b/lib/routes/pixiv/illustfollow.ts index 09589648d..63f9bdb47 100644 --- a/lib/routes/pixiv/illustfollow.ts +++ b/lib/routes/pixiv/illustfollow.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { getToken } from './token'; import getIllustFollows from './api/get-illust-follows'; @@ -5,7 +6,28 @@ import { config } from '@/config'; import pixivUtils from './utils'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/user/illustfollows', + categories: ['new-media'], + example: '/pixiv/user/illustfollows', + parameters: {}, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.pixiv.net/bookmark_new_illust.php'], + }, + name: 'Following timeline', + maintainers: ['ClarkeCheng'], + handler, +}; + +async function handler() { if (!config.pixiv || !config.pixiv.refreshToken) { throw new Error('pixiv RSS is disabled due to the lack of relevant config'); } @@ -17,7 +39,7 @@ export default async (ctx) => { const response = await getIllustFollows(token); const illusts = response.data.illusts; - ctx.set('data', { + return { title: `Pixiv关注的新作品`, link: 'https://www.pixiv.net/bookmark_new_illust.php', description: `Pixiv关注的画师们的最新作品`, @@ -31,5 +53,5 @@ export default async (ctx) => { link: `https://www.pixiv.net/artworks/${illust.id}`, }; }), - }); -}; + }; +} diff --git a/lib/routes/pixiv/novels.ts b/lib/routes/pixiv/novels.ts index e68638645..42cfb20c1 100644 --- a/lib/routes/pixiv/novels.ts +++ b/lib/routes/pixiv/novels.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; const baseUrl = 'https://www.pixiv.net'; -export default async (ctx) => { +export const route: Route = { + path: '/user/novels/:id', + categories: ['new-media'], + example: '/pixiv/user/novels/27104704', + parameters: { id: "User id, available in user's homepage URL" }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.pixiv.net/users/:id/novels'], + }, + name: 'User Novels', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const { limit = 100 } = ctx.req.query(); const url = `${baseUrl}/users/${id}/novels`; @@ -37,11 +59,11 @@ export default async (ctx) => { category: item.tags, })); - ctx.set('data', { + return { title: data.body.extraData.meta.title, description: data.body.extraData.meta.ogp.description, image: Object.values(data.body.works)[0].profileImageUrl, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/pixiv/ranking.ts b/lib/routes/pixiv/ranking.ts index c8d39cfbd..6719f5b6a 100644 --- a/lib/routes/pixiv/ranking.ts +++ b/lib/routes/pixiv/ranking.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { getToken } from './token'; import getRanking from './api/get-ranking'; @@ -56,7 +57,25 @@ const alias = { r18g: 'week_r18g', }; -export default async (ctx) => { +export const route: Route = { + path: '/ranking/:mode/:date?', + categories: ['new-media'], + example: '/pixiv/ranking/week', + parameters: { mode: 'rank type', date: 'format: `2018-4-25`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Rankings', + maintainers: ['EYHN'], + handler, +}; + +async function handler(ctx) { if (!config.pixiv || !config.pixiv.refreshToken) { throw new Error('pixiv RSS is disabled due to the lack of relevant config'); } @@ -75,7 +94,7 @@ export default async (ctx) => { const dateStr = `${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}日 `; - ctx.set('data', { + return { title: (ctx.req.param('date') ? dateStr : '') + titles[mode], link: links[mode], description: dateStr + titles[mode], @@ -90,5 +109,5 @@ export default async (ctx) => { category: illust.tags.map((tag) => tag.name), }; }), - }); -}; + }; +} diff --git a/lib/routes/pixiv/search.ts b/lib/routes/pixiv/search.ts index f04cdca6f..1dd89dd50 100644 --- a/lib/routes/pixiv/search.ts +++ b/lib/routes/pixiv/search.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { getToken } from './token'; import searchPopularIllust from './api/search-popular-illust'; @@ -6,7 +7,25 @@ import { config } from '@/config'; import pixivUtils from './utils'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/search/:keyword/:order?/:mode?', + categories: ['new-media'], + example: '/pixiv/search/Nezuko/popular/2', + parameters: { keyword: 'keyword', order: 'rank mode, empty or other for time order, popular for popular order', mode: 'filte R18 content' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Keyword', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { if (!config.pixiv || !config.pixiv.refreshToken) { throw new Error('pixiv RSS is disabled due to the lack of relevant config'); } @@ -29,7 +48,7 @@ export default async (ctx) => { illusts = illusts.filter((item) => item.x_restrict === 1); } - ctx.set('data', { + return { title: `${keyword} 的 pixiv ${order === 'popular' ? '热门' : ''}内容`, link: `https://www.pixiv.net/tags/${keyword}/artworks`, item: illusts.map((illust) => { @@ -43,5 +62,5 @@ export default async (ctx) => { }; }), allowEmpty: true, - }); -}; + }; +} diff --git a/lib/routes/pixiv/user.ts b/lib/routes/pixiv/user.ts index 27c03e5ad..9378675bb 100644 --- a/lib/routes/pixiv/user.ts +++ b/lib/routes/pixiv/user.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { getToken } from './token'; import getIllusts from './api/get-illusts'; @@ -5,7 +6,28 @@ import { config } from '@/config'; import pixivUtils from './utils'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/user/:id', + categories: ['new-media'], + example: '/pixiv/user/15288095', + parameters: { id: "user id, available in user's homepage URL" }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.pixiv.net/users/:id'], + }, + name: 'User Activity', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { if (!config.pixiv || !config.pixiv.refreshToken) { throw new Error('pixiv RSS is disabled due to the lack of relevant config'); } @@ -21,7 +43,7 @@ export default async (ctx) => { const illusts = response.data.illusts; const username = illusts[0].user.name; - ctx.set('data', { + return { title: `${username} 的 pixiv 动态`, link: `https://www.pixiv.net/users/${id}`, description: `${username} 的 pixiv 最新动态`, @@ -36,5 +58,5 @@ export default async (ctx) => { category: illust.tags.map((t) => t.name), }; }), - }); -}; + }; +} diff --git a/lib/routes/piyao/jrpy.ts b/lib/routes/piyao/jrpy.ts index 36584bd4c..fcced6fb6 100644 --- a/lib/routes/piyao/jrpy.ts +++ b/lib/routes/piyao/jrpy.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://www.piyao.org.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/jrpy', + categories: ['other'], + example: '/piyao/jrpy', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['piyao.org.cn/jrpy/index.htm'], + }, + name: '今日辟谣', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler() { const currentUrl = `${rootUrl}/jrpy/index.htm`; const response = await got(currentUrl); @@ -32,9 +54,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '今日辟谣', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/pkmer/recent.ts b/lib/routes/pkmer/recent.ts index 000c03b4c..0dad5af71 100644 --- a/lib/routes/pkmer/recent.ts +++ b/lib/routes/pkmer/recent.ts @@ -1,22 +1,44 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; const baseUrl = 'https://pkmer.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/recent', + categories: ['blog'], + example: '/pkmer/recent', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['pkmer.cn/page/*'], + }, + name: '最近更新', + maintainers: ['Gnoyong'], + handler, +}; + +async function handler() { const { data: response } = await got(`${baseUrl}/page/1/`); const $ = load(response); const items = process($); - ctx.set('data', { + return { title: 'PKMer', icon: 'https://cdn.pkmer.cn/covers/logo.png!nomark', logo: 'https://cdn.pkmer.cn/covers/logo.png!nomark', link: baseUrl, allowEmpty: true, item: items, - }); -}; + }; +} function process($) { const container = $('#pages > div.grid > .relative'); diff --git a/lib/routes/pku/bbs/hot.ts b/lib/routes/pku/bbs/hot.ts index 2c15a1355..67ef35820 100644 --- a/lib/routes/pku/bbs/hot.ts +++ b/lib/routes/pku/bbs/hot.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { config } from '@/config'; import { load } from 'cheerio'; @@ -5,7 +6,28 @@ 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: '/bbs/hot', + categories: ['forecast'], + example: '/pku/bbs/hot', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bbs.pku.edu.cn/v2/hot-topic.php', 'bbs.pku.edu.cn/'], + }, + name: '北大未名 BBS 全站十大', + maintainers: ['wooddance'], + handler, +}; + +async function handler() { const cookie = config.pkubbs.cookie; const headers = {}; if (cookie) { @@ -48,10 +70,10 @@ export default async (ctx) => { }) ) ); - ctx.set('data', { + return { title: '北大未名BBS 全站十大', link: 'https://bbs.pku.edu.cn/v2/hot-topic.php', description: '北大未名BBS 全站热门话题前十名', item, - }); -}; + }; +} diff --git a/lib/routes/pku/cls/announcement.ts b/lib/routes/pku/cls/announcement.ts index cf54e6407..1fb1938ba 100644 --- a/lib/routes/pku/cls/announcement.ts +++ b/lib/routes/pku/cls/announcement.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 homeUrl = 'https://bio.pku.edu.cn/homes/Index/news/21/21.html'; const baseUrl = 'https://bio.pku.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/cls/announcement', + categories: ['forecast'], + example: '/pku/cls/announcement', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bio.pku.edu.cn/homes/Index/news/21/21.html', 'bio.pku.edu.cn/'], + }, + name: '生命科学学院通知公告', + maintainers: ['william-swl'], + handler, +}; + +async function handler() { const response = await got(homeUrl); const $ = load(response.data); @@ -35,9 +57,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '北京大学生命科学学院通知公告', link: homeUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/pku/cls/lecture.ts b/lib/routes/pku/cls/lecture.ts index 511d4da78..ee70c647d 100644 --- a/lib/routes/pku/cls/lecture.ts +++ b/lib/routes/pku/cls/lecture.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,11 +6,32 @@ import { parseDate } from '@/utils/parse-date'; const homeUrl = 'http://bio.pku.edu.cn/homes/Index/news_jz/7/7.html'; const baseUrl = 'http://bio.pku.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/cls/lecture', + categories: ['forecast'], + example: '/pku/cls/lecture', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['bio.pku.edu.cn/homes/Index/news_jz/7/7.html', 'bio.pku.edu.cn/'], + }, + name: '生命科学学院近期讲座', + maintainers: ['TPOB'], + handler, +}; + +async function handler() { const response = await got(homeUrl); const $ = load(response.data); - ctx.set('data', { + return { title: `北京大学生命科学学院近期讲座`, link: homeUrl, description: `北京大学生命科学学院近期讲座`, @@ -21,5 +43,5 @@ export default async (ctx) => { link: baseUrl + $('a.clearfix').attr('href'), })) .get(), - }); -}; + }; +} diff --git a/lib/routes/pku/eecs.ts b/lib/routes/pku/eecs.ts index d74abbf6a..2d336cd33 100644 --- a/lib/routes/pku/eecs.ts +++ b/lib/routes/pku/eecs.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 { eecsMap } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/eecs/:type?', + name: 'Unknown', + maintainers: ['Ir1d'], + handler, +}; + +async function handler(ctx) { const host = 'https://eecs.pku.edu.cn'; let type = ctx.params && Number.parseInt(ctx.req.param('type')); @@ -56,10 +64,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: host + '/xygk1/ggtz/' + eecsMap.get(type), description: '北大信科 公告通知', item: items, - }); -}; + }; +} diff --git a/lib/routes/pku/hr.ts b/lib/routes/pku/hr.ts index 93dbd5750..ef55e63c5 100644 --- a/lib/routes/pku/hr.ts +++ b/lib/routes/pku/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: '/pku/hr', + parameters: { category: '分类,见下方说明,默认为首页最新公告' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['hr.pku.edu.cn/'], + }, + name: '人事处', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category')?.replace(/-/g, '/') ?? 'zxgg'; const rootUrl = 'https://hr.pku.edu.cn/'; @@ -47,9 +69,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('h2').text()} - ${$('title').text()}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/pku/nsd.ts b/lib/routes/pku/nsd.ts index dfe720b83..b41392ba3 100644 --- a/lib/routes/pku/nsd.ts +++ b/lib/routes/pku/nsd.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,28 @@ const pageType = (href) => { } }; -export default async (ctx) => { +export const route: Route = { + path: '/nsd/gd', + categories: ['forecast'], + example: '/pku/nsd/gd', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['nsd.pku.edu.cn/'], + }, + name: '观点 - 国家发展研究院', + maintainers: ['MisLink'], + handler, +}; + +async function handler() { const response = await got({ url: baseUrl, https: { rejectUnauthorized: false } }); const $ = load(response.data); @@ -62,9 +84,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: '观点 - 北京大学国家发展研究院', link: baseUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/pku/pkuyjs.ts b/lib/routes/pku/pkuyjs.ts index 7b109b8f1..bc278a6e0 100644 --- a/lib/routes/pku/pkuyjs.ts +++ b/lib/routes/pku/pkuyjs.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: '/admission/sszs', + categories: ['forecast'], + example: '/pku/admission/sszs', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['admission.pku.edu.cn/zsxx/sszs/index.htm', 'admission.pku.edu.cn/'], + }, + name: '研究生招生网', + maintainers: ['pkuyjs'], + handler, +}; + +async function handler() { const link = 'https://admission.pku.edu.cn/zsxx/sszs/index.htm'; const response = await got(link); const $ = load(response.data); const list = $('.zsxx_cont_list li'); - ctx.set('data', { + return { title: `${$('.twostage_title_C').text()} - ${$('title').text()}`, link, description: '北京大学研究生院通知公告', @@ -25,5 +47,5 @@ export default async (ctx) => { }; }) .get(), - }); -}; + }; +} diff --git a/lib/routes/pku/rccp/mzyt.ts b/lib/routes/pku/rccp/mzyt.ts index fb436b2d8..cece86905 100644 --- a/lib/routes/pku/rccp/mzyt.ts +++ b/lib/routes/pku/rccp/mzyt.ts @@ -1,13 +1,35 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; const baseUrl = 'https://www.rccp.pku.edu.cn/mzyt/'; -export default async (ctx) => { +export const route: Route = { + path: '/rccp/mzyt', + categories: ['forecast'], + example: '/pku/rccp/mzyt', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.rccp.pku.edu.cn/'], + }, + name: '每周一推 - 中国政治学研究中心', + maintainers: ['vhxubo'], + handler, +}; + +async function handler() { const response = await got(baseUrl); const $ = load(response.data); - ctx.set('data', { + return { title: '每周一推 - 北京大学中国政治学研究中心', link: baseUrl, description: $('meta[name="description"]').attr('content'), @@ -19,5 +41,5 @@ export default async (ctx) => { link: baseUrl + $(item).find('a').attr('href'), })) .get(), - }); -}; + }; +} diff --git a/lib/routes/pku/scc/recruit.ts b/lib/routes/pku/scc/recruit.ts index a06d66172..cfdb0b230 100644 --- a/lib/routes/pku/scc/recruit.ts +++ b/lib/routes/pku/scc/recruit.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,25 @@ const arr = { }; const baseUrl = 'https://scc.pku.edu.cn/'; -export default async (ctx) => { +export const route: Route = { + path: '/scc/recruit/:type?', + categories: ['forecast'], + example: '/pku/scc/recruit/zpxx', + parameters: { type: '分区,见下表,默认请求 `zpxx`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '学生就业指导服务中心', + maintainers: ['DylanXie123'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') ?? 'zpxx'; const rootUrl = baseUrl + arr[type]; @@ -36,7 +55,7 @@ export default async (ctx) => { const sorted = list.sort((a, b) => b.pubDate.getTime() - a.pubDate.getTime()).slice(0, 10); - ctx.set('data', { + return { title: `北京大学学生就业指导服务中心 - ${feed_title}`, link: rootUrl, item: await Promise.all( @@ -54,5 +73,5 @@ export default async (ctx) => { }) ) ), - }); -}; + }; +} diff --git a/lib/routes/pku/ss/admission.ts b/lib/routes/pku/ss/admission.ts index 7738cd5cf..f0d5eb8d3 100644 --- a/lib/routes/pku/ss/admission.ts +++ b/lib/routes/pku/ss/admission.ts @@ -1,16 +1,38 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { baseUrl, getSingleRecord, getArticle } from './common'; const host = `${baseUrl}/admission/admnotice/`; -export default async (ctx) => { +export const route: Route = { + path: '/ss/admission', + categories: ['forecast'], + example: '/pku/ss/admission', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ss.pku.edu.cn/admission/admnotice', 'ss.pku.edu.cn/'], + }, + name: '软件与微电子学院 - 招生通知', + maintainers: ['legr4ndk'], + handler, +}; + +async function handler() { const items = await getSingleRecord(host); const out = await Promise.all(items.map((item) => getArticle(item, cache.tryGet))); - ctx.set('data', { + return { title: '北大软微-招生通知', description: '北京大学软件与微电子学院 - 招生通知', link: host, item: out, - }); -}; + }; +} diff --git a/lib/routes/pku/ss/notice.ts b/lib/routes/pku/ss/notice.ts index a37b0d687..715587373 100644 --- a/lib/routes/pku/ss/notice.ts +++ b/lib/routes/pku/ss/notice.ts @@ -1,16 +1,27 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { baseUrl, getSingleRecord, getArticle } from './common'; const host = `${baseUrl}/newscenter/notice/`; -export default async (ctx) => { +export const route: Route = { + path: '/ss/notice', + radar: { + source: ['ss.pku.edu.cn/index.php/newscenter/notice', 'ss.pku.edu.cn/'], + }, + name: 'Unknown', + maintainers: ['legr4ndk'], + handler, +}; + +async function handler() { const items = await getSingleRecord(host); const out = await Promise.all(items.map((item) => getArticle(item, cache.tryGet))); - ctx.set('data', { + return { title: '北大软微-通知公告', description: '北京大学软件与微电子学院 - 通知公告', link: host, item: out, - }); -}; + }; +} diff --git a/lib/routes/pku/ss/pg-admin.ts b/lib/routes/pku/ss/pg-admin.ts index 197ef857f..7046dbcae 100644 --- a/lib/routes/pku/ss/pg-admin.ts +++ b/lib/routes/pku/ss/pg-admin.ts @@ -1,16 +1,38 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { baseUrl, getSingleRecord, getArticle } from './common'; const host = `${baseUrl}/admission/admbrochure/`; -export default async (ctx) => { +export const route: Route = { + path: '/ss/pgadmin', + categories: ['forecast'], + example: '/pku/ss/pgadmin', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ss.pku.edu.cn/admission/admbrochure/admission01', 'ss.pku.edu.cn/'], + }, + name: '软件与微电子学院 - 硕士统考招生通知', + maintainers: ['legr4ndk'], + handler, +}; + +async function handler() { const items = await getSingleRecord(host); const out = await Promise.all(items.map((item) => getArticle(item, cache.tryGet))); - ctx.set('data', { + return { title: '北大软微-硕士统考招生', description: '北京大学软件与微电子学院 - 硕士统考招生通知', link: host, item: out, - }); -}; + }; +} diff --git a/lib/routes/playno1/av.ts b/lib/routes/playno1/av.ts index 35b87fdf3..b0479e23f 100644 --- a/lib/routes/playno1/av.ts +++ b/lib/routes/playno1/av.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'; import { cookieJar, processArticle } from './utils'; const baseUrl = 'http://www.playno1.com'; -export default async (ctx) => { +export const route: Route = { + path: '/av/:catid?', + categories: ['blog'], + example: '/playno1/av', + parameters: { catid: '分类,见下表,默认为全部文章' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'AV', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { catid = '78' } = ctx.req.param(); const url = `${baseUrl}/portal.php?mod=list&catid=${catid}`; const response = await got(url, { @@ -33,10 +52,10 @@ export default async (ctx) => { items = await processArticle(items, cache); - ctx.set('data', { + return { title: $('head title').text(), link: url, item: items, language: 'zh-TW', - }); -}; + }; +} diff --git a/lib/routes/playno1/st.ts b/lib/routes/playno1/st.ts index 73625fdf9..01097ac27 100644 --- a/lib/routes/playno1/st.ts +++ b/lib/routes/playno1/st.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'; import { cookieJar, processArticle } from './utils'; const baseUrl = 'http://stno1.playno1.com'; -export default async (ctx) => { +export const route: Route = { + path: '/st/:catid?', + categories: ['blog'], + example: '/playno1/st', + parameters: { catid: '分类,见下表,默认为全部文章' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['stno1.playno1.com/stno1/:catid/'], + target: '/st/:catid', + }, + name: '情趣', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { catid = 'all' } = ctx.req.param(); const url = `${baseUrl}/stno1/${catid}/`; const response = await got(url, { @@ -28,10 +51,10 @@ export default async (ctx) => { items = await processArticle(items, cache); - ctx.set('data', { + return { title: $('head title').text(), link: url, item: items, language: 'zh-TW', - }); -}; + }; +} diff --git a/lib/routes/playpcesor/rss.ts b/lib/routes/playpcesor/rss.ts index 2b90bced9..14b7a263b 100644 --- a/lib/routes/playpcesor/rss.ts +++ b/lib/routes/playpcesor/rss.ts @@ -1,8 +1,20 @@ +import { Route } from '@/types'; import { parseDate } from '@/utils/parse-date'; import got from '@/utils/got'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['playpcesor.com/'], + target: '', + }, + name: 'Unknown', + maintainers: ['cnkmmk'], + handler, +}; + +async function handler() { const url = 'https://www.playpcesor.com/'; const response = await got({ method: 'get', url }); const $ = load(response.data); @@ -24,9 +36,9 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title: '电脑玩物', link: url, item: list, - }); -}; + }; +} diff --git a/lib/routes/plurk/anonymous.ts b/lib/routes/plurk/anonymous.ts index cc8e438aa..666eda89d 100644 --- a/lib/routes/plurk/anonymous.ts +++ b/lib/routes/plurk/anonymous.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { baseUrl, getPlurk } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/anonymous', + categories: ['new-media'], + example: '/plurk/anonymous', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['plurk.com/anonymous'], + }, + name: 'Anonymous', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { data: apiResponse } = await got(`${baseUrl}/Stats/getAnonymousPlurks`, { searchParams: { offset: 0, @@ -15,10 +37,10 @@ export default async (ctx) => { const items = await Promise.all(Object.values(apiResponse).map((item) => getPlurk(`plurk:${item.plurk_id}`, item, 'ಠ_ಠ', cache.tryGet))); - ctx.set('data', { + return { title: 'Anonymous - Plurk', image: 'https://s.plurk.com/2c1574c02566f3b06e91.png', link: `${baseUrl}/anonymous`, item: items, - }); -}; + }; +} diff --git a/lib/routes/plurk/hotlinks.ts b/lib/routes/plurk/hotlinks.ts index 4b32f2557..f550500e8 100644 --- a/lib/routes/plurk/hotlinks.ts +++ b/lib/routes/plurk/hotlinks.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { baseUrl, getPlurk } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/hotlinks', + categories: ['new-media'], + example: '/plurk/hotlinks', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['plurk.com/hotlinks'], + }, + name: 'Hotlinks', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { data: apiResponse } = await got(`${baseUrl}/hotlinks/getLinks`, { searchParams: { offset: 0, @@ -12,10 +34,10 @@ export default async (ctx) => { const items = await Promise.all(apiResponse.map((item) => getPlurk(item.link_url.startsWith('https://www.plurk.com/p/') ? item.link_url : `plurk:${item.link_url}`, item, null, cache.tryGet))); - ctx.set('data', { + return { title: `Hot Links - Plurk`, image: 'https://s.plurk.com/2c1574c02566f3b06e91.png', link: `${baseUrl}/hotlinks`, item: items, - }); -}; + }; +} diff --git a/lib/routes/plurk/news.ts b/lib/routes/plurk/news.ts index 68957fbdc..e7e509bdf 100644 --- a/lib/routes/plurk/news.ts +++ b/lib/routes/plurk/news.ts @@ -1,8 +1,31 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { baseUrl, fetchFriends, getPlurk } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/news/:lang?', + categories: ['new-media'], + example: '/plurk/news/:lang?', + parameters: { lang: 'Language, see the table above, `en` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['plurk.com/news'], + target: '/news', + }, + name: 'Plurk News', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { lang = 'en' } = ctx.req.param(); const { data: apiResponse } = await got(`${baseUrl}/PlurkTop/fetchOfficialPlurks`, { searchParams: { @@ -15,10 +38,10 @@ export default async (ctx) => { const items = await Promise.all(apiResponse.map((item) => getPlurk(`plurk:${item.plurk_id}`, item, names[item.user_id].display_name, cache.tryGet))); - ctx.set('data', { + return { title: 'Plurk News - Plurk', image: 'https://s.plurk.com/2c1574c02566f3b06e91.png', link: `${baseUrl}/news`, item: items, - }); -}; + }; +} diff --git a/lib/routes/plurk/search.ts b/lib/routes/plurk/search.ts index f5b9675b9..71b01566f 100644 --- a/lib/routes/plurk/search.ts +++ b/lib/routes/plurk/search.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import dayjs from 'dayjs'; import { baseUrl, getPlurk } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/search/:keyword', + categories: ['new-media'], + example: '/plurk/search/FGO', + parameters: { keyword: 'Search keyword' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Search', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const keyword = ctx.req.param('keyword'); const { data: apiResponse } = await got.post(`${baseUrl}/Search/search2`, { searchParams: { @@ -18,11 +37,11 @@ export default async (ctx) => { const items = await Promise.all(plurks.map((item) => getPlurk(`plurk:${item.plurk_id}`, item, users[item.user_id].display_name, cache.tryGet))); - ctx.set('data', { + return { title: `Search "${keyword}" - Plurk`, description: 'Search messages on Plurk', image: 'https://s.plurk.com/e8266f512246cdbc2721.jpg', link: `${baseUrl}/search?q=${encodeURIComponent(keyword)}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/plurk/top.ts b/lib/routes/plurk/top.ts index 075ed8286..1dd18fbaa 100644 --- a/lib/routes/plurk/top.ts +++ b/lib/routes/plurk/top.ts @@ -1,10 +1,29 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { baseUrl, getPlurk } from './utils'; const categoryList = new Set(['topReplurks', 'topFavorites', 'topResponded']); -export default async (ctx) => { +export const route: Route = { + path: '/top/:category?/:lang?', + categories: ['new-media'], + example: '/plurk/top/topReplurks', + parameters: { category: 'Category, see the table below, `topReplurks` by default', lang: 'Language, see the table below, `en` by default' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Top', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { category = 'topReplurks', lang = 'en' } = ctx.req.param(); if (!categoryList.has(category)) { throw new Error(`Invalid category: ${category}`); @@ -20,11 +39,11 @@ export default async (ctx) => { const items = await Promise.all(apiResponse.stats.map((item) => item[1]).map((item) => getPlurk(`plurk:${item.plurk_id}`, item, item.owner.display_name, cache.tryGet))); - ctx.set('data', { + return { title: 'Top Plurk - Plurk', image: 'https://s.plurk.com/2c1574c02566f3b06e91.png', link: `${baseUrl}/top#${category}`, item: items, language: lang, - }); -}; + }; +} diff --git a/lib/routes/plurk/topic.ts b/lib/routes/plurk/topic.ts index 87e3ada62..9ea4aedac 100644 --- a/lib/routes/plurk/topic.ts +++ b/lib/routes/plurk/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, fetchFriends, getPlurk } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/topic/:topic', + categories: ['new-media'], + example: '/plurk/topic/standwithukraine', + parameters: { topic: 'Topic ID, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['plurk.com/topic/:topic'], + }, + name: 'Topic', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const topic = ctx.req.param('topic'); const { data: pageResponse } = await got(`${baseUrl}/topic/${topic}`); const { data: apiResponse } = await got(`${baseUrl}/topic/getPlurks`, { @@ -24,11 +46,11 @@ export default async (ctx) => { const items = await Promise.all(Object.values(apiResponse).map((item) => getPlurk(`plurk:${item.plurk_id}`, item, names[item.user_id].display_name, cache.tryGet))); - ctx.set('data', { + return { title: $('head title').text(), description: $('meta[property=og:description]').attr('content'), image: $('meta[property=og:image]').attr('content') || $('meta[name=msapplication-TileImage]').attr('content'), link: `${baseUrl}/topic/${topic}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/plurk/user.ts b/lib/routes/plurk/user.ts index 380b2faae..898869f17 100644 --- a/lib/routes/plurk/user.ts +++ b/lib/routes/plurk/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 { baseUrl, fetchFriends, getPlurk } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/user/:user', + categories: ['new-media'], + example: '/plurk/user/plurkoffice', + parameters: { user: 'User ID, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'User', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const user = ctx.req.param('user'); const { data: pageResponse } = await got(`${baseUrl}/${user}`); @@ -22,11 +41,11 @@ export default async (ctx) => { const items = await Promise.all(publicPlurks.map((item) => getPlurk(`plurk:${item.plurk_id}`, item, names[item.user_id].display_name, cache.tryGet))); - ctx.set('data', { + return { title: $('head title').text(), description: $('meta[property=og:description]').attr('content'), image: $('meta[property=og:image]').attr('content') || $('meta[name=msapplication-TileImage]').attr('content'), link: `${baseUrl}/${user}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/pnas/index.ts b/lib/routes/pnas/index.ts index c667ff730..3a582e829 100644 --- a/lib/routes/pnas/index.ts +++ b/lib/routes/pnas/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -12,7 +13,18 @@ import { CookieJar } from 'tough-cookie'; import logger from '@/utils/logger'; import puppeteer from '@/utils/puppeteer'; -export default async (ctx) => { +export const route: Route = { + path: '/:topicPath{.+}?', + radar: { + source: ['pnas.org/*topicPath'], + target: '/:topicPath', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://www.pnas.org'; const topicPath = ctx.req.param('topicPath'); const link = `${baseUrl}/${topicPath ?? 'latest'}`; @@ -95,12 +107,12 @@ export default async (ctx) => { browser.close(); - ctx.set('data', { + return { title: `${$('.banner-widget__content h1').text()} - PNAS`, description: $('.banner-widget__content p').text(), image: 'https://www.pnas.org/favicon.ico', language: 'en-US', link, item: out, - }); -}; + }; +} diff --git a/lib/routes/pornhub/category-url.ts b/lib/routes/pornhub/category-url.ts index e6afedc8c..f1742869f 100644 --- a/lib/routes/pornhub/category-url.ts +++ b/lib/routes/pornhub/category-url.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { isValidHost } from '@/utils/valid-host'; import { headers, parseItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/:language?/category_url/:url?', + categories: ['picture'], + example: '/pornhub/category_url/video%3Fc%3D15%26o%3Dmv%26t%3Dw%26cc%3Djp', + parameters: { language: 'language, see below', url: 'relative path after `pornhub.com/`, need to be URL encoded' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Video List', + maintainers: ['I2IMk', 'queensferryme'], + handler, +}; + +async function handler(ctx) { const { language = 'www', url = 'video' } = ctx.req.param(); const link = `https://${language}.pornhub.com/${url}`; if (!isValidHost(language)) { @@ -16,10 +35,10 @@ export default async (ctx) => { .toArray() .map((e) => parseItems($(e))); - ctx.set('data', { + return { title: $('title').first().text(), link, language: $('html').attr('lang'), item: items, - }); -}; + }; +} diff --git a/lib/routes/pornhub/category.ts b/lib/routes/pornhub/category.ts index 0c9ed7269..96faa40fd 100644 --- a/lib/routes/pornhub/category.ts +++ b/lib/routes/pornhub/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 { defaultDomain, renderDescription } from './utils'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/category/:caty', + categories: ['picture'], + example: '/pornhub/category/popular-with-women', + parameters: { caty: 'category, see [categories](https://www.pornhub.com/webmasters/categories)' }, + 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('caty'); const categories = await cache.tryGet('pornhub:categories', async () => { @@ -37,9 +56,9 @@ export default async (ctx) => { category: [...new Set([...item.tags.map((t) => t.tag_name), ...item.categories.map((c) => c.category)])], })); - ctx.set('data', { + return { title: `Pornhub - ${categoryName}`, link: `${defaultDomain}/video?c=${categoryId}`, item: list, - }); -}; + }; +} diff --git a/lib/routes/pornhub/model.ts b/lib/routes/pornhub/model.ts index d43620ca3..ea09c6a23 100644 --- a/lib/routes/pornhub/model.ts +++ b/lib/routes/pornhub/model.ts @@ -1,9 +1,32 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { isValidHost } from '@/utils/valid-host'; import { headers, parseItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/:language?/model/:username/:sort?', + categories: ['picture'], + example: '/pornhub/model/stacy-starando', + parameters: { language: 'language, see below', username: 'username, part of the url e.g. `pornhub.com/model/stacy-starando`', sort: 'sorting method, see below' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['pornhub.com/model/:username/*'], + target: '/model/:username', + }, + name: 'Verified amateur / Model', + maintainers: ['I2IMk', 'queensferryme'], + handler, +}; + +async function handler(ctx) { const { language = 'www', username, sort = '' } = ctx.req.param(); const link = `https://${language}.pornhub.com/model/${username}/videos${sort ? `?o=${sort}` : ''}`; if (!isValidHost(language)) { @@ -16,7 +39,7 @@ export default async (ctx) => { .toArray() .map((e) => parseItems($(e))); - ctx.set('data', { + return { title: $('title').first().text(), description: $('section.aboutMeSection').text().trim(), link, @@ -25,5 +48,5 @@ export default async (ctx) => { icon: $('#getAvatar').attr('src'), language: $('html').attr('lang'), item: items, - }); -}; + }; +} diff --git a/lib/routes/pornhub/pornstar.ts b/lib/routes/pornhub/pornstar.ts index 57a6fd369..aa3514024 100644 --- a/lib/routes/pornhub/pornstar.ts +++ b/lib/routes/pornhub/pornstar.ts @@ -1,9 +1,32 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { isValidHost } from '@/utils/valid-host'; import { headers, parseItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/:language?/pornstar/:username/:sort?', + categories: ['picture'], + example: '/pornhub/pornstar/june-liu', + parameters: { language: 'language, see below', username: 'username, part of the url e.g. `pornhub.com/pornstar/june-liu`', sort: 'sorting method, see below' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['pornhub.com/pornstar/:username/*'], + target: '/pornstar/:username', + }, + name: 'Verified model / Pornstar', + maintainers: ['I2IMk', 'queensferryme'], + handler, +}; + +async function handler(ctx) { const { language = 'www', username, sort = 'mr' } = ctx.req.param(); const link = `https://${language}.pornhub.com/pornstar/${username}/videos?o=${sort}`; if (!isValidHost(language)) { @@ -16,7 +39,7 @@ export default async (ctx) => { .toArray() .map((e) => parseItems($(e))); - ctx.set('data', { + return { title: $('title').first().text(), description: $('section.aboutMeSection').text().trim(), link, @@ -25,5 +48,5 @@ export default async (ctx) => { icon: $('#getAvatar').attr('src'), language: $('html').attr('lang'), item: items, - }); -}; + }; +} diff --git a/lib/routes/pornhub/search.ts b/lib/routes/pornhub/search.ts index 626b5b50e..b6e3dde96 100644 --- a/lib/routes/pornhub/search.ts +++ b/lib/routes/pornhub/search.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { defaultDomain, renderDescription } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/search/:keyword', + categories: ['picture'], + example: '/pornhub/search/stepsister', + parameters: { keyword: 'keyword' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Keyword Search', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const keyword = ctx.req.param('keyword'); const currentUrl = `${defaultDomain}/webmasters/search?search=${keyword}`; const response = await got(currentUrl); @@ -15,9 +34,9 @@ export default async (ctx) => { category: [...new Set([...item.tags.map((t) => t.tag_name), ...item.categories.map((c) => c.category)])], })); - ctx.set('data', { + return { title: `Pornhub - ${keyword}`, link: currentUrl, item: list, - }); -}; + }; +} diff --git a/lib/routes/pornhub/users.ts b/lib/routes/pornhub/users.ts index a3284b7fe..ee711b942 100644 --- a/lib/routes/pornhub/users.ts +++ b/lib/routes/pornhub/users.ts @@ -1,9 +1,32 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { isValidHost } from '@/utils/valid-host'; import { headers, parseItems } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/:language?/users/:username', + categories: ['picture'], + example: '/pornhub/users/pornhubmodels', + parameters: { language: 'language, see below', username: 'username, part of the url e.g. `pornhub.com/users/pornhubmodels`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['pornhub.com/users/:username/*'], + target: '/users/:username', + }, + name: 'Users', + maintainers: ['I2IMk', 'queensferryme'], + handler, +}; + +async function handler(ctx) { const { language = 'www', username } = ctx.req.param(); const link = `https://${language}.pornhub.com/users/${username}/videos`; if (!isValidHost(language)) { @@ -16,7 +39,7 @@ export default async (ctx) => { .toArray() .map((e) => parseItems($(e))); - ctx.set('data', { + return { title: $('title').first().text(), description: $('.aboutMeText').text().trim(), link, @@ -26,5 +49,5 @@ export default async (ctx) => { language: $('html').attr('lang'), allowEmpty: true, item: items, - }); -}; + }; +} diff --git a/lib/routes/postman/release-notes.ts b/lib/routes/postman/release-notes.ts index 56f31b94c..db3523937 100644 --- a/lib/routes/postman/release-notes.ts +++ b/lib/routes/postman/release-notes.ts @@ -1,10 +1,32 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import MarkdownIt from 'markdown-it'; const md = MarkdownIt({ html: true, }); -export default async (ctx) => { +export const route: Route = { + path: '/release-notes', + categories: ['university'], + example: '/postman/release-notes', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['postman.com/downloads/release-notes', 'postman.com/'], + }, + name: 'Release Notes', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://www.postman.com'; const apiUrl = `${rootUrl}/mkapi/release.json`; const currentUrl = `${rootUrl}/downloads/release-notes`; @@ -20,9 +42,9 @@ export default async (ctx) => { description: md.render(item.content), })); - ctx.set('data', { + return { title: 'Release Notes | Postman', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/priconne-redive/news.ts b/lib/routes/priconne-redive/news.ts index b24e24487..bfa52ba5c 100644 --- a/lib/routes/priconne-redive/news.ts +++ b/lib/routes/priconne-redive/news.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import cache from '@/utils/cache'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/news', + categories: ['reading'], + example: '/priconne-redive/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['priconne-redive.jp/news'], + }, + name: '日服公告', + maintainers: ['SayaSS'], + handler, +}; + +async function handler() { const parseContent = (htmlString) => { const $ = load(htmlString); @@ -46,10 +68,10 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: '公主链接日服-新闻', link: 'https://priconne-redive.jp/news/', language: 'ja', item: out, - }); -}; + }; +} diff --git a/lib/routes/producthunt/today.ts b/lib/routes/producthunt/today.ts index 658189578..cf774fb7e 100644 --- a/lib/routes/producthunt/today.ts +++ b/lib/routes/producthunt/today.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: '/today', + categories: ['other'], + example: '/producthunt/today', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.producthunt.com/'], + }, + name: 'Today Popular', + maintainers: ['miaoyafeng', 'Fatpandac'], + handler, +}; + +async function handler() { const response = await got('https://www.producthunt.com/'); const data = JSON.parse(load(response.data)('#__NEXT_DATA__').html()); @@ -40,9 +62,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'Product Hunt Today Popular', link: 'https://www.producthunt.com/', item: items, - }); -}; + }; +} diff --git a/lib/routes/ps/monthly-games.ts b/lib/routes/ps/monthly-games.ts index e1cc6605e..2eef3ed65 100644 --- a/lib/routes/ps/monthly-games.ts +++ b/lib/routes/ps/monthly-games.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 { load } from 'cheerio'; import * as path from 'node:path'; import { art } from '@/utils/render'; -export default async (ctx) => { +export const route: Route = { + path: '/monthly-games', + categories: ['reading'], + example: '/ps/monthly-games', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.playstation.com/en-sg/ps-plus/whats-new'], + }, + name: 'PlayStation Monthly Games', + maintainers: ['justjustCC'], + handler, +}; + +async function handler() { const baseUrl = 'https://www.playstation.com/en-sg/ps-plus/whats-new/'; const { data: response } = await got(baseUrl); @@ -26,9 +48,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: 'PlayStation Plus Monthly Games', link: baseUrl, item: list, - }); -}; + }; +} diff --git a/lib/routes/ps/trophy.ts b/lib/routes/ps/trophy.ts index c25f1f618..f6ebbc55e 100644 --- a/lib/routes/ps/trophy.ts +++ b/lib/routes/ps/trophy.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: '/trophy/:id', + categories: ['reading'], + example: '/ps/trophy/DIYgod_', + parameters: { id: 'User ID' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'PlayStation Network user trophy', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const response = await got({ @@ -78,9 +97,9 @@ export default async (ctx) => { } result = result.sort((a, b) => new Date(b.pubDate) - new Date(a.pubDate)); - ctx.set('data', { + return { title: `${id} 的 PSN 奖杯`, link: `https://psnprofiles.com/${id}/log`, item: result, - }); -}; + }; +} diff --git a/lib/routes/pts/curations.ts b/lib/routes/pts/curations.ts index 2456fa633..d3c38244b 100644 --- a/lib/routes/pts/curations.ts +++ b/lib/routes/pts/curations.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 { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/curations', + categories: ['bbs'], + example: '/pts/curations', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['news.pts.org.tw/curations', 'news.pts.org.tw/'], + }, + name: '專題策展', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://news.pts.org.tw'; const currentUrl = `${rootUrl}/curations`; @@ -37,11 +59,11 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('title') .text() .replace(/第\d+頁 | /, ''), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/pts/index.ts b/lib/routes/pts/index.ts index 702f9b55a..b64162ea6 100644 --- a/lib/routes/pts/index.ts +++ b/lib/routes/pts/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://news.pts.org.tw'; const currentUrl = `${rootUrl}${getSubPath(ctx) === '/' ? '/dailynews' : getSubPath(ctx)}`; @@ -63,11 +71,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title') .text() .replace(/第\d+頁 | /, ''), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/pts/live.ts b/lib/routes/pts/live.ts index 2a3143349..008803829 100644 --- a/lib/routes/pts/live.ts +++ b/lib/routes/pts/live.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 { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/live/:id', + categories: ['bbs'], + example: '/pts/live/62e8e4bbb4de2cbd74468b2b', + parameters: { id: '報導 id,可在对应整理報導页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['news.pts.org.tw/live/:id', 'news.pts.org.tw/'], + }, + name: '整理報導', + maintainers: [], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const rootUrl = 'https://news.pts.org.tw'; @@ -45,9 +67,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `公視新聞網 PNN - ${response.data.data.title.replace(/【不斷更新】/, '')}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/pts/projects.ts b/lib/routes/pts/projects.ts index bfcb0ecc2..e55933720 100644 --- a/lib/routes/pts/projects.ts +++ b/lib/routes/pts/projects.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 { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/projects', + categories: ['bbs'], + example: '/pts/projects', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['news.pts.org.tw/projects', 'news.pts.org.tw/'], + }, + name: '數位敘事', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://news.pts.org.tw'; const currentUrl = `${rootUrl}/projects`; @@ -37,11 +59,11 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('title') .text() .replace(/第\d+頁 | /, ''), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/pubmed/trending.ts b/lib/routes/pubmed/trending.ts index cd4c8d69d..ea176d27d 100644 --- a/lib/routes/pubmed/trending.ts +++ b/lib/routes/pubmed/trending.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: '/trending/:filters?', + name: 'Unknown', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const filters = ctx.req.param('filters'); const rootUrl = 'https://pubmed.ncbi.nlm.nih.gov'; @@ -54,9 +62,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: 'Trending page - PubMed', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/pumc/mdadmission.ts b/lib/routes/pumc/mdadmission.ts index 210061d84..b5bcc0510 100644 --- a/lib/routes/pumc/mdadmission.ts +++ b/lib/routes/pumc/mdadmission.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: '/mdadmission', + categories: ['forecast'], + example: '/pumc/mdadmission', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['mdadmission.pumc.edu.cn/mdweb/site', 'mdadmission.pumc.edu.cn/'], + }, + name: '“4+4” 试点班招生网通知公告', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 100; const rootUrl = 'https://mdadmission.pumc.edu.cn'; @@ -55,9 +77,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '北京协和医学院招生网 - 通知公告', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/putty/changes.ts b/lib/routes/putty/changes.ts index 2613e2280..8084689a8 100644 --- a/lib/routes/putty/changes.ts +++ b/lib/routes/putty/changes.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: '/changes', + categories: ['university'], + example: '/putty/changes', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.chiark.greenend.org.uk/~sgtatham/putty/changes.html', 'www.chiark.greenend.org.uk/'], + }, + name: 'Change Log', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'https://www.chiark.greenend.org.uk'; const currentUrl = `${rootUrl}/~sgtatham/putty/changes.html`; @@ -28,9 +50,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('title').text(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/pwc/sustainability.ts b/lib/routes/pwc/sustainability.ts index b32fb1f11..61a3eca16 100644 --- a/lib/routes/pwc/sustainability.ts +++ b/lib/routes/pwc/sustainability.ts @@ -1,9 +1,31 @@ +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: '/strategyand/sustainability', + categories: ['other'], + example: '/pwc/strategyand/sustainability', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['strategyand.pwc.com/at/en/functions/sustainability-strategy/publications.html', 'strategyand.pwc.com/'], + }, + name: 'Sustainability', + maintainers: ['mintyfrankie'], + handler, +}; + +async function handler() { const baseUrl = 'https://www.strategyand.pwc.com/at/en/functions/sustainability-strategy/publications.html'; const feedLang = 'en'; const feedDescription = 'Sustainability Publications from PwC Strategy&'; @@ -49,11 +71,11 @@ export default async (ctx) => { browser.close(); - ctx.set('data', { + return { title: 'PwC Strategy& - Sustainability Publications', link: baseUrl, language: feedLang, description: feedDescription, item: items, - }); -}; + }; +} diff --git a/lib/routes/qbitai/category.ts b/lib/routes/qbitai/category.ts index 9a6bffbbb..f8a97cc0c 100644 --- a/lib/routes/qbitai/category.ts +++ b/lib/routes/qbitai/category.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import parser from '@/utils/rss-parser'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/category/:category', + categories: ['traditional-media'], + example: '/qbitai/category/资讯', + parameters: { category: '分类名,见下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['qbitai.com/category/:category'], + }, + name: '分类', + maintainers: ['FuryMartin'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category'); const link = encodeURI(`https://www.qbitai.com/category/${category}/feed`); const feed = await parser.parseURL(link); @@ -16,12 +38,12 @@ export default async (ctx) => { description: item['content:encoded'], })); - ctx.set('data', { + return { // 源标题 title: `量子位-${category}`, // 源链接 link: `https://www.qbitai.com/category/${category}`, // 源文章 item: items, - }); -}; + }; +} diff --git a/lib/routes/qbitai/tag.ts b/lib/routes/qbitai/tag.ts index cd8b4742b..1e9f4f281 100644 --- a/lib/routes/qbitai/tag.ts +++ b/lib/routes/qbitai/tag.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import parser from '@/utils/rss-parser'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/tag/:tag', + categories: ['traditional-media'], + example: '/qbitai/tag/大语言模型', + parameters: { tag: '标签名' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['qbitai.com/tag/:tag'], + }, + name: '标签', + maintainers: ['FuryMartin'], + handler, +}; + +async function handler(ctx) { const tag = ctx.req.param('tag'); const link = encodeURI(`https://www.qbitai.com/tag/${tag}/feed`); const feed = await parser.parseURL(link); @@ -16,12 +38,12 @@ export default async (ctx) => { description: item['content:encoded'], })); - ctx.set('data', { + return { // 源标题 title: `量子位-${tag}`, // 源链接 link: `https://www.qbitai.com/tag/${tag}`, // 源文章 item: items, - }); -}; + }; +} diff --git a/lib/routes/qbittorrent/news.ts b/lib/routes/qbittorrent/news.ts index 154405601..1764a57d9 100644 --- a/lib/routes/qbittorrent/news.ts +++ b/lib/routes/qbittorrent/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 { parseDate } from '@/utils/parse-date'; import { config } from '@/config'; -export default async (ctx) => { +export const route: Route = { + path: '/news', + categories: ['university'], + example: '/qbittorrent/news', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['qbittorrent.org/news.php', 'qbittorrent.org/'], + }, + name: 'News', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://www.qbittorrent.org'; const response = await cache.tryGet( @@ -46,15 +68,15 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: 'qBittorrent News', link: `${baseUrl}/news.php`, item, - }); + }; ctx.set('json', { title: 'qBittorrent News', link: `${baseUrl}/news.php`, item, }); -}; +} diff --git a/lib/routes/qdu/houqin.ts b/lib/routes/qdu/houqin.ts index d4cfa6792..298fb910d 100644 --- a/lib/routes/qdu/houqin.ts +++ b/lib/routes/qdu/houqin.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 base = 'https://houqin.qdu.edu.cn/'; -export default async (ctx) => { +export const route: Route = { + path: '/houqin', + categories: ['forecast'], + example: '/qdu/houqin', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['houqin.qdu.edu.cn/tzgg.htm', 'houqin.qdu.edu.cn/'], + }, + name: '后勤管理处通知', + maintainers: ['abc1763613206'], + handler, +}; + +async function handler() { const response = await got({ method: 'get', url: `${base}index/tzgg.htm`, @@ -55,10 +77,10 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: '青岛大学 - 后勤管理处通知', link: `${base}index/tzgg.htm`, description: '青岛大学 - 后勤管理处通知', item: items, - }); -}; + }; +} diff --git a/lib/routes/qdu/jwc.ts b/lib/routes/qdu/jwc.ts index b6bd78b6d..d770ce94b 100644 --- a/lib/routes/qdu/jwc.ts +++ b/lib/routes/qdu/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,28 @@ import { parseDate } from '@/utils/parse-date'; const base = 'https://jwc.qdu.edu.cn/'; -export default async (ctx) => { +export const route: Route = { + path: '/jwc', + categories: ['forecast'], + example: '/qdu/jwc', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['jwc.qdu.edu.cn/jwtz.htm', 'jwc.qdu.edu.cn/'], + }, + name: '教务处通知', + maintainers: ['abc1763613206'], + handler, +}; + +async function handler() { const response = await got({ method: 'get', url: `${base}jwtz.htm`, @@ -44,10 +66,10 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: '青岛大学 - 教务处通知', link: `${base}jwtz.htm`, description: '青岛大学 - 教务处通知', item: items, - }); -}; + }; +} diff --git a/lib/routes/qianp/news.ts b/lib/routes/qianp/news.ts index e0c47ba38..1136b70c1 100644 --- a/lib/routes/qianp/news.ts +++ b/lib/routes/qianp/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 { parseDate } from '@/utils/parse-date'; import { getTokenAndSecret } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/news/:path{.+}?', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const baseUrl = 'https://qianp.com'; const { path = 'news/recommend' } = ctx.req.param(); const url = `${baseUrl}/${path}/`; @@ -48,11 +56,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), description: $('meta[name="description"]').attr('content'), link: url, image: `${baseUrl}/favicon.ico`, item: items, - }); -}; + }; +} diff --git a/lib/routes/qianzhan/column.ts b/lib/routes/qianzhan/column.ts index 30c5f8e38..0291c733a 100644 --- a/lib/routes/qianzhan/column.ts +++ b/lib/routes/qianzhan/column.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: '/analyst/column/:type?', + categories: ['other'], + example: '/qianzhan/analyst/column/all', + parameters: { type: '分类,见下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '文章列表', + maintainers: ['moke8'], + handler, +}; + +async function handler(ctx) { let rootUrl = 'https://www.qianzhan.com/analyst/'; const titles = { all: '最新文章', @@ -43,9 +62,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `前瞻经济学人 - ${titles[type]}`, link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/qianzhan/rank.ts b/lib/routes/qianzhan/rank.ts index a1018f4d4..862f6407c 100644 --- a/lib/routes/qianzhan/rank.ts +++ b/lib/routes/qianzhan/rank.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: '/analyst/rank/:type?', + categories: ['other'], + example: '/qianzhan/analyst/rank/week', + parameters: { type: '分类,见下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['qianzhan.com/analyst', 'qianzhan.com/'], + target: '/analyst/rank', + }, + name: '排行榜', + maintainers: ['moke8'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') === 'week' ? 1 : 2; const rootUrl = 'https://www.qianzhan.com/analyst/'; @@ -33,9 +56,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `前瞻经济学人 - ${type === 1 ? '周排行' : '月排行'}`, link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/qidian/author.ts b/lib/routes/qidian/author.ts index 9ad46cbe4..0b146405a 100644 --- a/lib/routes/qidian/author.ts +++ b/lib/routes/qidian/author.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 timezone from '@/utils/timezone'; -export default async (ctx) => { +export const route: Route = { + path: '/author/:id', + categories: ['government'], + example: '/qidian/author/9639927', + parameters: { id: '作者 id, 可在作者页面 URL 找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['my.qidian.com/author/:id'], + }, + name: '作者', + maintainers: ['miles170'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const rootUrl = 'https://my.qidian.com'; const currentUrl = `${rootUrl}/author/${id}/`; @@ -35,10 +57,10 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `${authorName} - 起点中文网`, description: $('.header-msg-desc').text().trim(), link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/qidian/chapter.ts b/lib/routes/qidian/chapter.ts index 867f3c882..5eba4c9f1 100644 --- a/lib/routes/qidian/chapter.ts +++ b/lib/routes/qidian/chapter.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: '/chapter/:id', + categories: ['government'], + example: '/qidian/chapter/1010400217', + parameters: { id: '小说 id, 可在对应小说页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['book.qidian.com/info/:id'], + }, + name: '章节', + maintainers: ['fuzy112'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const response = await got(`https://m.qidian.com/book/${id}.html`); @@ -23,11 +45,11 @@ export default async (ctx) => { link: `https://vipreader.qidian.com/chapter/${id}/${c.id}`, })); - ctx.set('data', { + return { title: `起点 ${name}`, link: `https://book.qidian.com/info/${id}`, description: $('#bookSummary content').text(), image: coverUrl, item: chapterItem, - }); -}; + }; +} diff --git a/lib/routes/qidian/forum.ts b/lib/routes/qidian/forum.ts index 685b433ed..1a4b49d29 100644 --- a/lib/routes/qidian/forum.ts +++ b/lib/routes/qidian/forum.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: '/forum/:id', + categories: ['government'], + example: '/qidian/forum/1010400217', + parameters: { id: '小说 id, 可在对应小说页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['book.qidian.com/info/:id'], + }, + name: '讨论区', + maintainers: ['fuzy112'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const url = `https://forum.qidian.com/NewForum/List.aspx?BookId=${id}`; @@ -29,10 +51,10 @@ export default async (ctx) => { }); } - ctx.set('data', { + return { title: `起点 《${name}》讨论区`, link: url, image: cover_url, item: items, - }); -}; + }; +} diff --git a/lib/routes/qidian/free-next.ts b/lib/routes/qidian/free-next.ts index 2f4daaf10..3971afdf0 100644 --- a/lib/routes/qidian/free-next.ts +++ b/lib/routes/qidian/free-next.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: '/free-next/:type?', + categories: ['government'], + example: '/qidian/free-next', + parameters: { type: '默认不填为起点中文网,填 mm 为起点女生网' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.qidian.com/free'], + target: '/free', + }, + name: '限时免费下期预告', + maintainers: ['LogicJake'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); let link, title; @@ -33,10 +56,10 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title, description: `限时免费下期预告-${title}`, link, item: out, - }); -}; + }; +} diff --git a/lib/routes/qidian/free.ts b/lib/routes/qidian/free.ts index 34b00bf6d..2edc3b93c 100644 --- a/lib/routes/qidian/free.ts +++ b/lib/routes/qidian/free.ts @@ -1,8 +1,31 @@ +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: '/free/:type?', + categories: ['government'], + example: '/qidian/free', + parameters: { type: '默认不填为起点中文网,填 mm 为起点女生网' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.qidian.com/free'], + target: '/free', + }, + name: '限时免费', + maintainers: ['LogicJake'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type'); let link, title; @@ -36,10 +59,10 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title, description: `限时免费-${title}`, link, item: out, - }); -}; + }; +} diff --git a/lib/routes/qingting/channel.ts b/lib/routes/qingting/channel.ts index c47412e67..71ddf6e8a 100644 --- a/lib/routes/qingting/channel.ts +++ b/lib/routes/qingting/channel.ts @@ -1,9 +1,28 @@ +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: '/channel/:id', + categories: ['picture'], + example: '/qingting/channel/293411', + 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 channelUrl = `https://i.qingting.fm/capi/v3/channel/${ctx.req.param('id')}`; let response = await got(channelUrl); const title = response.data.data.title; @@ -16,7 +35,7 @@ export default async (ctx) => { pubDate: timezone(parseDate(item.update_time), +8), })); - ctx.set('data', { + return { title: `${title} - 蜻蜓FM`, link: `https://www.qingting.fm/channels/${ctx.req.param('id')}`, item: await Promise.all( @@ -29,5 +48,5 @@ export default async (ctx) => { }) ) ), - }); -}; + }; +} diff --git a/lib/routes/qingting/podcast.ts b/lib/routes/qingting/podcast.ts index f432a32f0..4264c2312 100644 --- a/lib/routes/qingting/podcast.ts +++ b/lib/routes/qingting/podcast.ts @@ -1,10 +1,32 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import crypto from 'crypto'; 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: '/podcast/:id', + categories: ['picture'], + example: '/qingting/podcast/293411', + parameters: { id: '专辑id, 可在专辑页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: true, + supportScihub: false, + }, + radar: { + source: ['qingting.fm/channels/:id'], + }, + name: '播客', + maintainers: ['RookieZoe', 'huyyi'], + handler, +}; + +async function handler(ctx) { const channelUrl = `https://i.qingting.fm/capi/v3/channel/${ctx.req.param('id')}`; let response = await got({ method: 'get', @@ -67,12 +89,12 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${title} - 蜻蜓FM`, description: desc, itunes_author: authors, image: channel_img, link: `https://www.qingting.fm/channels/${ctx.req.param('id')}`, item: resultItems, - }); -}; + }; +} diff --git a/lib/routes/qipamaijia/index.ts b/lib/routes/qipamaijia/index.ts index d5ed7f4eb..e7870a6f4 100644 --- a/lib/routes/qipamaijia/index.ts +++ b/lib/routes/qipamaijia/index.ts @@ -1,9 +1,32 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; const rootUrl = 'https://www.qipamaijia.com'; -export default async (ctx) => { +export const route: Route = { + path: '/:cate?', + categories: ['anime'], + example: '/qipamaijia/fuli', + parameters: { cate: '频道名,可在对应网址中找到,默认为最新' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['qipamaijia.com/', 'qipamaijia.com/:cate'], + target: '/:cate', + }, + name: 'Unknown', + maintainers: ['Fatpandac', 'nczitzk'], + handler, +}; + +async function handler(ctx) { const cate = ctx.req.param('cate') ?? ''; const url = `${rootUrl}/${cate}`; @@ -24,9 +47,9 @@ export default async (ctx) => { })) .get(); - ctx.set('data', { + return { title: `奇葩买家秀 - ${title}`, link: url, item: items, - }); -}; + }; +} diff --git a/lib/routes/qiyoujiage/price.ts b/lib/routes/qiyoujiage/price.ts index ed714aa8f..795cf519e 100644 --- a/lib/routes/qiyoujiage/price.ts +++ b/lib/routes/qiyoujiage/price.ts @@ -1,8 +1,16 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import md5 from '@/utils/md5'; -export default async (ctx) => { +export const route: Route = { + path: '/:path{.+}', + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const path = ctx.req.param('path'); const link = `http://www.qiyoujiage.com/${path}.shtml`; @@ -19,10 +27,10 @@ export default async (ctx) => { }, ]; - ctx.set('data', { + return { title: $('title').text(), description: $('meta[name="Description"]').attr('content'), link, item, - }); -}; + }; +} diff --git a/lib/routes/qlu/notice.ts b/lib/routes/qlu/notice.ts index 6d64549cf..9fe860052 100644 --- a/lib/routes/qlu/notice.ts +++ b/lib/routes/qlu/notice.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://www.qlu.edu.cn'; -export default async (ctx) => { +export const route: Route = { + path: '/notice', + categories: ['forecast'], + example: '/qlu/notice', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['qlu.edu.cn/tzggsh/list1.htm'], + }, + name: '通知公告', + maintainers: ['SunBK201'], + handler, +}; + +async function handler() { const response = await got({ method: 'get', url: `${host}/tzggsh/list1.htm`, @@ -41,10 +63,10 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `齐鲁工业大学 - 通知公告`, link: `${host}/tzggsh/list1.htm`, description: '齐鲁工业大学 - 通知公告', item: items, - }); -}; + }; +} diff --git a/lib/routes/qm120/news.ts b/lib/routes/qm120/news.ts index 2645a18ce..6f13720d4 100644 --- a/lib/routes/qm120/news.ts +++ b/lib/routes/qm120/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/:category?', + categories: ['traditional-media'], + example: '/qm120/news', + parameters: { category: '分类,见下表,默认为健康焦点' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['qm120.com/'], + }, + name: '新闻', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const category = ctx.req.param('category') ?? 'jdxw'; const rootUrl = 'http://www.qm120.com'; @@ -43,9 +65,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${$('.zt_liebiao_tit').text()} - 全民健康网`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/qoo-app/apps/card.ts b/lib/routes/qoo-app/apps/card.ts index 9f624065b..ce7b8e601 100644 --- a/lib/routes/qoo-app/apps/card.ts +++ b/lib/routes/qoo-app/apps/card.ts @@ -1,10 +1,29 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; import { appsUrl } from '../utils'; -export default async (ctx) => { +export const route: Route = { + path: '/apps/:lang?/card/:id', + categories: ['program-update'], + example: '/qoo-app/apps/en/card/7675', + parameters: { lang: 'Language, see the table above, empty means `中文`', id: 'Game ID, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Game Store - Cards', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { id, lang = '' } = ctx.req.param(); const link = `${appsUrl}${lang ? `/${lang}` : ''}/app-card/${id}`; @@ -29,10 +48,10 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('head title').text(), link, language: $('html').attr('lang'), item: items, - }); -}; + }; +} diff --git a/lib/routes/qoo-app/apps/comment.ts b/lib/routes/qoo-app/apps/comment.ts index fc594bc76..93e1a0162 100644 --- a/lib/routes/qoo-app/apps/comment.ts +++ b/lib/routes/qoo-app/apps/comment.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 { appsUrl } from '../utils'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/apps/:lang?/comment/:id', + categories: ['program-update'], + example: '/qoo-app/apps/en/comment/7675', + parameters: { lang: 'Language, see the table below, empty means `中文`', id: 'Game ID, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Game Store - Review', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { id, lang = '' } = ctx.req.param(); const link = `${appsUrl}${lang ? `/${lang}` : ''}/app-comment/${id}`; @@ -37,10 +56,10 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('head title').text(), link, language: $('html').attr('lang'), item: items, - }); -}; + }; +} diff --git a/lib/routes/qoo-app/apps/note.ts b/lib/routes/qoo-app/apps/note.ts index 3a531f80c..4ae835105 100644 --- a/lib/routes/qoo-app/apps/note.ts +++ b/lib/routes/qoo-app/apps/note.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'; import { appsUrl } from '../utils'; -export default async (ctx) => { +export const route: Route = { + path: '/apps/:lang?/note/:id', + categories: ['program-update'], + example: '/qoo-app/apps/en/note/7675', + parameters: { lang: 'Language, see the table above, empty means `中文`', id: 'Game ID, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Game Store - Notes', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { id, lang = '' } = ctx.req.param(); const link = `${appsUrl}${lang ? `/${lang}` : ''}/app-note/${id}`; @@ -39,10 +58,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), link, language: $('html').attr('lang'), item: items, - }); -}; + }; +} diff --git a/lib/routes/qoo-app/apps/post.ts b/lib/routes/qoo-app/apps/post.ts index d7fd247be..749470533 100644 --- a/lib/routes/qoo-app/apps/post.ts +++ b/lib/routes/qoo-app/apps/post.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'; import { appsUrl, newsUrl, fixImg } from '../utils'; -export default async (ctx) => { +export const route: Route = { + path: '/apps/:lang?/post/:id', + categories: ['program-update'], + example: '/qoo-app/apps/en/post/7675', + parameters: { lang: 'Language, see the table above, empty means `中文`', id: 'Game ID, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Game Store - Article', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { id, lang = '' } = ctx.req.param(); const link = `${appsUrl}${lang ? `/${lang}` : ''}/app-post/${id}`; @@ -43,10 +62,10 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('head title').text(), link, language: $('html').attr('lang'), item: items, - }); -}; + }; +} diff --git a/lib/routes/qoo-app/news.ts b/lib/routes/qoo-app/news.ts index 73b72c164..e9be434a5 100644 --- a/lib/routes/qoo-app/news.ts +++ b/lib/routes/qoo-app/news.ts @@ -1,10 +1,29 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import { newsUrl, siteIcon, fixImg } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/news/:lang?', + categories: ['program-update'], + example: '/qoo-app/news/en', + parameters: { lang: 'Language, see the table below, empty means `中文`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'News', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { lang = '' } = ctx.req.param(); const apiUrl = `${newsUrl}${lang ? `/${lang}` : ''}/wp-json/wp/v2/posts`; @@ -27,7 +46,7 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: 'QooApp : Anime Game Platform', description: lang === 'en' @@ -37,5 +56,5 @@ export default async (ctx) => { link: `${newsUrl}${lang ? `/${lang}` : ''}`, language: lang === 'en' ? 'en' : 'zh', item: items, - }); -}; + }; +} diff --git a/lib/routes/qoo-app/notes/note.ts b/lib/routes/qoo-app/notes/note.ts index 37b584d20..2c03e7985 100644 --- a/lib/routes/qoo-app/notes/note.ts +++ b/lib/routes/qoo-app/notes/note.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 { ssoUrl, notesUrl } from '../utils'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/notes/:lang?/note/:id', + categories: ['program-update'], + example: '/qoo-app/notes/en/note/2329113', + parameters: { lang: 'Language, see the table above, empty means `中文`', id: 'Note ID, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Note Comments', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const api = `${ssoUrl}/api/v1/comments`; const link = `${notesUrl}/note/${id}`; @@ -37,10 +56,10 @@ export default async (ctx) => { guid: `qoo-app:notes:note:${id}:${item.id}`, })); - ctx.set('data', { + return { title: $('head title').text(), link, language: $('html').attr('lang'), item: items, - }); -}; + }; +} diff --git a/lib/routes/qoo-app/notes/topic.ts b/lib/routes/qoo-app/notes/topic.ts index 939fd389a..b1d69f421 100644 --- a/lib/routes/qoo-app/notes/topic.ts +++ b/lib/routes/qoo-app/notes/topic.ts @@ -1,8 +1,16 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { notesUrl, extractNotes } from '../utils'; -export default async (ctx) => { +export const route: Route = { + path: '/notes/:lang?/topic/:topic', + name: 'Unknown', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { topic, lang } = ctx.req.param(); const link = `${notesUrl}${lang ? `/${lang}` : ''}/topic/${topic}`; @@ -11,10 +19,10 @@ export default async (ctx) => { const items = extractNotes($); - ctx.set('data', { + return { title: $('head title').text(), link, language: $('html').attr('lang'), item: items, - }); -}; + }; +} diff --git a/lib/routes/qoo-app/notes/user.ts b/lib/routes/qoo-app/notes/user.ts index 386085d29..241f5334c 100644 --- a/lib/routes/qoo-app/notes/user.ts +++ b/lib/routes/qoo-app/notes/user.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { notesUrl, extractNotes } from '../utils'; -export default async (ctx) => { +export const route: Route = { + path: '/notes/:lang?/user/:uid', + categories: ['program-update'], + example: '/qoo-app/notes/en/user/35399143', + parameters: { lang: 'Language, see the table above, empty means `中文`', uid: 'User ID, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'User Notes', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { uid, lang } = ctx.req.param(); const link = `${notesUrl}${lang ? `/${lang}` : ''}/user/${uid}`; @@ -11,10 +30,10 @@ export default async (ctx) => { const items = extractNotes($); - ctx.set('data', { + return { title: $('head title').text(), link, language: $('html').attr('lang'), item: items, - }); -}; + }; +} diff --git a/lib/routes/qoo-app/user/app-comment.ts b/lib/routes/qoo-app/user/app-comment.ts index bb582e72a..f60a24b27 100644 --- a/lib/routes/qoo-app/user/app-comment.ts +++ b/lib/routes/qoo-app/user/app-comment.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 { userUrl, appsUrl } from '../utils'; import { art } from '@/utils/render'; import * as path from 'node:path'; -export default async (ctx) => { +export const route: Route = { + path: '/user/:lang?/appComment/:uid', + categories: ['program-update'], + example: '/qoo-app/user/en/appComment/35399143', + parameters: { lang: 'Language, see the table above, empty means `中文`', uid: 'User ID, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'User Game Comments', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { uid, lang = '' } = ctx.req.param(); const link = `${userUrl}${lang ? `/${lang}` : ''}/${uid}`; @@ -34,11 +53,11 @@ export default async (ctx) => { author: username, })); - ctx.set('data', { + return { title: $('head title').text(), link, image: decodeURIComponent($('.person div.slot').attr('data-args')).replace('avatar=', '').split('?')[0], language: $('html').attr('lang'), item: items, - }); -}; + }; +} diff --git a/lib/routes/qq/ac/comic.ts b/lib/routes/qq/ac/comic.ts index 8e1e58601..1ecf04838 100644 --- a/lib/routes/qq/ac/comic.ts +++ b/lib/routes/qq/ac/comic.ts @@ -1,9 +1,21 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; import { rootUrl, mobileRootUrl } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/ac/comic/:id?', + radar: { + source: ['ac.qq.com/Comic/ComicInfo/id/:id', 'ac.qq.com/'], + target: '/ac/comic/:id', + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const currentUrl = `${rootUrl}/Comic/comicInfo/id/${id}`; @@ -34,10 +46,10 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `${$('h1').text()} - 腾讯动漫`, link: currentUrl, item: items, description: `

    ${$('.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: (?[^-]+) --/; const { blog_name = 'Unknown Title' } = $('channel > description').text().match(name_re).groups; - ctx.set('data', { + return { title: `Science Blogs: ${blog_name}`, description: `A Science.org blog called ${blog_name}`, image: `${baseUrl}/apple-touch-icon.png`, link: `${baseUrl}/blogs/${name}`, language: 'en-US', item: items, - }); -}; + }; +} diff --git a/lib/routes/science/cover.ts b/lib/routes/science/cover.ts index 3dc7cc250..4c38568c6 100644 --- a/lib/routes/science/cover.ts +++ b/lib/routes/science/cover.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -17,7 +18,28 @@ import { art } from '@/utils/render'; import * as path from 'node:path'; import { baseUrl } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/cover', + categories: ['finance'], + example: '/science/cover', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['science.org/'], + }, + name: 'Cover Story', + maintainers: ['y9c', 'TonyRL'], + handler, +}; + +async function handler() { const pageURL = `${baseUrl}/journals`; const { data: pageResponse } = await got(pageURL, { @@ -61,12 +83,12 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: $('head title').text(), description: $('meta[property="og:description"]').attr('content'), image: `${baseUrl}/apple-touch-icon.png`, link: pageURL, language: 'en-US', item: items, - }); -}; + }; +} diff --git a/lib/routes/science/current.ts b/lib/routes/science/current.ts index 403bf50f3..83b9564ee 100644 --- a/lib/routes/science/current.ts +++ b/lib/routes/science/current.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; // journals form AAAS publishing group // @@ -13,7 +14,29 @@ import got from '@/utils/got'; import { baseUrl, fetchDesc, getItem } from './utils'; import puppeteer from '@/utils/puppeteer'; -export default async (ctx) => { +export const route: Route = { + path: '/current/:journal?', + categories: ['finance'], + example: '/science/current/science', + parameters: { journal: 'Short name for a journal' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: true, + }, + radar: { + source: ['science.org/journal/:journal', 'science.org/toc/:journal/current'], + target: '/current/:journal', + }, + name: 'Current Issue', + maintainers: ['y9c', 'TonyRL'], + handler, +}; + +async function handler(ctx) { const { journal = 'science' } = ctx.req.param(); const pageURL = `${baseUrl}/toc/${journal}/current`; @@ -33,12 +56,12 @@ export default async (ctx) => { const items = await fetchDesc(list, browser, cache.tryGet); await browser.close(); - ctx.set('data', { + return { title: `${pageTitleName} | Current Issue`, description: `Current Issue of ${pageTitleName}`, image: `${baseUrl}/apple-touch-icon.png`, link: pageURL, language: 'en-US', item: items, - }); -}; + }; +} diff --git a/lib/routes/science/early.ts b/lib/routes/science/early.ts index 64c8cdb80..fdcee6398 100644 --- a/lib/routes/science/early.ts +++ b/lib/routes/science/early.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { load } from 'cheerio'; import got from '@/utils/got'; @@ -5,7 +6,29 @@ import puppeteer from '@/utils/puppeteer'; import { baseUrl, fetchDesc, getItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/early/:journal?', + categories: ['finance'], + example: '/science/early', + parameters: { journal: 'Short name for a journal' }, + features: { + requireConfig: false, + requirePuppeteer: true, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: true, + }, + radar: { + source: ['science.org/journal/:journal', 'science.org/toc/:journal/0/0'], + target: '/early/:journal', + }, + name: 'First Release', + maintainers: ['y9c', 'TonyRL'], + handler, +}; + +async function handler(ctx) { const { journal = 'science' } = ctx.req.param(); const pageUrl = `${baseUrl}/toc/${journal}/0/0`; @@ -24,12 +47,12 @@ export default async (ctx) => { const items = await fetchDesc(list, browser, cache.tryGet); await browser.close(); - ctx.set('data', { + return { title: $('head title').text(), description: $('.body02').text().trim(), image: `${baseUrl}/apple-touch-icon.png`, link: pageUrl, language: 'en-US', item: items, - }); -}; + }; +} diff --git a/lib/routes/sciencedirect/journal.ts b/lib/routes/sciencedirect/journal.ts index 069de8316..42a238569 100644 --- a/lib/routes/sciencedirect/journal.ts +++ b/lib/routes/sciencedirect/journal.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 { decodeCFEmail } from './cf-email'; -export default async (ctx) => { +export const route: Route = { + path: '/journal/:id', + categories: ['finance'], + example: '/sciencedirect/journal/research-policy', + parameters: { id: 'Journal id, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['sciencedirect.com/journal/:id', 'sciencedirect.com/'], + }, + name: 'Journal', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const rootUrl = 'https://www.sciencedirect.com'; @@ -62,9 +84,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${response.data.match(/\\"displayName\\":\\"(.*?)\\",\\"/)[1]} - ScienceDirect`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/sciencenet/blog.ts b/lib/routes/sciencenet/blog.ts index e54f47b51..255991c61 100644 --- a/lib/routes/sciencenet/blog.ts +++ b/lib/routes/sciencenet/blog.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: '/blog/:type?/:time?/:sort?', + categories: ['traditional-media'], + example: '/sciencenet/blog', + parameters: { type: '类型,见下表,默认为推荐', time: '时间,见下表,默认为所有时间', sort: '排序,见下表,默认为按发表时间排序' }, + 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') ?? 'recommend'; const time = ctx.req.param('time') ?? '5'; const sort = ctx.req.param('sort') ?? '1'; @@ -54,9 +73,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '科学网 - 精选博文', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/sciencenet/user.ts b/lib/routes/sciencenet/user.ts index 64ca36677..bdddf1ac5 100644 --- a/lib/routes/sciencenet/user.ts +++ b/lib/routes/sciencenet/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 iconv from 'iconv-lite'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/user/:id', + categories: ['traditional-media'], + example: '/sciencenet/user/tony8310', + parameters: { id: '用户 id,可在对用户博客页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['blog.sciencenet.cn/u/:id', 'blog.sciencenet.cn/'], + }, + name: '用户博客', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const rootUrl = 'https://blog.sciencenet.cn'; @@ -58,9 +80,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `科学网 - ${items[0].author}的博文`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/scmp/coronavirus.ts b/lib/routes/scmp/coronavirus.ts index 91452f7f4..7745656b4 100644 --- a/lib/routes/scmp/coronavirus.ts +++ b/lib/routes/scmp/coronavirus.ts @@ -1,3 +1,22 @@ -export default (ctx) => { - ctx.redirect('/scmp/topics/coronavirus-pandemic-all-stories'); +import { Route } from '@/types'; +export const route: Route = { + path: '/coronavirus', + categories: ['other'], + example: '/scmp/coronavirus', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'South China Morning Post - China coronavirus outbreak', + maintainers: ['DIYgod'], + handler, }; + +function handler(ctx) { + ctx.redirect('/scmp/topics/coronavirus-pandemic-all-stories'); +} diff --git a/lib/routes/scmp/index.ts b/lib/routes/scmp/index.ts index cb50e24da..c7dfd34f7 100644 --- a/lib/routes/scmp/index.ts +++ b/lib/routes/scmp/index.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 { parseItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/:category_id', + categories: ['bbs'], + example: '/scmp/3', + parameters: { category_id: 'Category' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['scmp.com/rss/:category_id/feed'], + }, + name: 'News', + maintainers: ['proletarius101'], + handler, +}; + +async function handler(ctx) { const categoryId = ctx.req.param('category_id'); const rssUrl = `https://www.scmp.com/rss/${categoryId}/feed`; const { data: response } = await got(rssUrl); @@ -49,7 +71,7 @@ export default async (ctx) => { items, }); - ctx.set('data', { + return { title: $('channel > title').text(), link: $('channel > link').text(), description: $('channel > description').text(), @@ -58,5 +80,5 @@ export default async (ctx) => { icon: 'https://assets.i-scmp.com/static/img/icons/scmp-icon-256x256.png', logo: 'https://customerservice.scmp.com/img/logo_scmp@2x.png', image: $('channel > image > url').text(), - }); -}; + }; +} diff --git a/lib/routes/scmp/topic.ts b/lib/routes/scmp/topic.ts index 4b4207924..25781cf10 100644 --- a/lib/routes/scmp/topic.ts +++ b/lib/routes/scmp/topic.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 { parseItem } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/topics/:topic', + categories: ['bbs'], + example: '/scmp/topics/coronavirus-pandemic-all-stories', + parameters: { topic: 'Topic, can be found in URL' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['scmp.com/topics/:topic'], + }, + name: 'Topics', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const topic = ctx.req.param('topic'); const limit = Number.parseInt(ctx.req.query('limit'), 10) || 30; const pageUrl = `https://www.scmp.com/topics/${topic}`; @@ -54,7 +76,7 @@ export default async (ctx) => { apiResponse, }); - ctx.set('data', { + return { title: topicData.name, link: pageUrl, description: topicData.description.text, @@ -63,5 +85,5 @@ export default async (ctx) => { icon: 'https://assets.i-scmp.com/static/img/icons/scmp-icon-256x256.png', logo: 'https://customerservice.scmp.com/img/logo_scmp@2x.png', image: 'https://assets-v2.i-scmp.com/production/_next/static/media/default-image.d1be8967.png', - }); -}; + }; +} diff --git a/lib/routes/scnu/cs/match.ts b/lib/routes/scnu/cs/match.ts index 16db6bc98..72dca14bf 100644 --- a/lib/routes/scnu/cs/match.ts +++ b/lib/routes/scnu/cs/match.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: '/cs/match', + categories: ['forecast'], + example: '/scnu/cs/match', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['cs.scnu.edu.cn/xueshenggongzuo/chengchangfazhan/kejichuangxin/', 'cs.scnu.edu.cn/'], + }, + name: '计算机学院竞赛通知', + maintainers: ['fengkx'], + handler, +}; + +async function handler() { const baseUrl = 'http://cs.scnu.edu.cn'; const url = `${baseUrl}/xueshenggongzuo/chengchangfazhan/kejichuangxin/`; const res = await got({ @@ -15,7 +37,7 @@ export default async (ctx) => { const $ = load(res.data); const list = $('.listshow li a'); - ctx.set('data', { + return { title: $('title').text(), link: url, description: '华南师范大学计算机学院 学科竞赛', @@ -32,5 +54,5 @@ export default async (ctx) => { link: item.attr('href'), }; }), - }); -}; + }; +} diff --git a/lib/routes/scnu/jw.ts b/lib/routes/scnu/jw.ts index 7006cd4fa..9c6d0ea26 100644 --- a/lib/routes/scnu/jw.ts +++ b/lib/routes/scnu/jw.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: '/jw', + categories: ['forecast'], + example: '/scnu/jw', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['jw.scnu.edu.cn/ann/index.html', 'jw.scnu.edu.cn/'], + }, + name: '教务处通知', + maintainers: ['fengkx'], + handler, +}; + +async function handler() { const baseUrl = 'http://jw.scnu.edu.cn'; const url = `${baseUrl}/ann/index.html`; const res = await got({ @@ -15,7 +37,7 @@ export default async (ctx) => { const $ = load(res.data); const list = $('.notice_01').find('li'); - ctx.set('data', { + return { title: $('title').first().text(), link: url, description: '华南师范大学教务处 - 通知公告', @@ -29,5 +51,5 @@ export default async (ctx) => { link: item.find('a').attr('href'), }; }), - }); -}; + }; +} diff --git a/lib/routes/scnu/library.ts b/lib/routes/scnu/library.ts index e791b8b3a..96fdf8a1e 100644 --- a/lib/routes/scnu/library.ts +++ b/lib/routes/scnu/library.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: '/library', + categories: ['forecast'], + example: '/scnu/library', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['lib.scnu.edu.cn/news/zuixingonggao', 'lib.scnu.edu.cn/'], + }, + name: '图书馆通知', + maintainers: ['fengkx'], + handler, +}; + +async function handler() { const baseUrl = 'https://lib.scnu.edu.cn'; const url = `${baseUrl}/news/zuixingonggao/`; const res = await got({ @@ -15,7 +37,7 @@ export default async (ctx) => { const $ = load(res.data); const list = $('.article-list').find('li'); - ctx.set('data', { + return { title: $('title').text(), link: url, description: '华南师范大学图书馆 - 通知公告', @@ -29,5 +51,5 @@ export default async (ctx) => { link: item.find('a').attr('href'), }; }), - }); -}; + }; +} diff --git a/lib/routes/scnu/physics-school-announcements-and-news.ts b/lib/routes/scnu/physics-school-announcements-and-news.ts index 082ff971b..47e0f7ffa 100644 --- a/lib/routes/scnu/physics-school-announcements-and-news.ts +++ b/lib/routes/scnu/physics-school-announcements-and-news.ts @@ -44,7 +44,7 @@ const getItemsFromURLs = async (URLs, cache) => { return items; }; -export const announcementsRouter = async (ctx) => { +export const announcementsRouter = async () => { const URLs = { 党政: 'https://physics.scnu.edu.cn/NEWS/Notices/PartyAndGovernment/', 教务: 'https://physics.scnu.edu.cn/NEWS/Notices/Education/', @@ -54,14 +54,14 @@ export const announcementsRouter = async (ctx) => { 学工: 'https://physics.scnu.edu.cn/NEWS/Notices/Students/', }; const items = await getItemsFromURLs(URLs, cache); - ctx.set('data', { + return { title: '华南师范大学物理与电信工程学院通知', link: 'https://physics.scnu.edu.cn/NEWS/Notices/', item: items, - }); + }; }; -export const newsRouter = async (ctx) => { +export const newsRouter = async () => { const URLs = { 学院新闻: 'https://physics.scnu.edu.cn/NEWS/News/College/', 教务新闻: 'https://physics.scnu.edu.cn/NEWS/News/Education/', @@ -70,14 +70,14 @@ export const newsRouter = async (ctx) => { 院友新闻: 'https://physics.scnu.edu.cn/NEWS/News/People/', }; const items = await getItemsFromURLs(URLs, cache); - ctx.set('data', { + return { title: '华南师范大学物理与电信工程学院新闻动态', link: 'https://physics.scnu.edu.cn/NEWS/News/', item: items, - }); + }; }; -export const researchNewsRouter = async (ctx) => { +export const researchNewsRouter = async () => { const URLs = { 学术报告: 'https://physics.scnu.edu.cn/RESEARCH/EVENTS/Seminars/', 学术会议: 'https://physics.scnu.edu.cn/RESEARCH/EVENTS/Conferences/', @@ -87,9 +87,9 @@ export const researchNewsRouter = async (ctx) => { 科研成果: 'https://physics.scnu.edu.cn/RESEARCH/Achievements/', }; const items = await getItemsFromURLs(URLs, cache); - ctx.set('data', { + return { title: '华南师范大学物理与电信工程学院科学研究动态', link: 'https://physics.scnu.edu.cn/RESEARCH/', item: items, - }); + }; }; diff --git a/lib/routes/scnu/ss.ts b/lib/routes/scnu/ss.ts index ed09f72d1..04e5316e3 100644 --- a/lib/routes/scnu/ss.ts +++ b/lib/routes/scnu/ss.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: '/ss', + categories: ['forecast'], + example: '/scnu/ss', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['ss.scnu.edu.cn/tongzhigonggao', 'ss.scnu.edu.cn/'], + }, + name: '软件学院通知公告', + maintainers: ['shengmaosu'], + handler, +}; + +async function handler() { const link = 'http://ss.scnu.edu.cn/tongzhigonggao/'; const response = await got(link); const $ = load(response.data); const list = $('.listshow li a'); - ctx.set('data', { + return { title: '华南师范大学软件学院', link, item: @@ -24,5 +46,5 @@ export default async (ctx) => { pubDate: parseDate(item.find('.time').text()), }; }), - }); -}; + }; +} diff --git a/lib/routes/scnu/yjs.ts b/lib/routes/scnu/yjs.ts index 3122134d4..32c288ce8 100644 --- a/lib/routes/scnu/yjs.ts +++ b/lib/routes/scnu/yjs.ts @@ -1,13 +1,35 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/yjs', + categories: ['forecast'], + example: '/scnu/yjs', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['yz.scnu.edu.cn/tongzhigonggao/ssgg', 'yz.scnu.edu.cn/'], + }, + name: '研究生院通知公告', + maintainers: ['shengmaosu'], + handler, +}; + +async function handler() { const link = 'https://yz.scnu.edu.cn/tongzhigonggao/ssgg/'; const response = await got(link); const $ = load(response.data); const list = $('.listmod div a'); - ctx.set('data', { + return { title: '华南师范大学研究生院', link, description: '华南师范大学研究生院通知公告', @@ -20,5 +42,5 @@ export default async (ctx) => { link: item.attr('href'), }; }), - }); -}; + }; +} diff --git a/lib/routes/sctv/programme.ts b/lib/routes/sctv/programme.ts index 2e3a0a93b..5cb944415 100644 --- a/lib/routes/sctv/programme.ts +++ b/lib/routes/sctv/programme.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: '/programme/:id?/:limit?/:isFull?', + categories: ['bbs'], + example: '/sctv/programme/1', + parameters: { id: '节目 id,可在对应节目页中找到,默认为 `1`,即四川新闻联播', limit: '期数,默认为 15,即单次获取最新 15 期', isFull: '是否仅获取完整视频,填写 true/yes 表示是、false/no 表示否,默认是' }, + 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') ?? '1'; const limit = ctx.req.param('limit') ? Number.parseInt(ctx.req.param('limit')) : 15; const isFull = /t|y/i.test(ctx.req.param('isFull') ?? 'true'); @@ -75,10 +94,10 @@ export default async (ctx) => { } } - ctx.set('data', { + return { title: `四川广播电视台 - ${name}`, link: currentUrl, item: items.slice(0, ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 100), image: cover, - }); -}; + }; +} diff --git a/lib/routes/scut/jwc/news.ts b/lib/routes/scut/jwc/news.ts index 9d25af9f5..c45525c5b 100644 --- a/lib/routes/scut/jwc/news.ts +++ b/lib/routes/scut/jwc/news.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'; @@ -46,7 +47,25 @@ const generateArticleLink = (id) => `

    链接: 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' ? '' : 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 ? `

    ${item.target.excerpt}

    ` : '', })); - ctx.set('data', { + return { title: `知乎热榜 - ${titles[category]}`, link: `https://www.zhihu.com/hot?list=${category}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/zhihu/hotlist.ts b/lib/routes/zhihu/hotlist.ts index a68ab0244..78b06a3a4 100644 --- a/lib/routes/zhihu/hotlist.ts +++ b/lib/routes/zhihu/hotlist.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import utils from './utils'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/hotlist', + categories: ['new-media'], + example: '/zhihu/hotlist', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.zhihu.com/hot'], + }, + name: '知乎热榜', + maintainers: ['DIYgod'], + handler, +}; + +async function handler() { const { data: { data }, } = await got({ @@ -10,7 +32,7 @@ export default async (ctx) => { url: 'https://www.zhihu.com/api/v3/explore/guest/feeds?limit=40', }); - ctx.set('data', { + return { title: '知乎热榜', link: 'https://www.zhihu.com/billboard', description: '知乎热榜', @@ -43,5 +65,5 @@ export default async (ctx) => { }; } }), - }); -}; + }; +} diff --git a/lib/routes/zhihu/pin/daily.ts b/lib/routes/zhihu/pin/daily.ts index e6ee798cb..82d49dc58 100644 --- a/lib/routes/zhihu/pin/daily.ts +++ b/lib/routes/zhihu/pin/daily.ts @@ -1,7 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { generateData } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/pin/daily', + categories: ['new-media'], + example: '/zhihu/pin/daily', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['daily.zhihu.com/*'], + target: '/daily', + }, + name: '知乎想法 - 24 小时新闻汇总', + maintainers: ['xyqfer'], + handler, +}; + +async function handler() { const { data: { data }, } = await got({ @@ -9,10 +32,10 @@ export default async (ctx) => { url: 'https://api.zhihu.com/pins/special/972884951192113152/moments?order_by=newest&reverse_order=0&limit=20', }); - ctx.set('data', { + return { title: '知乎想法-24小时新闻汇总', link: 'https://www.zhihu.com/pin/special/972884951192113152', description: '汇集每天的社会大事、行业资讯,让你用最简单的方式获得想法里的新闻', item: generateData(data), - }); -}; + }; +} diff --git a/lib/routes/zhihu/pin/hotlist.ts b/lib/routes/zhihu/pin/hotlist.ts index 99f98bedd..b33e4426c 100644 --- a/lib/routes/zhihu/pin/hotlist.ts +++ b/lib/routes/zhihu/pin/hotlist.ts @@ -1,7 +1,29 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { generateData } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/pin/hotlist', + categories: ['new-media'], + example: '/zhihu/pin/hotlist', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.zhihu.com/zhihu/bookstore/newest'], + }, + name: '知乎想法热榜', + maintainers: ['xyqfer'], + handler, +}; + +async function handler() { const { data: { data }, } = await got({ @@ -9,10 +31,10 @@ export default async (ctx) => { url: 'https://api.zhihu.com/pins/hot_list?reverse_order=0', }); - ctx.set('data', { + return { title: '知乎想法热榜', link: 'https://www.zhihu.com/', description: '整点更新', item: generateData(data), - }); -}; + }; +} diff --git a/lib/routes/zhihu/pin/people.ts b/lib/routes/zhihu/pin/people.ts index 3b5f91eea..d6145cd89 100644 --- a/lib/routes/zhihu/pin/people.ts +++ b/lib/routes/zhihu/pin/people.ts @@ -1,7 +1,29 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { generateData } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/people/pins/:id', + categories: ['new-media'], + example: '/zhihu/people/pins/kan-dan-45', + parameters: { id: '作者 id,可在用户主页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.zhihu.com/people/:id/pins'], + }, + name: '用户想法', + maintainers: ['xyqfer'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const { @@ -11,9 +33,9 @@ export default async (ctx) => { url: `https://api.zhihu.com/pins/${id}/moments?limit=10&offset=0`, }); - ctx.set('data', { + return { title: `${data[0].target.author.name}的知乎想法`, link: `https://www.zhihu.com/people/${id}/pins`, item: generateData(data), - }); -}; + }; +} diff --git a/lib/routes/zhihu/posts.ts b/lib/routes/zhihu/posts.ts index 761a6b397..71d5c9ffc 100644 --- a/lib/routes/zhihu/posts.ts +++ b/lib/routes/zhihu/posts.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import utils from './utils'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/posts/:usertype/:id', + categories: ['new-media'], + example: '/zhihu/posts/people/frederchen', + parameters: { usertype: '作者 id,可在用户主页 URL 中找到', id: '用户类型usertype,参考用户主页的URL。目前有两种,见下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.zhihu.com/:usertype/:id/posts'], + }, + name: '用户文章', + maintainers: ['whtsky', 'Colin-XKL'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const usertype = ctx.req.param('usertype'); @@ -44,10 +66,10 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `${authorname} 的知乎文章`, link: `https://www.zhihu.com/${usertype}/${id}/posts`, description: authordescription, item: list, - }); -}; + }; +} diff --git a/lib/routes/zhihu/question.ts b/lib/routes/zhihu/question.ts index 654b9f378..b287b62a3 100644 --- a/lib/routes/zhihu/question.ts +++ b/lib/routes/zhihu/question.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,29 @@ import md5 from '@/utils/md5'; import g_encrypt from './execlib/x-zse-96-v3'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/question/:questionId/:sortBy?', + categories: ['new-media'], + example: '/zhihu/question/59895982', + parameters: { questionId: '问题 id', sortBy: '排序方式:`default`, `created`, `updated`。默认为 `default`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.zhihu.com/question/:questionId'], + target: '/question/:questionId', + }, + name: '问题', + maintainers: [], + handler, +}; + +async function handler(ctx) { const { questionId, sortBy = 'default', // default,created,updated @@ -61,7 +84,7 @@ export default async (ctx) => { const listRes = response.data.data; - ctx.set('data', { + return { title: `知乎-${listRes[0].question.title}`, link: `https://www.zhihu.com/question/${questionId}`, item: listRes.map((item) => { @@ -77,5 +100,5 @@ export default async (ctx) => { link: `https://www.zhihu.com/question/${questionId}/answer/${item.id}`, }; }), - }); -}; + }; +} diff --git a/lib/routes/zhihu/timeline.ts b/lib/routes/zhihu/timeline.ts index 98eca5869..a48fc39e2 100644 --- a/lib/routes/zhihu/timeline.ts +++ b/lib/routes/zhihu/timeline.ts @@ -1,9 +1,28 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { config } from '@/config'; import utils from './utils'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/timeline', + categories: ['new-media'], + example: '/zhihu/timeline', + parameters: {}, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '用户关注时间线', + maintainers: ['SeanChao'], + handler, +}; + +async function handler() { const cookie = config.zhihu.cookies; if (cookie === undefined) { throw new Error('缺少知乎用户登录后的 Cookie 值'); @@ -107,9 +126,9 @@ export default async (ctx) => { return buildItem(e); }); - ctx.set('data', { + return { title: `知乎关注动态`, link: `https://www.zhihu.com/follow`, item: out, - }); -}; + }; +} diff --git a/lib/routes/zhihu/topic.ts b/lib/routes/zhihu/topic.ts index bdc416221..d93d76f4c 100644 --- a/lib/routes/zhihu/topic.ts +++ b/lib/routes/zhihu/topic.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,29 @@ 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: '/topic/:topicId/:isTop?', + categories: ['new-media'], + example: '/zhihu/topic/19828946', + parameters: { topicId: '话题 id', isTop: '仅精华,默认为否,其他值为是' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.zhihu.com/topic/:topicId/:type'], + target: '/topic/:topicId', + }, + name: '话题', + maintainers: ['xyqfer'], + handler, +}; + +async function handler(ctx) { const { topicId, isTop = false } = ctx.req.param(); const link = `https://www.zhihu.com/topic/${topicId}/${isTop ? 'top-answers' : 'newest'}`; @@ -103,10 +126,10 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `知乎话题-${topicId}-${isTop ? '精华' : '讨论'}`, description: topicMeta.content.introduction, link, item: items, - }); -}; + }; +} diff --git a/lib/routes/zhihu/weekly.ts b/lib/routes/zhihu/weekly.ts index a044eb58c..29d4301a5 100644 --- a/lib/routes/zhihu/weekly.ts +++ b/lib/routes/zhihu/weekly.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; const host = 'https://www.zhihu.com'; -export default async (ctx) => { +export const route: Route = { + path: '/weekly', + categories: ['new-media'], + example: '/zhihu/weekly', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.zhihu.com/pub/weekly'], + }, + name: '知乎书店 - 知乎周刊', + maintainers: ['LogicJake'], + handler, +}; + +async function handler() { const link = 'https://www.zhihu.com/pub/weekly'; const response = await got(link); const $ = load(response.data); @@ -22,10 +44,10 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title: '知乎周刊', link, description, item: out, - }); -}; + }; +} diff --git a/lib/routes/zhihu/xhu/activities.ts b/lib/routes/zhihu/xhu/activities.ts index b6dfc0910..d598bd681 100644 --- a/lib/routes/zhihu/xhu/activities.ts +++ b/lib/routes/zhihu/xhu/activities.ts @@ -1,9 +1,32 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import auth from './auth'; import utils from '../utils'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/xhu/people/activities/:hexId', + categories: ['new-media'], + example: '/zhihu/xhu/people/activities/246e6cf44e94cefbf4b959cb5042bc91', + parameters: { hexId: '用户的 16 进制 id,获取方式见下方说明' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.zhihu.com/people/:id'], + target: '/people/activities/:id', + }, + name: 'xhu - 用户动态', + maintainers: ['JimenezLi'], + handler, +}; + +async function handler(ctx) { const xhuCookie = await auth.getCookie(ctx); const hexId = ctx.req.param('hexId'); const link = `https://www.zhihu.com/people/${hexId}`; @@ -19,7 +42,7 @@ export default async (ctx) => { }); const data = response.data.data; - ctx.set('data', { + return { title: `${data[0].actor.name}的知乎动态`, link, image: data[0].actor.avatar_url, @@ -123,5 +146,5 @@ export default async (ctx) => { link: url, }; }), - }); -}; + }; +} diff --git a/lib/routes/zhihu/xhu/answers.ts b/lib/routes/zhihu/xhu/answers.ts index 409b043cd..63fa0a16a 100644 --- a/lib/routes/zhihu/xhu/answers.ts +++ b/lib/routes/zhihu/xhu/answers.ts @@ -1,8 +1,31 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import auth from './auth'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/xhu/people/answers/:hexId', + categories: ['new-media'], + example: '/zhihu/xhu/people/answers/246e6cf44e94cefbf4b959cb5042bc91', + parameters: { hexId: '用户的 16 进制 id,获取方式同 [xhu - 用户动态](#zhi-hu-xhu-yong-hu-dong-tai)' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.zhihu.com/people/:id/answers'], + target: '/people/answers/:id', + }, + name: 'xhu - 用户回答', + maintainers: ['JimenezLi'], + handler, +}; + +async function handler(ctx) { const xhuCookie = await auth.getCookie(ctx); const hexId = ctx.req.param('hexId'); const link = `https://www.zhihu.com/people/${hexId}/answers`; @@ -18,7 +41,7 @@ export default async (ctx) => { }); const data = response.data.data; - ctx.set('data', { + return { title: `${data[0].author.name}的知乎回答`, link, item: data.map((item) => ({ @@ -27,5 +50,5 @@ export default async (ctx) => { pubDate: parseDate(item.created_time * 1000), link: `https://www.zhihu.com/question/${item.question.id}/answer/${item.id}`, })), - }); -}; + }; +} diff --git a/lib/routes/zhihu/xhu/collection.ts b/lib/routes/zhihu/xhu/collection.ts index b42b30656..62a553506 100644 --- a/lib/routes/zhihu/xhu/collection.ts +++ b/lib/routes/zhihu/xhu/collection.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import auth from './auth'; import { generateData } from '../pin/utils'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/xhu/collection/:id', + categories: ['new-media'], + example: '/zhihu/xhu/collection/26444956', + parameters: { id: '收藏夹 id, 可在收藏夹页面 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.zhihu.com/collection/:id'], + }, + name: 'xhu - 收藏夹', + maintainers: ['JimenezLi'], + handler, +}; + +async function handler(ctx) { const xhuCookie = await auth.getCookie(ctx); const id = ctx.req.param('id'); const link = `https://www.zhihu.com/collection/${id}`; @@ -27,7 +49,7 @@ export default async (ctx) => { }); const listRes = contentResponse.data.data; - ctx.set('data', { + return { title: `知乎收藏夹-${titleResponse.data.title}`, description: titleResponse.data.description, link, @@ -72,5 +94,5 @@ export default async (ctx) => { link, }; }), - }); -}; + }; +} diff --git a/lib/routes/zhihu/xhu/posts.ts b/lib/routes/zhihu/xhu/posts.ts index 31cd4cd3f..2a9c37fcc 100644 --- a/lib/routes/zhihu/xhu/posts.ts +++ b/lib/routes/zhihu/xhu/posts.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import auth from './auth'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/xhu/people/posts/:hexId', + categories: ['new-media'], + example: '/zhihu/xhu/people/posts/246e6cf44e94cefbf4b959cb5042bc91', + parameters: { hexId: '用户的 16 进制 id,获取方式同 [xhu - 用户动态](#zhi-hu-xhu-yong-hu-dong-tai)' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'xhu - 用户文章', + maintainers: ['JimenezLi'], + handler, +}; + +async function handler(ctx) { const xhuCookie = await auth.getCookie(ctx); const hexId = ctx.req.param('hexId'); const link = `https://www.zhihu.com/people/${hexId}/posts`; @@ -18,7 +37,7 @@ export default async (ctx) => { }); const data = response.data.data; - ctx.set('data', { + return { title: `${data[0].author.name} 的知乎文章`, link, image: data[0].author.avatar_url, @@ -30,5 +49,5 @@ export default async (ctx) => { link: `https://zhuanlan.zhihu.com/p/${item.id}`, author: item.author.name, })), - }); -}; + }; +} diff --git a/lib/routes/zhihu/xhu/question.ts b/lib/routes/zhihu/xhu/question.ts index 8ad42056a..277da6f06 100644 --- a/lib/routes/zhihu/xhu/question.ts +++ b/lib/routes/zhihu/xhu/question.ts @@ -1,9 +1,32 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import auth from './auth'; import utils from '../utils'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/xhu/question/:questionId/:sortBy?', + categories: ['new-media'], + example: '/zhihu/xhu/question/264051433', + parameters: { questionId: '问题 id', sortBy: '排序方式:`default`, `created`, `updated`。默认为 `default`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.zhihu.com/question/:questionId'], + target: '/xhu/question/:questionId', + }, + name: 'xhu - 问题', + maintainers: ['JimenezLi'], + handler, +}; + +async function handler(ctx) { const xhuCookie = await auth.getCookie(ctx); const { questionId, @@ -22,7 +45,7 @@ export default async (ctx) => { }); const listRes = response.data.data; - ctx.set('data', { + return { title: `知乎-${listRes[0].question.title}`, link, item: listRes.map((item) => { @@ -40,5 +63,5 @@ export default async (ctx) => { link, }; }), - }); -}; + }; +} diff --git a/lib/routes/zhihu/xhu/topic.ts b/lib/routes/zhihu/xhu/topic.ts index f2201d9de..34fff6b44 100644 --- a/lib/routes/zhihu/xhu/topic.ts +++ b/lib/routes/zhihu/xhu/topic.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import auth from './auth'; import utils from '../utils'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/xhu/topic/:topicId', + categories: ['new-media'], + example: '/zhihu/xhu/topic/19566035', + parameters: { topicId: '话题ID' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['www.zhihu.com/topic/:topicId/:type'], + }, + name: 'Unknown', + maintainers: ['JimenezLi'], + handler, +}; + +async function handler(ctx) { const xhuCookie = await auth.getCookie(ctx); const topicId = ctx.req.param('topicId'); const link = `https://www.zhihu.com/topic/${topicId}/newest`; @@ -19,7 +41,7 @@ export default async (ctx) => { }); const listRes = response.data.data; - ctx.set('data', { + return { title: `知乎话题-${topicId}`, link, item: listRes.map(({ target: item }) => { @@ -66,5 +88,5 @@ export default async (ctx) => { link, }; }), - }); -}; + }; +} diff --git a/lib/routes/zhihu/xhu/zhuanlan.ts b/lib/routes/zhihu/xhu/zhuanlan.ts index da3b1fa76..099e85f79 100644 --- a/lib/routes/zhihu/xhu/zhuanlan.ts +++ b/lib/routes/zhihu/xhu/zhuanlan.ts @@ -1,9 +1,32 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import auth from './auth'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/xhu/zhuanlan/:id', + categories: ['new-media'], + example: '/zhihu/xhu/zhuanlan/githubdaily', + parameters: { id: '专栏 id, 可在专栏主页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['zhuanlan.zhihu.com/:id'], + target: '/zhuanlan/:id', + }, + name: 'xhu- 专栏', + maintainers: ['JimenezLi'], + handler, +}; + +async function handler(ctx) { const xhuCookie = await auth.getCookie(ctx); const id = ctx.req.param('id'); const link = `https://www.zhihu.com/column/${id}`; @@ -28,7 +51,7 @@ export default async (ctx) => { const listRes = contentResponse.data.data; - ctx.set('data', { + return { title: `知乎专栏-${titleResponse.data.title}`, description: titleResponse.data.description, link, @@ -87,5 +110,5 @@ export default async (ctx) => { link, }; }), - }); -}; + }; +} diff --git a/lib/routes/zhihu/zhuanlan.ts b/lib/routes/zhihu/zhuanlan.ts index 3d7c00859..9652ff63c 100644 --- a/lib/routes/zhihu/zhuanlan.ts +++ b/lib/routes/zhihu/zhuanlan.ts @@ -1,9 +1,31 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import utils from './utils'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/zhuanlan/:id', + categories: ['new-media'], + example: '/zhihu/zhuanlan/googledevelopers', + parameters: { id: '专栏 id,可在专栏主页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: true, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['zhuanlan.zhihu.com/:id'], + }, + name: '专栏', + maintainers: ['DIYgod'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const listRes = await got({ @@ -91,10 +113,10 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { description, item, title: `知乎专栏-${title}`, link: url, - }); -}; + }; +} diff --git a/lib/routes/zhitongcaijing/index.ts b/lib/routes/zhitongcaijing/index.ts index d8509c57b..9eeb5926e 100644 --- a/lib/routes/zhitongcaijing/index.ts +++ b/lib/routes/zhitongcaijing/index.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -67,7 +68,25 @@ const ids = { }, }; -export default async (ctx) => { +export const route: Route = { + path: '/:id?/:category?', + categories: ['other'], + example: '/zhitongcaijing', + parameters: { id: '栏目 id,可在对应栏目页 URL 中找到,默认为 recommend,即推荐', category: '分类 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') ?? 'recommend'; const category = ctx.req.param('category') ?? ''; @@ -114,9 +133,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `智通财经 - ${ids[id].title}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/zhiy/letter.ts b/lib/routes/zhiy/letter.ts index 8b4ec43b7..627cb739b 100644 --- a/lib/routes/zhiy/letter.ts +++ b/lib/routes/zhiy/letter.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { baseUrl, fetchUserDate } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/letters/:author', + categories: ['traditional-media'], + example: '/zhiy/letters/messy', + parameters: { author: '作者 ID,可在URL中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['zhiy.cc/:author'], + }, + name: 'Newsletter', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const author = ctx.req.param('author'); const userData = await fetchUserDate(author); @@ -24,11 +46,11 @@ export default async (ctx) => { link: `${baseUrl}/letter/${item.id}`, })); - ctx.set('data', { + return { title: authorName, link: `${baseUrl}/${author}`, description: authorSignature, image: authorAvatarUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/zhiy/post.ts b/lib/routes/zhiy/post.ts index c158ff55b..a4721005e 100644 --- a/lib/routes/zhiy/post.ts +++ b/lib/routes/zhiy/post.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 { art } from '@/utils/render'; import * as path from 'node:path'; import dayjs from 'dayjs'; -export default async (ctx) => { +export const route: Route = { + path: '/posts/:author', + categories: ['traditional-media'], + example: '/zhiy/posts/long', + parameters: { author: '作者 ID,可在URL中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['zhiy.cc/:author'], + }, + name: '笔记', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { art.defaults.imports = { dayjs, ...art.defaults.imports, @@ -63,11 +85,11 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: authorName, link: `${baseUrl}/${author}`, description: authorSignature, image: authorAvatarUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/zhubai/index.ts b/lib/routes/zhubai/index.ts index 9771b197a..66eca5136 100644 --- a/lib/routes/zhubai/index.ts +++ b/lib/routes/zhubai/index.ts @@ -1,8 +1,27 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { isValidHost } from '@/utils/valid-host'; -export default async (ctx) => { +export const route: Route = { + path: '/:id', + categories: ['programming'], + example: '/zhubai/via', + parameters: { id: '`id` 为竹白主页 url 中的三级域名,如 via.zhubai.love 的 `id` 为 `via`' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '文章', + maintainers: ['naixy28'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 20; if (!isValidHost(id)) { @@ -19,7 +38,7 @@ export default async (ctx) => { const data = response.data.data; const { name, description } = data[0].publication; - ctx.set('data', { + return { title: name, link: `https://${id}.zhubai.love/`, description, @@ -29,5 +48,5 @@ export default async (ctx) => { link: `https://${id}.zhubai.love/posts/${item.id}`, author: name, })), - }); -}; + }; +} diff --git a/lib/routes/zhubai/top20.ts b/lib/routes/zhubai/top20.ts index 369d24a73..51bbf039b 100644 --- a/lib/routes/zhubai/top20.ts +++ b/lib/routes/zhubai/top20.ts @@ -1,3 +1,4 @@ +import { Route } from '@/types'; import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); @@ -15,7 +16,28 @@ const parseContent = (content) => art.defaults.imports.parseContent = parseContent; -export default async (ctx) => { +export const route: Route = { + path: '/top20', + categories: ['programming'], + example: '/zhubai/top20', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['analy.zhubai.love/'], + }, + name: 'Unknown', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 20; const rootUrl = 'http://analy.zhubai.wiki'; @@ -55,7 +77,7 @@ export default async (ctx) => { const icon = $('link[rel="apple-touch-icon"]').prop('href'); - ctx.set('data', { + return { item: items, title: `${$('meta[property="og:title"]').prop('content')} - TOP20`, link: rootUrl, @@ -66,5 +88,5 @@ export default async (ctx) => { logo: icon, subtitle: $('meta[property="og:description"]').prop('content'), author: $('meta[name="twitter:site"]').prop('content'), - }); -}; + }; +} diff --git a/lib/routes/zhujiceping/rss.ts b/lib/routes/zhujiceping/rss.ts index 7c7530499..4c61721ae 100644 --- a/lib/routes/zhujiceping/rss.ts +++ b/lib/routes/zhujiceping/rss.ts @@ -1,8 +1,20 @@ +import { Route } from '@/types'; import { parseDate } from '@/utils/parse-date'; import got from '@/utils/got'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['zhujiceping.com/'], + target: '', + }, + name: 'Unknown', + maintainers: ['cnkmmk'], + handler, +}; + +async function handler() { const url = 'https://www.zhujiceping.com/'; const response = await got({ method: 'get', url }); const $ = load(response.data); @@ -24,9 +36,9 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title: '国外主机测评', link: url, item: list, - }); -}; + }; +} diff --git a/lib/routes/zhuwang/index.ts b/lib/routes/zhuwang/index.ts index dbf79a10e..a7030fa6a 100644 --- a/lib/routes/zhuwang/index.ts +++ b/lib/routes/zhuwang/index.ts @@ -1,6 +1,28 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/zhujia', + categories: ['game'], + example: '/zhuwang/zhujia', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['zhujia.zhuwang.cc/'], + }, + name: 'Unknown', + maintainers: [], + handler, +}; + +async function handler() { const baseUrl = 'https://zhujia.zhuwang.cc/'; const now = new Date(); const date = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`; @@ -37,10 +59,10 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `全国今日生猪价格`, desription: '中国养猪网猪价频道是中国猪价权威平台,提供每日猪评,猪价和行情分析,并且预测猪价和分析每天的猪价排行。', link: baseUrl, item: priceItems, - }); -}; + }; +} diff --git a/lib/routes/zimuxia/index.ts b/lib/routes/zimuxia/index.ts index 6a1640d32..225c71606 100644 --- a/lib/routes/zimuxia/index.ts +++ b/lib/routes/zimuxia/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: '/:category?', + categories: ['picture'], + example: '/zimuxia', + parameters: { category: '分类,见下表,默认为 ALL' }, + 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://www.zimuxia.cn'; @@ -57,9 +76,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${category || 'ALL'} - FIX字幕侠`, link: response.url, item: items, - }); -}; + }; +} diff --git a/lib/routes/zimuxia/portfolio.ts b/lib/routes/zimuxia/portfolio.ts index f55167e0d..6cbe127cd 100644 --- a/lib/routes/zimuxia/portfolio.ts +++ b/lib/routes/zimuxia/portfolio.ts @@ -1,8 +1,30 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; const allowlinktypes = new Set(['all', 'magnet', 'ed2k', 'baidu', 'subhd', 'quark', '115']); -export default async (ctx) => { +export const route: Route = { + path: '/portfolio/:id', + categories: ['picture'], + example: '/zimuxia/portfolio/我们这一天', + parameters: { id: '剧集名,可在剧集页 URL 中找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['zimuxia.cn/portfolio/:id'], + }, + name: '剧集', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id'); let linktype = ctx.req.query('linktype') ?? 'magnet'; linktype = allowlinktypes.has(linktype) ? linktype : 'magnet'; @@ -44,9 +66,9 @@ export default async (ctx) => { }) .reverse(); - ctx.set('data', { + return { title: `${$('.content-page-title').text()} - FIX字幕侠`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/zjgtjy/index.ts b/lib/routes/zjgtjy/index.ts index 2cd45023a..54f6495ec 100644 --- a/lib/routes/zjgtjy/index.ts +++ b/lib/routes/zjgtjy/index.ts @@ -1,7 +1,15 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/:type?', + name: 'Unknown', + maintainers: ['Fatpandac'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') === 'all' ? '' : ctx.req.param('type').toUpperCase(); const host = `https://td.zjgtjy.cn:8553/devops/noticeInfo/queryNoticeInfoList?pageSize=10&pageNumber=1¬iceType=${type}&sort=DESC`; @@ -36,9 +44,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: '浙江土地使用权挂牌公告', link: host, item: items, - }); -}; + }; +} diff --git a/lib/routes/zjol/paper.ts b/lib/routes/zjol/paper.ts index cba5eecb1..f5659c675 100644 --- a/lib/routes/zjol/paper.ts +++ b/lib/routes/zjol/paper.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: '/paper/:id?', + categories: ['bbs'], + example: '/zjol/paper/zjrb', + parameters: { id: '报纸 id,见下表,默认为 `zjrb`,即浙江日报' }, + 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') ?? 'zjrb'; const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 100; @@ -86,9 +105,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: $('title').text(), link: rootUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/zju/career/index.ts b/lib/routes/zju/career/index.ts index 70a4cd0e5..02223ef3c 100644 --- a/lib/routes/zju/career/index.ts +++ b/lib/routes/zju/career/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'; @@ -12,7 +13,25 @@ const map = new Map([ [4, { title: '浙大就业服务平台 -- 告示通告', id: 'lmjdid=739BEBB72A252B25E0538713470A6C41&sjlmid=undefined&lmtype=2&lx=2' }], ]); -export default async (ctx) => { +export const route: Route = { + path: '/career/:type', + categories: ['forecast'], + example: '/zju/career/1', + parameters: { type: '分类,见下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '就业服务平台', + maintainers: ['Caicailiushui'], + handler, +}; + +async function handler(ctx) { const type = Number.parseInt(ctx.req.param('type')); const id = map.get(type).id; const res = await got(`${host}${id}`); @@ -35,9 +54,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/zju/cst/custom.ts b/lib/routes/zju/cst/custom.ts index e5c071724..93cac03fa 100644 --- a/lib/routes/zju/cst/custom.ts +++ b/lib/routes/zju/cst/custom.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 @@ async function getPage(id) { ); } -export default async (ctx) => { +export const route: Route = { + path: '/cst/custom/:id', + categories: ['forecast'], + example: '/zju/cst/custom/36194+36241+36246', + parameters: { id: '提取出通知页面中的 `ID`,如 `http://www.cst.zju.edu.cn/36246/list.htm` 中的 `36246`,可将你想获取通知的多个页面,通过 `+` 符号来聚合。' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '软件学院', + maintainers: ['zwithz'], + handler, +}; + +async function handler(ctx) { const id = ctx.req.param('id').split('+'); const tasks = id.map((id) => getPage(id)); const results = await Promise.all(tasks); @@ -39,9 +58,9 @@ export default async (ctx) => { items = [...items, ...result]; } - ctx.set('data', { + return { title: '浙江大学软件学院通知', link: 'http://www.cst.zju.edu.cn/', item: items, - }); -}; + }; +} diff --git a/lib/routes/zju/cst/index.ts b/lib/routes/zju/cst/index.ts index b9b622733..ba15eef2e 100644 --- a/lib/routes/zju/cst/index.ts +++ b/lib/routes/zju/cst/index.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,25 @@ async function getPage(id) { ); } -export default async (ctx) => { +export const route: Route = { + path: '/cst/:type', + categories: ['forecast'], + example: '/zju/cst/0', + parameters: { type: '分类,见下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Unknown', + maintainers: ['yonvenne', 'zwithz'], + handler, +}; + +async function handler(ctx) { const type = Number.parseInt(ctx.req.param('type')); const link = host + map.get(type).id; let items = []; @@ -77,9 +96,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: map.get(type).title, link, item: out, - }); -}; + }; +} diff --git a/lib/routes/zju/grs/index.ts b/lib/routes/zju/grs/index.ts index 78726cdfa..f718e16a4 100644 --- a/lib/routes/zju/grs/index.ts +++ b/lib/routes/zju/grs/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: '浙大研究生院 -- 海外交流', tag: 'hwjl' }], ]); -export default async (ctx) => { +export const route: Route = { + path: '/grs/:type', + categories: ['forecast'], + example: '/zju/grs/1', + parameters: { type: '分类,见下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '研究生院', + maintainers: ['Caicailiushui'], + handler, +}; + +async function handler(ctx) { const type = Number.parseInt(ctx.req.param('type')); const tag = map.get(type).tag; const url = `${host}${tag}/list.htm`; @@ -36,9 +55,9 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title: map.get(type).title, link: `${host}${tag}/list.htm`, item: items, - }); -}; + }; +} diff --git a/lib/routes/zju/list.ts b/lib/routes/zju/list.ts index 0643b1ccc..6818d3d67 100644 --- a/lib/routes/zju/list.ts +++ b/lib/routes/zju/list.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'; const host = 'https://www.zju.edu.cn/'; -export default async (ctx) => { +export const route: Route = { + path: '/list/:type', + categories: ['forecast'], + example: '/zju/list/xs', + parameters: { type: '`xs`为学术,`xw`为新闻,`5461`是图片新闻,`578`是浙大报道,具体参数参考左侧的菜单' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '普通栏目 如学术 / 图片 / 新闻等', + maintainers: ['Jeason0228'], + handler, +}; + +async function handler(ctx) { const type = ctx.req.param('type') ?? 'xs'; const link = host + type + `/list.htm`; const response = await got({ @@ -55,9 +74,9 @@ export default async (ctx) => { }); }) ); - ctx.set('data', { + return { title: `浙江大学` + $('ul.submenu .selected').text(), link, item: out, - }); -}; + }; +} diff --git a/lib/routes/zju/physics/index.ts b/lib/routes/zju/physics/index.ts index bf83b7ac6..2cc518e24 100644 --- a/lib/routes/zju/physics/index.ts +++ b/lib/routes/zju/physics/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 map = new Map([ [11, { title: '浙大物理学院 -- 学术报告', id: '3735' }],*/ ]); -export default async (ctx) => { +export const route: Route = { + path: '/physics/:type', + categories: ['forecast'], + example: '/zju/physics/1', + parameters: { type: '分类,见下表' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '物理学院', + maintainers: ['Caicailiushui'], + handler, +}; + +async function handler(ctx) { const type = Number.parseInt(ctx.req.param('type')); const id = map.get(type).id; const res = await got({ @@ -44,9 +63,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/zjuvag/blog.ts b/lib/routes/zjuvag/blog.ts index 03ca97ca4..a96dbde08 100644 --- a/lib/routes/zjuvag/blog.ts +++ b/lib/routes/zjuvag/blog.ts @@ -1,10 +1,29 @@ +import { Route } from '@/types'; // 导入必要的模组 import got from '@/utils/got'; // 自订的 got import { load } from 'cheerio'; // 可以使用类似 jQuery 的 API HTML 解析器 import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -export default async (ctx) => { +export const route: Route = { + path: '/blog', + categories: ['programming'], + example: '/zjuvag/blog', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '博客', + maintainers: ['KaiyoungYu'], + handler, +}; + +async function handler() { const baseUrl = 'https://zjuvag.org/blog/'; const { data: response } = await got(baseUrl); const $ = load(response); @@ -24,10 +43,10 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { // 在此处输出您的 RSS title: '浙江大学可视分析小组博客', link: 'https://zjuvag.org/blog/', item: items, - }); -}; + }; +} diff --git a/lib/routes/zodgame/forum.ts b/lib/routes/zodgame/forum.ts index fc6839ee3..4c843efe1 100644 --- a/lib/routes/zodgame/forum.ts +++ b/lib/routes/zodgame/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 * as path from 'node:path'; const rootUrl = 'https://zodgame.xyz'; -export default async (ctx) => { +export const route: Route = { + path: '/forum/:fid?', + categories: ['blog'], + example: '/zodgame/forum/13', + parameters: { fid: 'forum id, can be found in URL' }, + features: { + requireConfig: true, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'forum', + maintainers: ['FeCCC'], + handler, +}; + +async function handler(ctx) { const fid = ctx.req.param('fid'); const subUrl = `${rootUrl}/api/mobile/index.php?version=4&module=forumdisplay&fid=${fid}`; const cookie = config.zodgame.cookie; @@ -88,9 +107,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `${info.forum.name} - ZodGame论坛`, link: `${rootUrl}/forum.php?mod=forumdisplay&fid=${fid}`, item: items, - }); -}; + }; +} diff --git a/lib/routes/zotero/versions.ts b/lib/routes/zotero/versions.ts index 4fdf7e7a8..9ac565b4b 100644 --- a/lib/routes/zotero/versions.ts +++ b/lib/routes/zotero/versions.ts @@ -1,14 +1,36 @@ +import { Route } from '@/types'; import got from '@/utils/got'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/versions', + categories: ['university'], + example: '/zotero/versions', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['zotero.org/', 'zotero.org/support/changelog'], + }, + name: 'Version History', + maintainers: ['jasongzy'], + handler, +}; + +async function handler() { const url = 'https://www.zotero.org/support/changelog'; const response = await got(url); const data = response.data; const $ = load(data); const list = $('h2'); - ctx.set('data', { + return { title: 'Zotero - Version History', link: url, item: @@ -28,5 +50,5 @@ export default async (ctx) => { }; }) .get(), - }); -}; + }; +} diff --git a/lib/routes/zrblog/rss.ts b/lib/routes/zrblog/rss.ts index 3a0704c3b..a65b6177c 100644 --- a/lib/routes/zrblog/rss.ts +++ b/lib/routes/zrblog/rss.ts @@ -1,8 +1,20 @@ +import { Route } from '@/types'; import { parseDate } from '@/utils/parse-date'; import got from '@/utils/got'; import { load } from 'cheerio'; -export default async (ctx) => { +export const route: Route = { + path: '/', + radar: { + source: ['zrblog.net/'], + target: '', + }, + name: 'Unknown', + maintainers: ['cnkmmk'], + handler, +}; + +async function handler() { const url = 'https://www.zrblog.net/'; const response = await got({ method: 'get', url }); const $ = load(response.data); @@ -24,9 +36,9 @@ export default async (ctx) => { }) .get(); - ctx.set('data', { + return { title: '赵容部落', link: url, item: list, - }); -}; + }; +} diff --git a/lib/routes/zuel/notice.ts b/lib/routes/zuel/notice.ts index 2f7e77e1b..c3c5f3a60 100644 --- a/lib/routes/zuel/notice.ts +++ b/lib/routes/zuel/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 { parseDate } from '@/utils/parse-date'; -export default async (ctx) => { +export const route: Route = { + path: '/notice', + categories: ['forecast'], + example: '/zuel/notice', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + radar: { + source: ['wap.zuel.edu.cn/', 'wap.zuel.edu.cn/notice/list.htm'], + }, + name: '通知公告', + maintainers: ['nczitzk'], + handler, +}; + +async function handler() { const rootUrl = 'http://wap.zuel.edu.cn'; const currentUrl = `${rootUrl}/notice/list.htm`; @@ -45,9 +67,9 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: '中南财经大学 - 通知公告', link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/zuvio/boards.ts b/lib/routes/zuvio/boards.ts index 6748f4af3..463522525 100644 --- a/lib/routes/zuvio/boards.ts +++ b/lib/routes/zuvio/boards.ts @@ -1,15 +1,34 @@ +import { Route } from '@/types'; import cache from '@/utils/cache'; import { getBoards, rootUrl } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/student5/boards', + categories: ['blog'], + example: '/zuvio/student5/boards', + parameters: {}, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '看板列表', + maintainers: ['TonyRL'], + handler, +}; + +async function handler() { const items = await getBoards(cache.tryGet); - ctx.set('data', { + return { title: 'Zuvio 校園話題列表 - 大學生論壇', description: 'Zuvio 校園話題,Zuvio 校園話題千種動物頭像交流心得。最萌的匿名論壇,上千種逗趣動物頭像隨你變換,點頭像立即私訊功能,讓你找到共同話題的小夥伴!多人分享配對心得、聊天交友心情在此,快加入17分享!', image: 'https://s3.hicloud.net.tw/zuvio.public/public/system/images/irs_v4/chicken/shared/webshare.png', link: `${rootUrl}/articles`, item: items, language: 'zh-Hant', - }); -}; + }; +} diff --git a/lib/routes/zuvio/student5.ts b/lib/routes/zuvio/student5.ts index 60dc6184c..b5078f6c0 100644 --- a/lib/routes/zuvio/student5.ts +++ b/lib/routes/zuvio/student5.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 { token, apiUrl, rootUrl, renderDesc, getBoards } from './utils'; -export default async (ctx) => { +export const route: Route = { + path: '/student5/:board?', + categories: ['blog'], + example: '/zuvio/student5/34', + parameters: { board: '看板 ID,空为全站文章,可在看板 URL 或下方路由找到' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '校園話題', + maintainers: ['TonyRL'], + handler, +}; + +async function handler(ctx) { const { board = '' } = ctx.req.param(); const title = board ? (await getBoards(cache.tryGet)).find((i) => i.boardId === board).title : '全部'; @@ -48,12 +67,12 @@ export default async (ctx) => { ) ); - ctx.set('data', { + return { title: `Zuvio 校園${title}話題 - 大學生論壇`, description: 'Zuvio 校園話題千種動物頭像交流心得。最萌的匿名論壇,上千種逗趣動物頭像隨你變換,點頭像立即私訊功能,讓你找到共同話題的小夥伴!多人分享配對心得、聊天交友心情在此,快加入17分享!', image: 'https://s3.hicloud.net.tw/zuvio.public/public/system/images/irs_v4/chicken/shared/webshare.png', link: `${rootUrl}/articles`, item: items, language: 'zh-Hant', - }); -}; + }; +} diff --git a/lib/routes/zyshow/index.ts b/lib/routes/zyshow/index.ts index cb9ea80f6..c2082f766 100644 --- a/lib/routes/zyshow/index.ts +++ b/lib/routes/zyshow/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 = 'http://www.zyshow.net'; const currentUrl = `${rootUrl}${getSubPath(ctx).replace(/\/$/, '')}/`; @@ -42,9 +50,9 @@ export default async (ctx) => { }; }); - ctx.set('data', { + return { title: `综艺秀 - ${$('h2').text()}`, link: currentUrl, item: items, - }); -}; + }; +} diff --git a/lib/routes/zyw/hot.ts b/lib/routes/zyw/hot.ts index 0fc948b86..a26f5da7d 100644 --- a/lib/routes/zyw/hot.ts +++ b/lib/routes/zyw/hot.ts @@ -1,6 +1,25 @@ +import { Route } from '@/types'; import got from '@/utils/got'; -export default async (ctx) => { +export const route: Route = { + path: '/hot/:site?', + categories: ['traditional-media'], + example: '/zyw/hot', + parameters: { site: '站点,见下表,默认为空,即全部' }, + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: '今日热榜', + maintainers: ['nczitzk'], + handler, +}; + +async function handler(ctx) { const site = ctx.req.param('site') ?? ''; const rootUrl = 'https://hot.zyw.asia'; @@ -55,9 +74,9 @@ export default async (ctx) => { }) ); - ctx.set('data', { + return { title: `今日热榜${site ? ` - ${sites[0].label}` : ''}`, link: currentUrl, item: items, - }); -}; + }; +}