制作路由
创建命名空间
制作新的 RSS 路由的第一步是创建命名空间。命名空间原则上应该与您制作 RSS 源的主要网站的二级域名相同。例如,如果您正在为 https://github.com/DIYgod/RSSHub/issues 制作 RSS 源,第二级域名是 github
。因此,您应该在 lib/routes
下创建名为 github
的文件夹,作为您的 RSS 路由的命名空间。
在创建命名空间时,避免为同一命名空间的创建多个变体。例如,如果您为 yahoo.co.jp
和 yahoo.com
制作 RSS 源,则应该使用单个命名空间 yahoo
,而不是创建多个命名空间如 yahoo-jp
、yahoojp
、yahoo.jp
、jp.yahoo
、yahoocojp
等。
一旦您为 RSS 路由创建了命名空间,下一步就是创建文件 namespace.ts
来定义命名空间。
文件应该通过 namespace 返回一个符合 Namespace 类型的对象。Namespace 的定义在 /lib/types.ts
- name:供人类阅读的命名空间的名称,它会被用作文档的标题
- url:对应网站的不包含 protocol 的网址
- description:可选,对使用此命名空间用户的提示和额外说明,它会被插入到文档中
- zh, zh-TW, ja: 可选,英文以外的多语言支持,它会被用作生成多语言文档
一个完整的例子是:
import type { Namespace } from '@/types';
export const namespace: Namespace = {
name: 'GitHub',
url: 'github.com',
description: `
:::tip
GitHub provides some official RSS feeds:
- Repo releases: \`https://github.com/:owner/:repo/releases.atom\`
- Repo commits: \`https://github.com/:owner/:repo/commits.atom\`
- User activities: \`https://github.com/:user.atom\`
- Private feed: \`https://github.com/:user.private.atom?token=:secret\` (You can find **Subscribe to your news feed** in [dashboard](https://github.com) page after login)
- Wiki history: \`https://github.com/:owner/:repo/wiki.atom\`
:::`,
zh: {
name: '给他哈不',
},
};
创建路由
一旦您为路由创建了命名空间,下一步创建一个路由文件注册路由。
例如,如果您为 GitHub 仓库 Issues 制作 RSS 源,并且假设您希望用户输入 GitHub 用户名和仓库名,如果他们没有输入仓库名,则返回到 RSSHub
,您可以在 /lib/routes/github/issue.ts
中注册您的新 RSS 路由,文件需要通过 route 返回一个符合 Route 类型的对象。Route 的定义在 /lib/types.ts
- path: 路由路径,使用 Hono 路由 语法
- name: 供人类阅读的路由名称,它会被用作文档的标题
- url: 对应网站的不包含 protocol 的网址
- maintainers: 负责维护此路由的人员的 GitHub handle
- example: 路由的一个示例 URL
- parameters: 路由的参数说明
- description: 可选,对使用此路由用户的提示和额外说明,它会被插入到文档中
- categories: 路由的分类,它会被写入到对应分类的文档中
- features: 路由的一些特性,比如依赖哪些配置项,是否反爬严格,是否支持某种功能等
- radar: 可以帮助用户在使用 RSSHub Radar 或其他兼容其格式的软件时订阅您的新 RSS 路由,我们将在后面的部分更多介绍
- handler: 路由的处理函数,我们将在后面的部分更多介绍
一个完整例子是:
import { Route } from '@/types';
export const route: Route = {
path: '/issue/:user/:repo/:state?/:labels?',
categories: ['programming'],
example: '/github/issue/vuejs/core/all/wontfix',
parameters: { user: 'GitHub username', repo: 'GitHub repo name', state: 'the state of the issues. Can be either `open`, `closed`, or `all`. Default: `open`.', labels: 'a list of comma separated label names' },
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
radar: {
source: ['github.com/:user/:repo/issues', 'github.com/:user/:repo/issues/:id', 'github.com/:user/:repo'],
target: '/issue/:user/:repo',
},
name: 'Repo Issues',
maintainers: ['HenryQW', 'AndreyMZ'],
handler,
};
在上面的示例中,issue
是一个精确匹配,:user
是一个必需参数,:repo?
是一个可选参数。?
在 :repo
之后表示该参数是可选的
编写路由处理函数
处理函数会被传入一个参数 ctx,函数结束后需要返回一个包含 RSS 所需信息的对象
ctx 可以使用的 API 可以在 Hono context 文档中查看
返回值的类型在这里定义:/lib/types.ts#L37
如前所述,我们以 GitHub 仓库 Issues 为例制作 RSS 源。我们将展示前面提到的四种数据获取方法:
以下示例代码为旧版标准,区别为
- 处理函数之前会被整体返回,现在只作为 route 对象的一部分返回
- 处理函数之前会把 RSS 信息保存在
ctx.set('data')
中且没有返回值,现在需要把 RSS 信息作为处理函数的返回值
通过 API
查看 API 文档
不同的站点有不同的 API。您可以查看要为其制作 RSS 源的站点的 API 文档。在本例中,我们将使用 GitHub Issues API。
创建主文件
打开您的代码编辑器并创建一个新文件。由于我们要为 GitHub 仓库 Issues 制作 RSS 源,因此建议将文件命名为 issue.ts
。
以下是让您开始的基本代码:
- issue.ts
// 导入所需模组
import got from '@/utils/got'; // 自订的 got
import { parseDate } from '@/utils/parse-date';
export default async (ctx) => {
// 在此处编写您的逻辑
ctx.set('data', {
// 在此处输出您的 RSS
});
};
获取用户输入
如前所述,我们 需要从用户输入中获取 GitHub 用户名和仓库名称。如果请求 URL 中未提供仓库名称,则应默认为 RSSHub
。您可以使用以下代码实现:
- 解构赋值
- 传统赋值
export default async (ctx) => {
const { user, repo = 'RSSHub' } = ctx.req.param();
ctx.set('data', {
// 在此处输出您的 RSS
});
};
export default async (ctx) => {
const user = ctx.req.param('user');
const repo = ctx.req.param('repo') ?? 'RSSHub';
ctx.set('data', {
// 在此处输出您的 RSS
});
};
这两个代码片段都执行相同的操 作。第一个使用对象解构将 user
和 repo
变量赋值,而第二个使用传统赋值和空值合并运算符在请求 URL 中未提供它的情况下将 repo
变量分配默认值 RSSHub
。
从 API 获取数据
在获取用户输入后,我们可以使用它向 API 发送请求。大多数情况下,您需要使用 @/utils/got
中的 got
(一个自订的 got 包装函数)发送 HTTP 请求。有关更多信息,请参阅 got 文档。
- 解构赋值
- 传统赋值
export default async (ctx) => {
const { user, repo = 'RSSHub' } = ctx.req.param();
// 发送 HTTP GET 请求到 API 并解构返回的数据对象
const { data } = await got(`https://api.github.com/repos/${user}/${repo}/issues`, {
headers: {
// 为简单起见,此示例使用 HTML 而不是推荐的 'application/vnd.github+json',
// 因后者返回 Markdown 并需要进一步处理
accept: 'application/vnd.github.html+json',
},
searchParams: {
// 这允许用户设置条数限制
per_page: ctx.req.query('limit') ? parseInt(ctx.req.query('limit'), 10) : 30,
},
});
ctx.set('data', {
// 在此处输出您的 RSS
});
};
export default async (ctx) => {
const user = ctx.req.param('user');
const repo = ctx.req.param('repo') ?? 'RSSHub';
// 发送 HTTP GET 请求到 API
const response = await got(`https://api.github.com/repos/${user}/${repo}/issues`, {
headers: {
accept: 'application/vnd.github.html+json',
},
searchParams: {
per_page: ctx.req.query('limit') ? parseInt(ctx.req.query('limit'), 10) : 30,
},
});
// response.data 是上述请求返回的数据对象
const data = response.data;
ctx.set('data', {
// 在此处输出您的 RSS
});
};
生成 RSS 源
一旦我们从 API 获取到数据,我们需要进一步处理它以生成符合 RSS 规范的 RSS 源。具体来说,我们需要提取源标题、源链接、文章标题、文章链接、文章正文和文章发布日期。
为此,我们可以将相关数据传给 ctx.set('data', obj)
,RSSHub 的中间件将处理其余部分。
以下是应有的最终代码:
- 最终代码
- 替代代码
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
export default async (ctx) => {
const { user, repo = 'RSSHub' } = ctx.req.param();
const { data } = await got(`https://api.github.com/repos/${user}/${repo}/issues`, {
headers: {
accept: 'application/vnd.github.html+json',
},
searchParams: {
per_page: ctx.req.query('limit') ? parseInt(ctx.req.query('limit'), 10) : 30,
},
});
// 从 API 响应中提取相关数据
const items = data.map((item) => ({
// 文章标题
title: item.title,
// 文章链接
link: item.html_url,
// 文章正文
description: item.body_html,
// 文章发布日期
pubDate: parseDate(item.created_at),
// 如果有的话,文章作者
author: item.user.login,
// 如果有的话,文章分类
category: item.labels.map((label) => label.name),
}));
ctx.set('data', {
// 源标题
title: `${user}/${repo} issues`,
// 源链接
link: `https://github.com/${user}/${repo}/issues`,
// 源文章
item: items,
});
};
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
export default async (ctx) => {
const { user, repo = 'RSSHub' } = ctx.req.param();
const { data } = await got(`https://api.github.com/repos/${user}/${repo}/issues`, {
headers: {
accept: 'application/vnd.github.html+json',
},
searchParams: {
per_page: ctx.req.query('limit') ? parseInt(ctx.req.query('limit'), 10) : 30,
},
});
ctx.set('data', {
// 源标题
title: `${user}/${repo} issues`,
// 源链接
link: `https://github.com/${user}/${repo}/issues`,
// 遍历所有此前获取的数据
item: data.map((item) => ({
// 文章标题
title: item.title,
// 文章链接
link: item.html_url,
// 文章正文
description: item.body_html,
// 文章发布日期
pubDate: parseDate(item.created_at),
// 如果有的话,文章作者
author: item.user.login,
// 如果有的话,文章分类
category: item.labels.map((label) => label.name),
}));
});
};
通过 got 从 HTML 获取数据
创建主文件
打开您的代码编辑器并创建一个新文件。由于我们要为 GitHub 仓库 Issues 制作 RSS 源,因此建议将文件命名为 issue.ts
。
以下是让您开始的基本代码:
// 导入必要的模组
import got from '@/utils/got'; // 自订的 got
import { load } from 'cheerio'; // 可以使用类似 jQuery 的 API HTML 解析器
import { parseDate } from '@/utils/parse-date';
export default async (ctx) => {
// 在此处编写您的逻辑
ctx.set('data', {
// 在此处输出您的 RSS
});
};