feat: add routes

This commit is contained in:
DIYgod 2024-03-09 03:06:53 +08:00
parent f8b141ee6e
commit 45f9ea21fb
No known key found for this signature in database
2092 changed files with 47948 additions and 8143 deletions

View File

@ -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));

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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(),
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
};
}),
});
};
};
}

View File

@ -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(),
});
};
};
}

View File

@ -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 <a href="https://docs.rsshub.app/install/#pei-zhi-bu-fen-rss-mo-kuai-pei-zhi">relevant config</a>');
}
@ -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,
};
}),
});
};
};
}

View File

@ -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,
};
}),
});
};
};
}

View File

@ -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,
};
}),
});
};
};
}

View File

@ -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),
});
};
};
}

View File

@ -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),
});
};
};
}

View File

@ -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 <a href="https://docs.rsshub.app/routes/game#wang-yi-da-shen">https://docs.rsshub.app/routes/game#wang-yi-da-shen</a>');
}
@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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'),
});
};
};
}

View File

@ -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'),
});
};
};
}

View File

@ -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));
};
}

View File

@ -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));
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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),
});
};
};
}

View File

@ -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}`,
})),
});
};
};
}

View File

@ -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),
});
};
};
}

View File

@ -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),
});
};
};
}

View File

@ -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}`,
})),
});
};
};
}

View File

@ -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}`,
})),
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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));
};
}

View File

@ -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));
};
}

View File

@ -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,
});
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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'),
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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'),
});
};
};
}

View File

@ -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));
})
),
});
};
};
}

View File

@ -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));
})
)),
});
};
};
}

View File

@ -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));
})
),
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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));
};
}

View File

@ -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));
};
}

View File

@ -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));
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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'),
})),
});
};
};
}

View File

@ -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'),
};
}),
});
};
};
}

View File

@ -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,
})),
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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'),
});
};
};
}

View File

@ -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'),
});
};
};
}

View File

@ -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'),
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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,
});
};
};
}

View File

@ -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) => {
},
})
);
};
}

View File

@ -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,
});
};
};
}

Some files were not shown because too many files have changed in this diff Show More