Merge remote-tracking branch 'origin/master' into feature/ofetch
This commit is contained in:
commit
9f7de5ab9a
|
|
@ -2,4 +2,7 @@ coverage
|
|||
.vscode
|
||||
docker-compose.yml
|
||||
!/.github
|
||||
!/docs/.vuepress
|
||||
lib/routes-deprecated
|
||||
lib/router.js
|
||||
babel.config.js
|
||||
scripts/docker/minify-docker.js
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@ updates:
|
|||
ignore:
|
||||
- dependency-name: jsrsasign
|
||||
versions: ['>=11.0.0'] # no longer includes KJUR.crypto.Cipher for RSA
|
||||
- dependency-name: unified
|
||||
versions: ['>=10.0.0']
|
||||
|
||||
- package-ecosystem: 'github-actions'
|
||||
directory: '/'
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ jobs:
|
|||
build:
|
||||
runs-on: ubuntu-latest
|
||||
name: Build assets
|
||||
timeout-minutes: 5
|
||||
timeout-minutes: 60
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
|
|
@ -33,13 +33,9 @@ jobs:
|
|||
- name: Install dependencies (yarn)
|
||||
run: pnpm i
|
||||
- name: Build assets
|
||||
run: npm run build
|
||||
- name: Commit files
|
||||
run: |
|
||||
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git status
|
||||
git diff-index --quiet HEAD || (git commit -m "chore: auto build" -a --no-verify && git push "https://${GITHUB_ACTOR}:${{ secrets.GITHUB_TOKEN }}@github.com/${GITHUB_REPOSITORY}.git" HEAD:master)
|
||||
run: pnpm build
|
||||
- name: Build docs
|
||||
run: pnpm build:docs
|
||||
- name: Deploy
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
|
|
@ -57,6 +53,8 @@ jobs:
|
|||
run: |
|
||||
cp -r ./assets/build/docs/en/* ./rsshub-docs/src/routes
|
||||
cp -r ./assets/build/docs/zh/* ./rsshub-docs/src/zh/routes
|
||||
cp ./lib/types.ts ./rsshub-docs/.vitepress/theme/types.ts
|
||||
cp ./scripts/workflow/data.ts ./rsshub-docs/.vitepress/config/data.ts
|
||||
- name: Commit docs
|
||||
run: |
|
||||
cd rsshub-docs
|
||||
|
|
|
|||
|
|
@ -15,16 +15,16 @@ jobs:
|
|||
- uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 8
|
||||
- uses: actions/setup-node@v4 # just need its cache
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies (pnpm) # needed since we need to parse markdown, so we also use got instead
|
||||
- name: Install dependencies (pnpm) # import remark-parse and unified
|
||||
run: pnpm i
|
||||
- name: Generate feedback
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const script = require(`${process.env.GITHUB_WORKSPACE}/scripts/workflow/test-issue/call-maintainer.js`)
|
||||
return script({ github, context, core })
|
||||
const { default: callMaintainer } = await import('${{ github.workspace }}/scripts/workflow/test-issue/call-maintainer.mjs')
|
||||
await callMaintainer({ github, context, core })
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@ name: PR - route test
|
|||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [ PR - Docker build test ] # open, reopen, synchronized, edited included
|
||||
types: [ completed ]
|
||||
workflows: [PR - Docker build test] # open, reopen, synchronized, edited included
|
||||
types: [completed]
|
||||
|
||||
jobs:
|
||||
testRoute:
|
||||
name: Route test
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }} # skip if unsuccessful
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }} # skip if unsuccessful
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
|
|
@ -44,8 +44,8 @@ jobs:
|
|||
const body = PR.body
|
||||
const number = PR.number
|
||||
const sender = PR.user.login
|
||||
const script = require(`${process.env.GITHUB_WORKSPACE}/scripts/workflow/test-route/identify.js`)
|
||||
return script({ github, context, core }, body, number, sender)
|
||||
const { default: identify } = await import('${{ github.workspace }}/scripts/workflow/test-route/identify.mjs')
|
||||
return identify({ github, context, core }, body, number, sender)
|
||||
|
||||
- name: Fetch Docker image
|
||||
if: (env.TEST_CONTINUE)
|
||||
|
|
@ -72,13 +72,13 @@ jobs:
|
|||
with:
|
||||
version: 8
|
||||
|
||||
- uses: actions/setup-node@v4 # just need its cache
|
||||
- uses: actions/setup-node@v4
|
||||
if: (env.TEST_CONTINUE)
|
||||
with:
|
||||
node-version: lts/*
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies (pnpm) # `got` needed since `github.request` disallows HTTP requests
|
||||
- name: Install dependencies (pnpm) # require js-beautify
|
||||
if: (env.TEST_CONTINUE)
|
||||
run: pnpm i
|
||||
|
||||
|
|
@ -99,9 +99,8 @@ jobs:
|
|||
const routes = JSON.parse(process.env.TEST_ROUTES)
|
||||
const number = PR.number
|
||||
core.info(`${link}, ${routes}, ${number}`)
|
||||
const got = require("got")
|
||||
const script = require(`${process.env.GITHUB_WORKSPACE}/scripts/workflow/test-route/test.js`)
|
||||
return script({ github, context, core, got }, link, routes, number)
|
||||
const { default: test } = await import('${{ github.workspace }}/scripts/workflow/test-route/test.mjs')
|
||||
await test({ github, context, core }, link, routes, number)
|
||||
|
||||
- name: Pull Request Labeler
|
||||
if: ${{ failure() }}
|
||||
|
|
@ -114,4 +113,4 @@ jobs:
|
|||
|
||||
- name: Print Docker container logs
|
||||
if: (env.TEST_CONTINUE)
|
||||
run: docker logs rsshub # logs/combined.log? Not so readable...
|
||||
run: docker logs rsshub # logs/combined.log? Not so readable...
|
||||
|
|
|
|||
|
|
@ -77,8 +77,8 @@ jobs:
|
|||
const body = event.comment.body
|
||||
const number = event.issue.number
|
||||
const sender = event.comment.user.login
|
||||
const script = require(`${process.env.GITHUB_WORKSPACE}/scripts/workflow/test-route/identify.js`)
|
||||
return script({ github, context, core }, body, number, sender)
|
||||
const { default: identify } = await import('${{ github.workspace }}/scripts/workflow/test-route/identify.mjs')
|
||||
return identify({ github, context, core }, body, number, sender)
|
||||
|
||||
- name: Build RSSHub
|
||||
if: env.TEST_CONTINUE
|
||||
|
|
@ -107,9 +107,8 @@ jobs:
|
|||
const routes = JSON.parse(process.env.TEST_ROUTES)
|
||||
const number = event.issue.number
|
||||
core.info(`${link}, ${routes}, ${number}`)
|
||||
const got = require("got")
|
||||
const script = require(`${process.env.GITHUB_WORKSPACE}/scripts/workflow/test-route/test.js`)
|
||||
return script({ github, context, core, got }, link, routes, number)
|
||||
const { default: test } = await import('${{ github.workspace }}/scripts/workflow/test-route/test.mjs')
|
||||
await test({ github, context, core }, link, routes, number)
|
||||
|
||||
- name: Print logs
|
||||
if: (env.TEST_CONTINUE)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
name: Build assets
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
name: Build assets
|
||||
timeout-minutes: 60
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 8
|
||||
- name: Use Node.js Active LTS
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies (yarn)
|
||||
run: pnpm i
|
||||
- name: Build assets
|
||||
run: pnpm build
|
||||
- name: Build full routes test result
|
||||
continue-on-error: true
|
||||
run: pnpm vitest:fullroutes
|
||||
- name: Deploy
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./assets
|
||||
user_name: 'github-actions[bot]'
|
||||
user_email: '41898282+github-actions[bot]@users.noreply.github.com'
|
||||
keep_files: true
|
||||
|
|
@ -5,7 +5,6 @@ on:
|
|||
branches-ignore:
|
||||
- 'dependabot/**'
|
||||
paths:
|
||||
- 'test/**'
|
||||
- 'lib/**'
|
||||
- 'package.json'
|
||||
- 'pnpm-lock.yaml'
|
||||
|
|
@ -43,6 +42,8 @@ jobs:
|
|||
run: pnpm i
|
||||
- name: Run postinstall script for dependencies
|
||||
run: pnpm rb
|
||||
- name: Build routes
|
||||
run: pnpm build
|
||||
- name: Test all and generate coverage
|
||||
run: pnpm run vitest:coverage
|
||||
env:
|
||||
|
|
@ -84,6 +85,8 @@ jobs:
|
|||
run: pnpm i
|
||||
- name: Run postinstall script for dependencies
|
||||
run: pnpm rb
|
||||
- name: Build routes
|
||||
run: pnpm build
|
||||
- name: Install Chromium
|
||||
if: ${{ matrix.chromium.dependency != '' }}
|
||||
# 'chromium-browser' from Ubuntu APT repo is a dummy package. Its version (85.0.4183.83) means
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
lib/routes-deprecated
|
||||
lib/router.js
|
||||
babel.config.js
|
||||
scripts/docker/minify-docker.js
|
||||
|
|
@ -84,7 +84,7 @@ FROM node:21-bookworm-slim AS chromium-downloader
|
|||
# Yeah, downloading Chromium never needs those dependencies below.
|
||||
|
||||
WORKDIR /app
|
||||
COPY ./.puppeteerrc.js /app/
|
||||
COPY ./.puppeteerrc.cjs /app/
|
||||
COPY --from=dep-version-parser /ver/.puppeteer_version /app/.puppeteer_version
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
|
|
|
|||
|
|
@ -1,23 +1,16 @@
|
|||
import { describe, expect, it, vi, afterEach, afterAll, beforeAll } from 'vitest';
|
||||
import { describe, expect, it, vi, afterEach } from 'vitest';
|
||||
import Parser from 'rss-parser';
|
||||
import wait from '@/utils/wait';
|
||||
|
||||
process.env.CACHE_EXPIRE = '1';
|
||||
process.env.CACHE_CONTENT_EXPIRE = '3';
|
||||
|
||||
const parser = new Parser();
|
||||
|
||||
beforeAll(() => {
|
||||
process.env.CACHE_EXPIRE = '1';
|
||||
process.env.CACHE_CONTENT_EXPIRE = '3';
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
delete process.env.CACHE_TYPE;
|
||||
vi.resetModules();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
delete process.env.CACHE_EXPIRE;
|
||||
});
|
||||
|
||||
describe('cache', () => {
|
||||
it('memory', async () => {
|
||||
process.env.CACHE_TYPE = 'memory';
|
||||
|
|
|
|||
|
|
@ -1,55 +1,8 @@
|
|||
import { describe, expect, it, afterAll } from 'vitest';
|
||||
process.env.SOCKET = 'socket';
|
||||
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import app from '@/app';
|
||||
import Parser from 'rss-parser';
|
||||
const parser = new Parser();
|
||||
import { config } from '@/config';
|
||||
|
||||
afterAll(() => {
|
||||
delete process.env.SOCKET;
|
||||
});
|
||||
|
||||
async function checkRSS(response) {
|
||||
const checkDate = (date) => {
|
||||
expect(date).toEqual(expect.any(String));
|
||||
expect(Date.parse(date)).toEqual(expect.any(Number));
|
||||
expect(Date.now() - +new Date(date)).toBeGreaterThan(-1000 * 60 * 60 * 24 * 5);
|
||||
expect(Date.now() - +new Date(date)).toBeLessThan(1000 * 60 * 60 * 24 * 30 * 12 * 10);
|
||||
};
|
||||
|
||||
const parsed = await parser.parseString(await response.text());
|
||||
|
||||
expect(parsed).toEqual(expect.any(Object));
|
||||
expect(parsed.title).toEqual(expect.any(String));
|
||||
expect(parsed.title).not.toBe('RSSHub');
|
||||
expect(parsed.description).toEqual(expect.any(String));
|
||||
expect(parsed.link).toEqual(expect.any(String));
|
||||
expect(parsed.lastBuildDate).toEqual(expect.any(String));
|
||||
expect(parsed.ttl).toEqual(Math.trunc(config.cache.routeExpire / 60) + '');
|
||||
expect(parsed.items).toEqual(expect.any(Array));
|
||||
checkDate(parsed.lastBuildDate);
|
||||
|
||||
// check items
|
||||
const guids: (string | undefined)[] = [];
|
||||
for (const item of parsed.items) {
|
||||
expect(item).toEqual(expect.any(Object));
|
||||
expect(item.title).toEqual(expect.any(String));
|
||||
expect(item.link).toEqual(expect.any(String));
|
||||
expect(item.content).toEqual(expect.any(String));
|
||||
expect(item.guid).toEqual(expect.any(String));
|
||||
if (item.pubDate) {
|
||||
expect(item.pubDate).toEqual(expect.any(String));
|
||||
checkDate(item.pubDate);
|
||||
}
|
||||
|
||||
// guid must be unique
|
||||
expect(guids).not.toContain(item.guid);
|
||||
guids.push(item.guid);
|
||||
}
|
||||
}
|
||||
|
||||
describe('router', () => {
|
||||
describe('registry', () => {
|
||||
// root
|
||||
it(`/`, async () => {
|
||||
const response = await app.request('/');
|
||||
|
|
@ -58,14 +11,6 @@ describe('router', () => {
|
|||
expect(response.headers.get('cache-control')).toBe('no-cache');
|
||||
});
|
||||
|
||||
// route
|
||||
it(`/test/1`, async () => {
|
||||
const response = await app.request('/test/1');
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
await checkRSS(response);
|
||||
});
|
||||
|
||||
// robots.txt
|
||||
it('/robots.txt', async () => {
|
||||
config.disallowRobot = false;
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@ 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/test/namespace';
|
||||
import { route as testRoute } from '@/routes/test/index';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
|
|
@ -27,18 +25,9 @@ let namespaces: Record<
|
|||
|
||||
switch (process.env.NODE_ENV) {
|
||||
case 'test':
|
||||
modules = {
|
||||
'/test/namespace.ts': {
|
||||
namespace: testNamespace,
|
||||
},
|
||||
'/test/index.ts': {
|
||||
route: testRoute,
|
||||
},
|
||||
};
|
||||
break;
|
||||
case 'production':
|
||||
// eslint-disable-next-line n/no-unpublished-require
|
||||
namespaces = require('../assets/build/routes.json');
|
||||
// @ts-expect-error
|
||||
namespaces = await import('../assets/build/routes.json');
|
||||
break;
|
||||
default:
|
||||
modules = directoryImport({
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
import app from '@/app';
|
||||
import Parser from 'rss-parser';
|
||||
const parser = new Parser();
|
||||
import { config } from '@/config';
|
||||
|
||||
process.env.ALLOW_USER_SUPPLY_UNSAFE_DOMAIN = 'true';
|
||||
|
||||
const routes = {
|
||||
'/test/:id': '/test/1',
|
||||
};
|
||||
if (process.env.FULL_ROUTES_TEST) {
|
||||
const { namespaces } = await import('@/registry');
|
||||
for (const namespace in namespaces) {
|
||||
for (const route in namespaces[namespace].routes) {
|
||||
const requireConfig = namespaces[namespace].routes[route].features?.requireConfig;
|
||||
let configs;
|
||||
if (typeof requireConfig !== 'boolean') {
|
||||
configs = requireConfig
|
||||
?.filter((config) => !config.optional)
|
||||
.map((config) => config.name)
|
||||
.filter((name) => name !== 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN');
|
||||
}
|
||||
if (namespaces[namespace].routes[route].example && !configs?.length) {
|
||||
routes[`/${namespace}${route}`] = namespaces[namespace].routes[route].example;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function checkRSS(response) {
|
||||
const checkDate = (date) => {
|
||||
expect(date).toEqual(expect.any(String));
|
||||
expect(Date.parse(date)).toEqual(expect.any(Number));
|
||||
expect(Date.now() - +new Date(date)).toBeGreaterThan(-1000 * 60 * 60 * 24 * 5);
|
||||
expect(Date.now() - +new Date(date)).toBeLessThan(1000 * 60 * 60 * 24 * 30 * 12 * 10);
|
||||
};
|
||||
|
||||
const parsed = await parser.parseString(await response.text());
|
||||
|
||||
expect(parsed).toEqual(expect.any(Object));
|
||||
expect(parsed.title).toEqual(expect.any(String));
|
||||
expect(parsed.title).not.toBe('RSSHub');
|
||||
expect(parsed.description).toEqual(expect.any(String));
|
||||
expect(parsed.link).toEqual(expect.any(String));
|
||||
expect(parsed.lastBuildDate).toEqual(expect.any(String));
|
||||
expect(parsed.ttl).toEqual(Math.trunc(config.cache.routeExpire / 60) + '');
|
||||
expect(parsed.items).toEqual(expect.any(Array));
|
||||
checkDate(parsed.lastBuildDate);
|
||||
|
||||
// check items
|
||||
const guids: (string | undefined)[] = [];
|
||||
for (const item of parsed.items) {
|
||||
expect(item).toEqual(expect.any(Object));
|
||||
expect(item.title).toEqual(expect.any(String));
|
||||
expect(item.link).toEqual(expect.any(String));
|
||||
expect(item.content).toEqual(expect.any(String));
|
||||
expect(item.guid).toEqual(expect.any(String));
|
||||
if (item.pubDate) {
|
||||
expect(item.pubDate).toEqual(expect.any(String));
|
||||
checkDate(item.pubDate);
|
||||
}
|
||||
|
||||
// guid must be unique
|
||||
expect(guids).not.toContain(item.guid);
|
||||
guids.push(item.guid);
|
||||
}
|
||||
}
|
||||
|
||||
describe('routes', () => {
|
||||
for (const route in routes) {
|
||||
it.concurrent(route, async () => {
|
||||
const response = await app.request(routes[route]);
|
||||
expect(response.status).toBe(200);
|
||||
await checkRSS(response);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -31,9 +31,9 @@ export const route: Route = {
|
|||
| ps4 | sgame | 3ds | psv | jiaocheng | ps3yx | zhuji.md | zhangji.psp | pcgame | zhangji | zhuji | ps4.psjc | ps41.ps4pkg | nsaita.cundang | nsaita.pojie | nsaita.buding | nsaita.zhutie | nsaita.zhuti |`,
|
||||
};
|
||||
|
||||
async function handler(ctx?: Context): Promise<Data> {
|
||||
const category = (ctx!.req.param('category') ?? 'sgame').replaceAll('.', '/');
|
||||
const tab = ctx?.req.param('tab') ?? 'all';
|
||||
async function handler(ctx: Context): Promise<Data> {
|
||||
const category = (ctx.req.param('category') ?? 'sgame').replaceAll('.', '/');
|
||||
const tab = ctx.req.param('tab') ?? 'all';
|
||||
|
||||
const currentUrl = `https://www.2023game.com/${category}/`;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import got from '@/utils/got';
|
||||
import type { Route } from '@/types';
|
||||
|
||||
export const route: Route = {
|
||||
path: '/dynamic/:uid?',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
import type { Namespace } from '@/types';
|
||||
|
||||
export const namespace: Namespace = {
|
||||
name: 'AppsTorrent',
|
||||
url: 'appstorrent.ru',
|
||||
};
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
import { Data, DataItem, Route } from '@/types';
|
||||
import cache from '@/utils/cache';
|
||||
import got, { Options } from '@/utils/got';
|
||||
import { getCurrentPath } from '@/utils/helpers';
|
||||
import { parseDate } from '@/utils/parse-date';
|
||||
import { art } from '@/utils/render';
|
||||
import { load } from 'cheerio';
|
||||
import dayjs from 'dayjs';
|
||||
import { Context } from 'hono';
|
||||
import * as path from 'node:path';
|
||||
const __dirname = getCurrentPath(import.meta.url);
|
||||
|
||||
export const route: Route = {
|
||||
path: '/programs',
|
||||
categories: ['program-update'],
|
||||
example: '/appstorrent/programs',
|
||||
name: 'Programs',
|
||||
maintainers: ['xzzpig'],
|
||||
handler,
|
||||
url: 'appstorrent.ru/programs/',
|
||||
};
|
||||
|
||||
async function handler(ctx?: Context): Promise<Data> {
|
||||
const limit = ctx?.req.query('limit') ? Number.parseInt(ctx?.req.query('limit') ?? '20') : 20;
|
||||
const baseUrl = 'https://appstorrent.ru';
|
||||
const currentUrl = `${baseUrl}/programs/`;
|
||||
const gotOptions: Options = {
|
||||
http2: true,
|
||||
};
|
||||
|
||||
const response = await got(currentUrl, gotOptions);
|
||||
const $ = load(response.data as any);
|
||||
|
||||
const selector = 'article.soft-item:not(.locked)';
|
||||
const list = $(selector)
|
||||
.slice(0, limit)
|
||||
.toArray()
|
||||
.map((item) => {
|
||||
const $item = $(item);
|
||||
return {
|
||||
title: $item.find('.subtitle').text().trim(),
|
||||
link: $item.find('.subtitle a').attr('href')!,
|
||||
category: [$item.find('.info .category').text().trim()],
|
||||
version: $item.find('.version').text(),
|
||||
architecture: $item.find('.architecture').text().trim(),
|
||||
size: $item.find('.size').text().trim(),
|
||||
};
|
||||
});
|
||||
|
||||
const items: DataItem[] = await Promise.all(
|
||||
list.map(
|
||||
(item) =>
|
||||
cache.tryGet(item.link, async () => {
|
||||
const response = await got(item.link, gotOptions);
|
||||
const $ = load(response.data as any);
|
||||
|
||||
const pubDate = parseDate($('.tech-info .date-news a').attr('href')?.replace('https://appstorrent.ru/', '') ?? '');
|
||||
|
||||
return {
|
||||
title: item.title,
|
||||
link: item.link,
|
||||
category: item.category,
|
||||
pubDate,
|
||||
description: art(path.join(__dirname, 'templates/description.art'), {
|
||||
cover: baseUrl + $('.main-title img').attr('src')?.trim(),
|
||||
title: item.title,
|
||||
pubDate: dayjs(pubDate).format('YYYY-MM-DD'),
|
||||
version: item.version,
|
||||
architecture: item.architecture,
|
||||
compatibility: $('div.right > div.info > div.right-container > div:nth-child(5) > div > span:nth-child(2) > a').text(),
|
||||
size: item.size,
|
||||
activation: $('div.right > div.info > div.right-container > div:nth-child(4) > div > span:nth-child(2) > a').text(),
|
||||
description: $('.content .body-content').first().text(),
|
||||
changelog: $('.content .body-content').last().text(),
|
||||
screenshots: $('.screenshots img')
|
||||
.toArray()
|
||||
.map((img) => $(img).attr('src'))
|
||||
.map((src) => baseUrl + src),
|
||||
}),
|
||||
} as DataItem;
|
||||
}) as Promise<DataItem>
|
||||
)
|
||||
);
|
||||
|
||||
return {
|
||||
title: $('title').text(),
|
||||
link: currentUrl.toString(),
|
||||
allowEmpty: true,
|
||||
item: items,
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<p>
|
||||
<img src="{{ cover }}">
|
||||
<h1>{{ title }}</h1><br>
|
||||
<b>Public Date</b>: {{pubDate}}<br>
|
||||
<b>Version</b>: {{version}}<br>
|
||||
<b>Architecture</b>: {{architecture}}<br>
|
||||
<b>Compactibility</b>: {{compatibility}}<br>
|
||||
<b>Size</b>: {{size}}<br>
|
||||
<b>Activation</b>: {{activation}}<br>
|
||||
</p>
|
||||
<b>Description</b>:
|
||||
<p>
|
||||
{{ description }}
|
||||
</p>
|
||||
<b>Change Log</b>:
|
||||
<p>
|
||||
{{ changelog }}
|
||||
</p>
|
||||
<b>Screenshots</b>
|
||||
{{each screenshots}}
|
||||
<img src="{{ $value }}">
|
||||
{{/each}}
|
||||
|
|
@ -121,7 +121,6 @@ async function handler(ctx) {
|
|||
headers: {
|
||||
Referer: `https://space.bilibili.com/${uid}/`,
|
||||
},
|
||||
transformResponse: [(data) => data],
|
||||
});
|
||||
const cards = JSONbig.parse(response.body).data.cards;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import got from '@/utils/got';
|
|||
import cache from './cache';
|
||||
import utils from './utils';
|
||||
import { parseDate } from '@/utils/parse-date';
|
||||
import { queryToBoolean } from '@/utils/readable-social';
|
||||
|
||||
const notFoundData = {
|
||||
title: '此 bilibili 频道不存在',
|
||||
|
|
@ -15,7 +16,7 @@ export const route: Route = {
|
|||
parameters: {
|
||||
uid: '用户 id, 可在 UP 主主页中找到',
|
||||
sid: '合集 id, 可在合集页面的 URL 中找到',
|
||||
disableEmbed: '默认为开启内嵌视频, 任意值为关闭',
|
||||
disableEmbed: '空,0与false为开启内嵌视频, 其他任意值为关闭',
|
||||
sortReverse: '默认:默认排序 1:升序排序',
|
||||
page: '页码, 默认1',
|
||||
},
|
||||
|
|
@ -35,7 +36,7 @@ export const route: Route = {
|
|||
async function handler(ctx) {
|
||||
const uid = Number.parseInt(ctx.req.param('uid'));
|
||||
const sid = Number.parseInt(ctx.req.param('sid'));
|
||||
const disableEmbed = ctx.req.param('disableEmbed');
|
||||
const disableEmbed = queryToBoolean(ctx.req.param('disableEmbed'));
|
||||
const sortReverse = Number.parseInt(ctx.req.param('sortReverse')) === 1;
|
||||
const page = ctx.req.param('page') ? Number.parseInt(ctx.req.param('page')) : 1;
|
||||
const limit = ctx.req.query('limit') ?? 25;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Route } from '@/types';
|
||||
import cache from '@/utils/cache';
|
||||
import cherrio from 'cheerio';
|
||||
import * as cheerio from 'cheerio';
|
||||
import { parseDate } from '@/utils/parse-date';
|
||||
import puppeteer from '@/utils/puppeteer';
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ async function handler() {
|
|||
const res = await page.evaluate(() => document.documentElement.innerHTML);
|
||||
await page.close();
|
||||
|
||||
const $ = cherrio.load(res);
|
||||
const $ = cheerio.load(res);
|
||||
|
||||
const items = $('div h3 a')
|
||||
.toArray()
|
||||
|
|
@ -67,7 +67,7 @@ async function handler() {
|
|||
waitUntil: 'domcontentloaded',
|
||||
});
|
||||
const res = await page.evaluate(() => document.documentElement.innerHTML);
|
||||
const $ = cherrio.load(res);
|
||||
const $ = cheerio.load(res);
|
||||
await page.close();
|
||||
|
||||
item.description = $('div.article__body').html();
|
||||
|
|
|
|||
|
|
@ -21,9 +21,11 @@ export const route: Route = {
|
|||
supportPodcast: false,
|
||||
supportScihub: false,
|
||||
},
|
||||
radar: {
|
||||
source: ['mmda.booru.org/index.php'],
|
||||
},
|
||||
radar: [
|
||||
{
|
||||
source: ['mmda.booru.org/index.php'],
|
||||
},
|
||||
],
|
||||
name: 'MMDArchive 标签查询',
|
||||
maintainers: ['N78Wy'],
|
||||
handler,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Route } from '@/types';
|
||||
import cache from '@/utils/cache';
|
||||
import cherrio from 'cheerio';
|
||||
import * as cheerio from 'cheerio';
|
||||
import got from '@/utils/got';
|
||||
import { parseDate } from '@/utils/parse-date';
|
||||
import timezone from '@/utils/timezone';
|
||||
|
|
@ -34,7 +34,7 @@ async function handler() {
|
|||
const url = `${homepage}/f/article/articleList?pageNo=1&pageSize=15&createTimeSort=DESC`;
|
||||
const response = await got(url);
|
||||
|
||||
const $ = cherrio.load(response.data);
|
||||
const $ = cheerio.load(response.data);
|
||||
const articles = $('.aw-item').toArray();
|
||||
|
||||
const items = await Promise.all(
|
||||
|
|
@ -45,7 +45,7 @@ async function handler() {
|
|||
|
||||
return cache.tryGet(link, async () => {
|
||||
const result = await got(link);
|
||||
const $ = cherrio.load(result.data);
|
||||
const $ = cheerio.load(result.data);
|
||||
return {
|
||||
title,
|
||||
author: $('.user_name').text(),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
import type { Namespace } from '@/types';
|
||||
|
||||
export const namespace: Namespace = {
|
||||
name: 'dbaplus社群',
|
||||
url: 'dbaplus.cn',
|
||||
};
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import { Route } from '@/types';
|
||||
import { parseDate } from '@/utils/parse-date';
|
||||
import got from '@/utils/got';
|
||||
import { load } from 'cheerio';
|
||||
|
||||
export const route: Route = {
|
||||
path: '/',
|
||||
categories: ['programming'],
|
||||
example: '/dbaplus',
|
||||
radar: [
|
||||
{
|
||||
source: ['dbaplus.cn/'],
|
||||
},
|
||||
],
|
||||
name: '最新文章',
|
||||
maintainers: ['cnkmmk'],
|
||||
handler,
|
||||
url: 'dbaplus.cn/',
|
||||
};
|
||||
|
||||
async function handler() {
|
||||
const url = 'https://dbaplus.cn';
|
||||
const response = await got(`${url}/news-9-1.html`);
|
||||
const $ = load(response.data);
|
||||
|
||||
const list = $('div.col-xs-12.col-md-8.pd30 > div.panel.panel-default.categeay > div.panel-body > ul.media-list.clearfix > li.media')
|
||||
.map((i, e) => {
|
||||
const element = $(e);
|
||||
const title = element.find('h3 > a').text();
|
||||
const link = element.find('h3 > a').attr('href');
|
||||
const description = element.find('div.mt10.geay').text();
|
||||
const dateraw = element.find('span.time').text();
|
||||
|
||||
return {
|
||||
title,
|
||||
description,
|
||||
link,
|
||||
pubDate: parseDate(dateraw, 'YYYY年MM月DD日'),
|
||||
};
|
||||
})
|
||||
.get();
|
||||
|
||||
return {
|
||||
title: 'dbaplus社群',
|
||||
link: url,
|
||||
item: list,
|
||||
};
|
||||
}
|
||||
|
|
@ -37,23 +37,25 @@ export const route: Route = {
|
|||
supportPodcast: false,
|
||||
supportScihub: false,
|
||||
},
|
||||
radar: {
|
||||
source: [
|
||||
'modrinth.com/mod/:id/*',
|
||||
'modrinth.com/plugin/:id/*',
|
||||
'modrinth.com/datapack/:id/*',
|
||||
'modrinth.com/shader/:id/*',
|
||||
'modrinth.com/resourcepack/:id/*',
|
||||
'modrinth.com/modpack/:id/*',
|
||||
'modrinth.com/mod/:id',
|
||||
'modrinth.com/plugin/:id',
|
||||
'modrinth.com/datapack/:id',
|
||||
'modrinth.com/shader/:id',
|
||||
'modrinth.com/resourcepack/:id',
|
||||
'modrinth.com/modpack/:id',
|
||||
],
|
||||
target: '/project/:id/versions',
|
||||
},
|
||||
radar: [
|
||||
{
|
||||
source: [
|
||||
'modrinth.com/mod/:id/*',
|
||||
'modrinth.com/plugin/:id/*',
|
||||
'modrinth.com/datapack/:id/*',
|
||||
'modrinth.com/shader/:id/*',
|
||||
'modrinth.com/resourcepack/:id/*',
|
||||
'modrinth.com/modpack/:id/*',
|
||||
'modrinth.com/mod/:id',
|
||||
'modrinth.com/plugin/:id',
|
||||
'modrinth.com/datapack/:id',
|
||||
'modrinth.com/shader/:id',
|
||||
'modrinth.com/resourcepack/:id',
|
||||
'modrinth.com/modpack/:id',
|
||||
],
|
||||
target: '/project/:id/versions',
|
||||
},
|
||||
],
|
||||
name: 'Project versions',
|
||||
maintainers: ['SettingDust'],
|
||||
handler,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { parseDate } from '@/utils/parse-date';
|
|||
import got from '@/utils/got';
|
||||
import cache from '@/utils/cache';
|
||||
import CryptoJS from 'crypto-js/crypto-js';
|
||||
import { Route } from '@/types';
|
||||
|
||||
export const route: Route = {
|
||||
path: '/hqsz',
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Route } from '@/types';
|
||||
import cache from '@/utils/cache';
|
||||
import cherrio from 'cheerio';
|
||||
import * as cheerio from 'cheerio';
|
||||
import got from '@/utils/got';
|
||||
import { parseDate } from '@/utils/parse-date';
|
||||
|
||||
|
|
@ -32,7 +32,7 @@ async function handler() {
|
|||
const link = 'https://telegram.org/blog';
|
||||
|
||||
const res = await got(link);
|
||||
const $$ = cherrio.load(res.body);
|
||||
const $$ = cheerio.load(res.body);
|
||||
|
||||
const items = await Promise.all(
|
||||
$$('.dev_blog_card_link_wrap')
|
||||
|
|
@ -42,7 +42,7 @@ async function handler() {
|
|||
const link = 'https://telegram.org' + $.attr('href');
|
||||
return cache.tryGet(link, async () => {
|
||||
const result = await got(link);
|
||||
const $ = cherrio.load(result.body);
|
||||
const $ = cheerio.load(result.body);
|
||||
return {
|
||||
title: $('#dev_page_title').text(),
|
||||
link,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import readline from 'node:readline/promises';
|
||||
import { Api, TelegramClient } from 'telegram';
|
||||
import { UserAuthParams } from 'telegram/client/auth';
|
||||
import { StringSession } from 'telegram/sessions';
|
||||
|
|
@ -187,20 +186,3 @@ function streamDocument(obj, thumbSize = '', offset, limit) {
|
|||
}
|
||||
|
||||
export { client, getMediaLink, decodeMedia, getFilename, streamDocument, streamThumbnail };
|
||||
|
||||
if (require.main === module) {
|
||||
Promise.resolve().then(async () => {
|
||||
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
||||
const client = await getClient(
|
||||
{
|
||||
phoneNumber: () => rl.question('Please enter your phone number: '),
|
||||
password: () => rl.question('Please enter your password: '),
|
||||
phoneCode: () => rl.question('Please enter the code you received: '),
|
||||
onError: (err) => process.stderr.write(err.toString()),
|
||||
},
|
||||
''
|
||||
);
|
||||
process.stdout.write(`TELEGRAM_SESSION=${client.session.save()}\n`);
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,13 +79,9 @@ async function handler(ctx) {
|
|||
break;
|
||||
|
||||
case 'cache': {
|
||||
const description = await cache.tryGet(
|
||||
'test',
|
||||
() => ({
|
||||
text: `Cache${++cacheIndex}`,
|
||||
}),
|
||||
config.cache.routeExpire * 2
|
||||
);
|
||||
const description = await cache.tryGet('test', () => ({
|
||||
text: `Cache${++cacheIndex}`,
|
||||
}));
|
||||
item.push({
|
||||
title: 'Cache Title',
|
||||
description: description.text,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,12 @@ export const route: Route = {
|
|||
example: '/youtube/channel/UCDwDMPOZfxVV0x_dz0eQ8KQ',
|
||||
parameters: { id: 'YouTube channel id', embed: 'Default to embed the video, set to any value to disable embedding' },
|
||||
features: {
|
||||
requireConfig: false,
|
||||
requireConfig: [
|
||||
{
|
||||
name: 'YOUTUBE_KEY',
|
||||
description: ' YouTube API Key, support multiple keys, split them with `,`, [API Key application](https://console.developers.google.com/)',
|
||||
},
|
||||
],
|
||||
requirePuppeteer: false,
|
||||
antiCrawler: false,
|
||||
supportBT: false,
|
||||
|
|
|
|||
32
lib/types.ts
32
lib/types.ts
|
|
@ -1,5 +1,31 @@
|
|||
import type { Context } from 'hono';
|
||||
|
||||
// Make sure it's synchronise with scripts/workflow/data.ts
|
||||
type Category =
|
||||
| 'social-media'
|
||||
| 'new-media'
|
||||
| 'traditional-media'
|
||||
| 'bbs'
|
||||
| 'blog'
|
||||
| 'programming'
|
||||
| 'design'
|
||||
| 'live'
|
||||
| 'multimedia'
|
||||
| 'picture'
|
||||
| 'anime'
|
||||
| 'program-update'
|
||||
| 'university'
|
||||
| 'forecast'
|
||||
| 'travel'
|
||||
| 'shopping'
|
||||
| 'game'
|
||||
| 'reading'
|
||||
| 'government'
|
||||
| 'study'
|
||||
| 'journal'
|
||||
| 'finance'
|
||||
| 'other';
|
||||
|
||||
// rss
|
||||
export type DataItem = {
|
||||
title: string;
|
||||
|
|
@ -72,7 +98,7 @@ interface NamespaceItem {
|
|||
/**
|
||||
* The classification of the namespace, which will be written into the corresponding classification document
|
||||
*/
|
||||
categories?: string[];
|
||||
categories?: Category[];
|
||||
|
||||
/**
|
||||
* Hints and additional explanations for users using this namespace, it will be inserted into the documentation
|
||||
|
|
@ -117,7 +143,7 @@ interface RouteItem {
|
|||
/**
|
||||
* The handler function of the route
|
||||
*/
|
||||
handler: (ctx?: Context) => Promise<Data> | Data;
|
||||
handler: (ctx: Context) => Promise<Data> | Data;
|
||||
|
||||
/**
|
||||
* An example URL of the route
|
||||
|
|
@ -137,7 +163,7 @@ interface RouteItem {
|
|||
/**
|
||||
* The classification of the route, which will be written into the corresponding classification documentation
|
||||
*/
|
||||
categories?: string[];
|
||||
categories?: Category[];
|
||||
|
||||
/**
|
||||
* Special features of the route, such as what configuration items it depends on, whether it is strict anti-crawl, whether it supports a certain function and so on
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
import { config } from '@/config';
|
||||
import { PacProxyAgent } from 'pac-proxy-agent';
|
||||
import { HttpsProxyAgent } from 'https-proxy-agent';
|
||||
import { SocksProxyAgent } from 'socks-proxy-agent';
|
||||
|
||||
const proxyIsPAC = config.pacUri || config.pacScript;
|
||||
|
||||
|
|
@ -20,16 +23,13 @@ if (proxyIsPAC) {
|
|||
proxyUrlHandler = proxy.proxyUrlHandler;
|
||||
}
|
||||
|
||||
let agent = null;
|
||||
let agent: PacProxyAgent<string> | HttpsProxyAgent<string> | SocksProxyAgent | null = null;
|
||||
if (proxyIsPAC) {
|
||||
const { PacProxyAgent } = require('pac-proxy-agent');
|
||||
agent = new PacProxyAgent(`pac+${proxyUri}`);
|
||||
} else if (proxyUri) {
|
||||
if (proxyUri.startsWith('http')) {
|
||||
const { HttpsProxyAgent } = require('https-proxy-agent');
|
||||
agent = new HttpsProxyAgent(proxyUri);
|
||||
} else if (proxyUri.startsWith('socks')) {
|
||||
const { SocksProxyAgent } = require('socks-proxy-agent');
|
||||
agent = new SocksProxyAgent(proxyUri);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@ import { config } from '@/config';
|
|||
import puppeteer from 'puppeteer';
|
||||
import logger from './logger';
|
||||
import proxy from './proxy';
|
||||
|
||||
const proxyChain = require('proxy-chain');
|
||||
import proxyChain from 'proxy-chain';
|
||||
|
||||
import { type PuppeteerExtra, addExtra } from 'puppeteer-extra';
|
||||
import StealthPlugin from 'puppeteer-extra-plugin-stealth';
|
||||
|
|
@ -27,28 +26,6 @@ const outPuppeteer = async (
|
|||
let insidePuppeteer: PuppeteerExtra | typeof puppeteer = puppeteer;
|
||||
if (extraOptions.stealth) {
|
||||
insidePuppeteer = addExtra(puppeteer);
|
||||
|
||||
// workaround for vercel/nft #54, #283, #304
|
||||
require('puppeteer-extra-plugin-stealth/evasions/chrome.app');
|
||||
require('puppeteer-extra-plugin-stealth/evasions/chrome.csi');
|
||||
require('puppeteer-extra-plugin-stealth/evasions/chrome.loadTimes');
|
||||
require('puppeteer-extra-plugin-stealth/evasions/chrome.runtime');
|
||||
require('puppeteer-extra-plugin-stealth/evasions/defaultArgs');
|
||||
require('puppeteer-extra-plugin-stealth/evasions/iframe.contentWindow');
|
||||
require('puppeteer-extra-plugin-stealth/evasions/media.codecs');
|
||||
require('puppeteer-extra-plugin-stealth/evasions/navigator.hardwareConcurrency');
|
||||
require('puppeteer-extra-plugin-stealth/evasions/navigator.languages');
|
||||
require('puppeteer-extra-plugin-stealth/evasions/navigator.permissions');
|
||||
require('puppeteer-extra-plugin-stealth/evasions/navigator.plugins');
|
||||
require('puppeteer-extra-plugin-stealth/evasions/navigator.vendor');
|
||||
require('puppeteer-extra-plugin-stealth/evasions/navigator.webdriver');
|
||||
require('puppeteer-extra-plugin-stealth/evasions/sourceurl');
|
||||
require('puppeteer-extra-plugin-stealth/evasions/user-agent-override');
|
||||
require('puppeteer-extra-plugin-stealth/evasions/webgl.vendor');
|
||||
require('puppeteer-extra-plugin-stealth/evasions/window.outerdimensions');
|
||||
require('puppeteer-extra-plugin-user-preferences');
|
||||
require('puppeteer-extra-plugin-user-data-dir');
|
||||
|
||||
insidePuppeteer.use(StealthPlugin());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,13 +52,12 @@ const Index: FC<{
|
|||
<div className="text-left w-[800px] space-y-6 !mt-10">
|
||||
<div className="space-y-2">
|
||||
<p className="mb-2 font-bold">Helpful Information</p>
|
||||
<p>Error Message:</p>
|
||||
<code className="mt-2 block max-h-28 overflow-auto bg-zinc-100 align-bottom w-fit details">{message}</code>
|
||||
<p>Route: <code className="ml-2 bg-zinc-100">{errorRoute}</code></p>
|
||||
<p>Full Route: <code className="ml-2 bg-zinc-100">{requestPath}</code></p>
|
||||
<p>Node Version: <code className="ml-2 bg-zinc-100">{nodeVersion}</code></p>
|
||||
<p>Git Hash: <code className="ml-2 bg-zinc-100">{gitHash}</code></p>
|
||||
<p>Git Date: <code className="ml-2 bg-zinc-100">{gitDate?.toUTCString()}</code></p>
|
||||
<p className="message">Error Message:<br/><code className="mt-2 block max-h-28 overflow-auto bg-zinc-100 align-bottom w-fit details">{message}</code></p>
|
||||
<p className="message">Route: <code className="ml-2 bg-zinc-100">{errorRoute}</code></p>
|
||||
<p className="message">Full Route: <code className="ml-2 bg-zinc-100">{requestPath}</code></p>
|
||||
<p className="message">Node Version: <code className="ml-2 bg-zinc-100">{nodeVersion}</code></p>
|
||||
<p className="message">Git Hash: <code className="ml-2 bg-zinc-100">{gitHash}</code></p>
|
||||
<p className="message">Git Date: <code className="ml-2 bg-zinc-100">{gitDate?.toUTCString()}</code></p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="mb-2 font-bold">Report</p>
|
||||
|
|
|
|||
25
package.json
25
package.json
|
|
@ -19,20 +19,23 @@
|
|||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsx scripts/workflow/build-routes.ts",
|
||||
"dev": "cross-env NODE_ENV=dev tsx watch --no-cache lib/index.ts",
|
||||
"dev:cache": "cross-env NODE_ENV=production tsx watch lib/index.ts",
|
||||
"build:docs": "tsx scripts/workflow/build-docs.ts",
|
||||
"dev": "NODE_ENV=dev tsx watch --no-cache lib/index.ts",
|
||||
"dev:cache": "NODE_ENV=production tsx watch lib/index.ts",
|
||||
"format": "eslint --cache --fix \"**/*.{ts,js,yml}\" && prettier \"**/*.{ts,js,json}\" --write",
|
||||
"format:check": "eslint --cache \"**/*.{ts,js,yml}\" && prettier \"**/*.{ts,js,json}\" --check",
|
||||
"format:staged": "lint-staged",
|
||||
"vitest": "cross-env NODE_ENV=test vitest",
|
||||
"vitest:coverage": "cross-env NODE_ENV=test vitest --coverage.enabled --reporter=junit",
|
||||
"vitest:watch": "cross-env NODE_ENV=test vitest --watch",
|
||||
"vitest": "NODE_ENV=test vitest",
|
||||
"vitest:fullroutes": "NODE_ENV=test FULL_ROUTES_TEST=true vitest --reporter=json --reporter=default --outputFile=\"./assets/build/test-full-routes.json\" routes",
|
||||
"vitest:coverage": "NODE_ENV=test vitest --coverage.enabled --reporter=junit",
|
||||
"vitest:watch": "NODE_ENV=test vitest --watch",
|
||||
"lint": "eslint --cache .",
|
||||
"prepare": "husky || true",
|
||||
"profiling": "NODE_ENV=production tsx --prof lib/index.ts",
|
||||
"start": "cross-env NODE_ENV=production tsx lib/index.ts",
|
||||
"start": "NODE_ENV=production tsx lib/index.ts",
|
||||
"test": "npm run format:check && npm run vitest:coverage"
|
||||
},
|
||||
"lint-staged": {
|
||||
|
|
@ -49,10 +52,10 @@
|
|||
"dependencies": {
|
||||
"@hono/node-server": "1.8.2",
|
||||
"@hono/swagger-ui": "0.2.1",
|
||||
"@hono/zod-openapi": "0.9.8",
|
||||
"@hono/zod-openapi": "0.9.9",
|
||||
"@notionhq/client": "2.2.14",
|
||||
"@postlight/parser": "2.2.3",
|
||||
"@sentry/node": "7.107.0",
|
||||
"@sentry/node": "7.108.0",
|
||||
"@tonyrl/rand-user-agent": "2.0.55",
|
||||
"aes-js": "3.1.2",
|
||||
"art-template": "4.13.2",
|
||||
|
|
@ -110,7 +113,7 @@
|
|||
"telegram": "2.20.2",
|
||||
"tiny-async-pool": "2.1.0",
|
||||
"title": "3.5.3",
|
||||
"tldts": "6.1.13",
|
||||
"tldts": "6.1.14",
|
||||
"tough-cookie": "4.1.3",
|
||||
"tsx": "4.7.1",
|
||||
"twitter-api-v2": "1.16.1",
|
||||
|
|
@ -149,7 +152,6 @@
|
|||
"@typescript-eslint/parser": "7.3.1",
|
||||
"@vercel/nft": "0.26.4",
|
||||
"@vitest/coverage-v8": "1.4.0",
|
||||
"cross-env": "7.0.3",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-config-prettier": "9.1.0",
|
||||
"eslint-nibble": "8.1.0",
|
||||
|
|
@ -159,6 +161,7 @@
|
|||
"eslint-plugin-yml": "1.13.2",
|
||||
"fs-extra": "11.2.0",
|
||||
"husky": "9.0.11",
|
||||
"js-beautify": "1.15.1",
|
||||
"lint-staged": "15.2.2",
|
||||
"mockdate": "3.0.5",
|
||||
"nock": "13.5.4",
|
||||
|
|
@ -167,7 +170,7 @@
|
|||
"supertest": "6.3.4",
|
||||
"tosource": "2.0.0-alpha.3",
|
||||
"typescript": "5.4.3",
|
||||
"unified": "9.2.2",
|
||||
"unified": "11.0.4",
|
||||
"vite-tsconfig-paths": "4.3.2",
|
||||
"vitest": "1.4.0"
|
||||
},
|
||||
|
|
|
|||
302
pnpm-lock.yaml
302
pnpm-lock.yaml
|
|
@ -12,8 +12,8 @@ dependencies:
|
|||
specifier: 0.2.1
|
||||
version: 0.2.1(hono@4.1.3)
|
||||
'@hono/zod-openapi':
|
||||
specifier: 0.9.8
|
||||
version: 0.9.8(hono@4.1.3)(zod@3.22.4)
|
||||
specifier: 0.9.9
|
||||
version: 0.9.9(hono@4.1.3)(zod@3.22.4)
|
||||
'@notionhq/client':
|
||||
specifier: 2.2.14
|
||||
version: 2.2.14
|
||||
|
|
@ -21,8 +21,8 @@ dependencies:
|
|||
specifier: 2.2.3
|
||||
version: 2.2.3
|
||||
'@sentry/node':
|
||||
specifier: 7.107.0
|
||||
version: 7.107.0
|
||||
specifier: 7.108.0
|
||||
version: 7.108.0
|
||||
'@tonyrl/rand-user-agent':
|
||||
specifier: 2.0.55
|
||||
version: 2.0.55
|
||||
|
|
@ -195,8 +195,8 @@ dependencies:
|
|||
specifier: 3.5.3
|
||||
version: 3.5.3
|
||||
tldts:
|
||||
specifier: 6.1.13
|
||||
version: 6.1.13
|
||||
specifier: 6.1.14
|
||||
version: 6.1.14
|
||||
tough-cookie:
|
||||
specifier: 4.1.3
|
||||
version: 4.1.3
|
||||
|
|
@ -307,9 +307,6 @@ devDependencies:
|
|||
'@vitest/coverage-v8':
|
||||
specifier: 1.4.0
|
||||
version: 1.4.0(vitest@1.4.0)
|
||||
cross-env:
|
||||
specifier: 7.0.3
|
||||
version: 7.0.3
|
||||
eslint:
|
||||
specifier: 8.57.0
|
||||
version: 8.57.0
|
||||
|
|
@ -337,6 +334,9 @@ devDependencies:
|
|||
husky:
|
||||
specifier: 9.0.11
|
||||
version: 9.0.11
|
||||
js-beautify:
|
||||
specifier: 1.15.1
|
||||
version: 1.15.1
|
||||
lint-staged:
|
||||
specifier: 15.2.2
|
||||
version: 15.2.2
|
||||
|
|
@ -362,8 +362,8 @@ devDependencies:
|
|||
specifier: 5.4.3
|
||||
version: 5.4.3
|
||||
unified:
|
||||
specifier: 9.2.2
|
||||
version: 9.2.2
|
||||
specifier: 11.0.4
|
||||
version: 11.0.4
|
||||
vite-tsconfig-paths:
|
||||
specifier: 4.3.2
|
||||
version: 4.3.2(typescript@5.4.3)
|
||||
|
|
@ -1915,8 +1915,8 @@ packages:
|
|||
hono: 4.1.3
|
||||
dev: false
|
||||
|
||||
/@hono/zod-openapi@0.9.8(hono@4.1.3)(zod@3.22.4):
|
||||
resolution: {integrity: sha512-NS6lvQEGnsjyQkp+aQjMARREz3WGf19y0+RiiJMVrihWlmKbGaJkPuOWpfFQG6FU9q+FVUyUCryXK+3J07GPAw==}
|
||||
/@hono/zod-openapi@0.9.9(hono@4.1.3)(zod@3.22.4):
|
||||
resolution: {integrity: sha512-Icak3c8WKNS1gFDWmYs2zJ7ra3js9lDeAXNPf5h3fa0SnJQcjcCrLaG6EZPPAbW92HRwCDeQt8yA/ZVp17HPFg==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
peerDependencies:
|
||||
hono: '>=3.11.3'
|
||||
|
|
@ -1970,6 +1970,18 @@ packages:
|
|||
resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==}
|
||||
dev: false
|
||||
|
||||
/@isaacs/cliui@8.0.2:
|
||||
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
string-width: 5.1.2
|
||||
string-width-cjs: /string-width@4.2.3
|
||||
strip-ansi: 7.1.0
|
||||
strip-ansi-cjs: /strip-ansi@6.0.1
|
||||
wrap-ansi: 8.1.0
|
||||
wrap-ansi-cjs: /wrap-ansi@7.0.0
|
||||
dev: true
|
||||
|
||||
/@istanbuljs/schema@0.1.3:
|
||||
resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==}
|
||||
engines: {node: '>=8'}
|
||||
|
|
@ -2098,6 +2110,10 @@ packages:
|
|||
- encoding
|
||||
dev: false
|
||||
|
||||
/@one-ini/wasm@0.1.1:
|
||||
resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==}
|
||||
dev: true
|
||||
|
||||
/@otplib/core@12.0.1:
|
||||
resolution: {integrity: sha512-4sGntwbA/AC+SbPhbsziRiD+jNDdIzsZ3JUyfZwjtKyc/wufl1pnSIaG4Uqx8ymPagujub0o92kgBnB89cuAMA==}
|
||||
dev: false
|
||||
|
|
@ -2131,6 +2147,13 @@ packages:
|
|||
'@otplib/plugin-thirty-two': 12.0.1
|
||||
dev: false
|
||||
|
||||
/@pkgjs/parseargs@0.11.0:
|
||||
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
|
||||
engines: {node: '>=14'}
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@pkgr/core@0.1.1:
|
||||
resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==}
|
||||
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
|
||||
|
|
@ -2334,43 +2357,43 @@ packages:
|
|||
selderee: 0.11.0
|
||||
dev: false
|
||||
|
||||
/@sentry-internal/tracing@7.107.0:
|
||||
resolution: {integrity: sha512-le9wM8+OHBbq7m/8P7JUJ1UhSPIty+Z/HmRXc5Z64ODZcOwFV6TmDpYx729IXDdz36XUKmeI+BeM7yQdTTZPfQ==}
|
||||
/@sentry-internal/tracing@7.108.0:
|
||||
resolution: {integrity: sha512-zuK5XsTsb+U+hgn3SPetYDAogrXsM16U/LLoMW7+TlC6UjlHGYQvmX3o+M2vntejoU1QZS8m1bCAZSMWEypAEw==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
'@sentry/core': 7.107.0
|
||||
'@sentry/types': 7.107.0
|
||||
'@sentry/utils': 7.107.0
|
||||
'@sentry/core': 7.108.0
|
||||
'@sentry/types': 7.108.0
|
||||
'@sentry/utils': 7.108.0
|
||||
dev: false
|
||||
|
||||
/@sentry/core@7.107.0:
|
||||
resolution: {integrity: sha512-C7ogye6+KPyBi8NVL0P8Rxx3Ur7Td8ufnjxosVy678lqY+dcYPk/HONROrzUFYW5fMKWL4/KYnwP+x9uHnkDmw==}
|
||||
/@sentry/core@7.108.0:
|
||||
resolution: {integrity: sha512-I/VNZCFgLASxHZaD0EtxZRM34WG9w2gozqgrKGNMzAymwmQ3K9g/1qmBy4e6iS3YRptb7J5UhQkZQHrcwBbjWQ==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
'@sentry/types': 7.107.0
|
||||
'@sentry/utils': 7.107.0
|
||||
'@sentry/types': 7.108.0
|
||||
'@sentry/utils': 7.108.0
|
||||
dev: false
|
||||
|
||||
/@sentry/node@7.107.0:
|
||||
resolution: {integrity: sha512-UZXkG7uThT2YyPW8AOSKRXp1LbVcBHufa4r1XAwBukA2FKO6HHJPjMUgY6DYVQ6k+BmA56CNfVjYrdLbyjBYYA==}
|
||||
/@sentry/node@7.108.0:
|
||||
resolution: {integrity: sha512-pMxc9txnDDkU4Z8k2Uw/DPSLPehNtWV3mjJ3+my0AMORGYrXLkJI93tddlE5z/7k+GEJdj1HsOLgxUN0OU+HGA==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
'@sentry-internal/tracing': 7.107.0
|
||||
'@sentry/core': 7.107.0
|
||||
'@sentry/types': 7.107.0
|
||||
'@sentry/utils': 7.107.0
|
||||
'@sentry-internal/tracing': 7.108.0
|
||||
'@sentry/core': 7.108.0
|
||||
'@sentry/types': 7.108.0
|
||||
'@sentry/utils': 7.108.0
|
||||
dev: false
|
||||
|
||||
/@sentry/types@7.107.0:
|
||||
resolution: {integrity: sha512-H7qcPjPSUWHE/Zf5bR1EE24G0pGVuJgrSx8Tvvl5nKEepswMYlbXHRVSDN0gTk/E5Z7cqf+hUBOpkQgZyps77w==}
|
||||
/@sentry/types@7.108.0:
|
||||
resolution: {integrity: sha512-bKtHITmBN3kqtqE5eVvL8mY8znM05vEodENwRpcm6TSrrBjC2RnwNWVwGstYDdHpNfFuKwC8mLY9bgMJcENo8g==}
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/@sentry/utils@7.107.0:
|
||||
resolution: {integrity: sha512-C6PbN5gHh73MRHohnReeQ60N8rrLYa9LciHue3Ru2290eSThg4CzsPnx4SzkGpkSeVlhhptKtKZ+hp/ha3iVuw==}
|
||||
/@sentry/utils@7.108.0:
|
||||
resolution: {integrity: sha512-a45yEFD5qtgZaIFRAcFkG8C8lnDzn6t4LfLXuV4OafGAy/3ZAN3XN8wDnrruHkiUezSSANGsLg3bXaLW/JLvJw==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
'@sentry/types': 7.107.0
|
||||
'@sentry/types': 7.108.0
|
||||
dev: false
|
||||
|
||||
/@sinclair/typebox@0.27.8:
|
||||
|
|
@ -2691,10 +2714,6 @@ packages:
|
|||
resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==}
|
||||
dev: false
|
||||
|
||||
/@types/unist@2.0.10:
|
||||
resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==}
|
||||
dev: true
|
||||
|
||||
/@types/unist@3.0.2:
|
||||
resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==}
|
||||
dev: true
|
||||
|
|
@ -2994,6 +3013,11 @@ packages:
|
|||
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
|
||||
dev: true
|
||||
|
||||
/abbrev@2.0.0:
|
||||
resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==}
|
||||
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||
dev: true
|
||||
|
||||
/abort-controller@3.0.0:
|
||||
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
|
||||
engines: {node: '>=6.5'}
|
||||
|
|
@ -3266,10 +3290,6 @@ packages:
|
|||
- supports-color
|
||||
dev: true
|
||||
|
||||
/bail@1.0.5:
|
||||
resolution: {integrity: sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==}
|
||||
dev: true
|
||||
|
||||
/bail@2.0.2:
|
||||
resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==}
|
||||
dev: true
|
||||
|
|
@ -3456,7 +3476,7 @@ packages:
|
|||
http-cache-semantics: 4.1.1
|
||||
keyv: 4.5.4
|
||||
mimic-response: 4.0.0
|
||||
normalize-url: 8.0.1
|
||||
normalize-url: 8.0.0
|
||||
responselike: 3.0.0
|
||||
dev: false
|
||||
|
||||
|
|
@ -3802,6 +3822,11 @@ packages:
|
|||
dependencies:
|
||||
delayed-stream: 1.0.0
|
||||
|
||||
/commander@10.0.1:
|
||||
resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==}
|
||||
engines: {node: '>=14'}
|
||||
dev: true
|
||||
|
||||
/commander@11.1.0:
|
||||
resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==}
|
||||
engines: {node: '>=16'}
|
||||
|
|
@ -3822,6 +3847,13 @@ packages:
|
|||
/concat-map@0.0.1:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
|
||||
/config-chain@1.1.13:
|
||||
resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==}
|
||||
dependencies:
|
||||
ini: 1.3.8
|
||||
proto-list: 1.2.4
|
||||
dev: true
|
||||
|
||||
/console-control-strings@1.1.0:
|
||||
resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
|
||||
dev: true
|
||||
|
|
@ -3872,14 +3904,6 @@ packages:
|
|||
typescript: 5.4.3
|
||||
dev: false
|
||||
|
||||
/cross-env@7.0.3:
|
||||
resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
|
||||
engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
cross-spawn: 7.0.3
|
||||
dev: true
|
||||
|
||||
/cross-spawn@5.1.0:
|
||||
resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==}
|
||||
dependencies:
|
||||
|
|
@ -4232,6 +4256,10 @@ packages:
|
|||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/eastasianwidth@0.2.0:
|
||||
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
||||
dev: true
|
||||
|
||||
/ecc-jsbn@0.1.2:
|
||||
resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==}
|
||||
dependencies:
|
||||
|
|
@ -4245,6 +4273,17 @@ packages:
|
|||
safe-buffer: 5.2.1
|
||||
dev: false
|
||||
|
||||
/editorconfig@1.0.4:
|
||||
resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==}
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@one-ini/wasm': 0.1.1
|
||||
commander: 10.0.1
|
||||
minimatch: 9.0.1
|
||||
semver: 7.6.0
|
||||
dev: true
|
||||
|
||||
/electron-to-chromium@1.4.669:
|
||||
resolution: {integrity: sha512-E2SmpffFPrZhBSgf8ibqanRS2mpuk3FIRDzLDwt7WFpfgJMKDHJs0hmacyP0PS1cWsq0dVkwIIzlscNaterkPg==}
|
||||
dev: true
|
||||
|
|
@ -4260,6 +4299,10 @@ packages:
|
|||
/emoji-regex@8.0.0:
|
||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||
|
||||
/emoji-regex@9.2.2:
|
||||
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
|
||||
dev: true
|
||||
|
||||
/enabled@2.0.0:
|
||||
resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==}
|
||||
dev: false
|
||||
|
|
@ -4936,6 +4979,14 @@ packages:
|
|||
for-in: 1.0.2
|
||||
dev: false
|
||||
|
||||
/foreground-child@3.1.1:
|
||||
resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==}
|
||||
engines: {node: '>=14'}
|
||||
dependencies:
|
||||
cross-spawn: 7.0.3
|
||||
signal-exit: 4.1.0
|
||||
dev: true
|
||||
|
||||
/forever-agent@0.6.1:
|
||||
resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==}
|
||||
dev: false
|
||||
|
|
@ -5155,6 +5206,18 @@ packages:
|
|||
is-glob: 4.0.3
|
||||
dev: true
|
||||
|
||||
/glob@10.3.10:
|
||||
resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==}
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
foreground-child: 3.1.1
|
||||
jackspeak: 2.3.6
|
||||
minimatch: 9.0.3
|
||||
minipass: 5.0.0
|
||||
path-scurry: 1.10.1
|
||||
dev: true
|
||||
|
||||
/glob@7.2.3:
|
||||
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
|
||||
dependencies:
|
||||
|
|
@ -5585,6 +5648,10 @@ packages:
|
|||
/inherits@2.0.4:
|
||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||
|
||||
/ini@1.3.8:
|
||||
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
|
||||
dev: true
|
||||
|
||||
/inquirer@8.2.6:
|
||||
resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
|
@ -5691,11 +5758,6 @@ packages:
|
|||
resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==}
|
||||
dev: false
|
||||
|
||||
/is-buffer@2.0.5:
|
||||
resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==}
|
||||
engines: {node: '>=4'}
|
||||
dev: true
|
||||
|
||||
/is-builtin-module@3.2.1:
|
||||
resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==}
|
||||
engines: {node: '>=6'}
|
||||
|
|
@ -5761,11 +5823,6 @@ packages:
|
|||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/is-plain-obj@2.1.0:
|
||||
resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/is-plain-obj@4.1.0:
|
||||
resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
|
||||
engines: {node: '>=12'}
|
||||
|
|
@ -5855,6 +5912,32 @@ packages:
|
|||
istanbul-lib-report: 3.0.1
|
||||
dev: true
|
||||
|
||||
/jackspeak@2.3.6:
|
||||
resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
|
||||
engines: {node: '>=14'}
|
||||
dependencies:
|
||||
'@isaacs/cliui': 8.0.2
|
||||
optionalDependencies:
|
||||
'@pkgjs/parseargs': 0.11.0
|
||||
dev: true
|
||||
|
||||
/js-beautify@1.15.1:
|
||||
resolution: {integrity: sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==}
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
config-chain: 1.1.13
|
||||
editorconfig: 1.0.4
|
||||
glob: 10.3.10
|
||||
js-cookie: 3.0.5
|
||||
nopt: 7.2.0
|
||||
dev: true
|
||||
|
||||
/js-cookie@3.0.5:
|
||||
resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
|
||||
engines: {node: '>=14'}
|
||||
dev: true
|
||||
|
||||
/js-tokens@3.0.2:
|
||||
resolution: {integrity: sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==}
|
||||
dev: false
|
||||
|
|
@ -6280,7 +6363,6 @@ packages:
|
|||
/lru-cache@10.2.0:
|
||||
resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==}
|
||||
engines: {node: 14 || >=16.14}
|
||||
dev: false
|
||||
|
||||
/lru-cache@4.1.5:
|
||||
resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==}
|
||||
|
|
@ -6688,6 +6770,13 @@ packages:
|
|||
dependencies:
|
||||
brace-expansion: 1.1.11
|
||||
|
||||
/minimatch@9.0.1:
|
||||
resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==}
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
dependencies:
|
||||
brace-expansion: 2.0.1
|
||||
dev: true
|
||||
|
||||
/minimatch@9.0.3:
|
||||
resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==}
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
|
|
@ -6856,6 +6945,14 @@ packages:
|
|||
abbrev: 1.1.1
|
||||
dev: true
|
||||
|
||||
/nopt@7.2.0:
|
||||
resolution: {integrity: sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==}
|
||||
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
abbrev: 2.0.0
|
||||
dev: true
|
||||
|
||||
/normalize-package-data@2.5.0:
|
||||
resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
|
||||
dependencies:
|
||||
|
|
@ -6865,8 +6962,8 @@ packages:
|
|||
validate-npm-package-license: 3.0.4
|
||||
dev: true
|
||||
|
||||
/normalize-url@8.0.1:
|
||||
resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==}
|
||||
/normalize-url@8.0.0:
|
||||
resolution: {integrity: sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==}
|
||||
engines: {node: '>=14.16'}
|
||||
dev: false
|
||||
|
||||
|
|
@ -7188,6 +7285,14 @@ packages:
|
|||
/path-parse@1.0.7:
|
||||
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
|
||||
|
||||
/path-scurry@1.10.1:
|
||||
resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==}
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
dependencies:
|
||||
lru-cache: 10.2.0
|
||||
minipass: 5.0.0
|
||||
dev: true
|
||||
|
||||
/path-type@4.0.0:
|
||||
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
|
||||
engines: {node: '>=8'}
|
||||
|
|
@ -7360,6 +7465,10 @@ packages:
|
|||
engines: {node: '>= 8'}
|
||||
dev: true
|
||||
|
||||
/proto-list@1.2.4:
|
||||
resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
|
||||
dev: true
|
||||
|
||||
/proxy-agent@6.4.0:
|
||||
resolution: {integrity: sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==}
|
||||
engines: {node: '>= 14'}
|
||||
|
|
@ -8285,6 +8394,15 @@ packages:
|
|||
is-fullwidth-code-point: 3.0.0
|
||||
strip-ansi: 6.0.1
|
||||
|
||||
/string-width@5.1.2:
|
||||
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
eastasianwidth: 0.2.0
|
||||
emoji-regex: 9.2.2
|
||||
strip-ansi: 7.1.0
|
||||
dev: true
|
||||
|
||||
/string-width@7.1.0:
|
||||
resolution: {integrity: sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==}
|
||||
engines: {node: '>=18'}
|
||||
|
|
@ -8545,15 +8663,15 @@ packages:
|
|||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/tldts-core@6.1.13:
|
||||
resolution: {integrity: sha512-M1XP4D13YtXARKroULnLsKKuI1NCRAbJmUGGoXqWinajIDOhTeJf/trYUyBoLVx1/Nx1KBKxCrlW57ZW9cMHAA==}
|
||||
/tldts-core@6.1.14:
|
||||
resolution: {integrity: sha512-McyMQkkIUFYhfs3FPTxTn+5mewxERhfwy2x7TWHkBPb1poKaTBJhXehtuMg0FrhXp53J5eXRfvSD/oH/3mk/2A==}
|
||||
dev: false
|
||||
|
||||
/tldts@6.1.13:
|
||||
resolution: {integrity: sha512-+GxHFKVHvUTg2ieNPTx3b/NpZbgJSTZEDdI4cJzTjVYDuxijeHi1tt7CHHsMjLqyc+T50VVgWs3LIb2LrXOzxw==}
|
||||
/tldts@6.1.14:
|
||||
resolution: {integrity: sha512-zGbimRt9fHP68Gbj5fGWmR90xiuRDK/iYukHevT9mcG6Job+HfR119D9DSr6voFWjpStJwOtVfWGpbhNYVwt3A==}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
tldts-core: 6.1.13
|
||||
tldts-core: 6.1.14
|
||||
dev: false
|
||||
|
||||
/tmp@0.0.33:
|
||||
|
|
@ -8627,10 +8745,6 @@ packages:
|
|||
engines: {node: '>= 14.0.0'}
|
||||
dev: false
|
||||
|
||||
/trough@1.0.5:
|
||||
resolution: {integrity: sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==}
|
||||
dev: true
|
||||
|
||||
/trough@2.2.0:
|
||||
resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==}
|
||||
dev: true
|
||||
|
|
@ -8844,24 +8958,6 @@ packages:
|
|||
vfile: 6.0.1
|
||||
dev: true
|
||||
|
||||
/unified@9.2.2:
|
||||
resolution: {integrity: sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==}
|
||||
dependencies:
|
||||
'@types/unist': 2.0.10
|
||||
bail: 1.0.5
|
||||
extend: 3.0.2
|
||||
is-buffer: 2.0.5
|
||||
is-plain-obj: 2.1.0
|
||||
trough: 1.0.5
|
||||
vfile: 4.2.1
|
||||
dev: true
|
||||
|
||||
/unist-util-stringify-position@2.0.3:
|
||||
resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==}
|
||||
dependencies:
|
||||
'@types/unist': 2.0.10
|
||||
dev: true
|
||||
|
||||
/unist-util-stringify-position@4.0.0:
|
||||
resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
|
||||
dependencies:
|
||||
|
|
@ -8988,13 +9084,6 @@ packages:
|
|||
extsprintf: 1.3.0
|
||||
dev: false
|
||||
|
||||
/vfile-message@2.0.4:
|
||||
resolution: {integrity: sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==}
|
||||
dependencies:
|
||||
'@types/unist': 2.0.10
|
||||
unist-util-stringify-position: 2.0.3
|
||||
dev: true
|
||||
|
||||
/vfile-message@4.0.2:
|
||||
resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==}
|
||||
dependencies:
|
||||
|
|
@ -9002,15 +9091,6 @@ packages:
|
|||
unist-util-stringify-position: 4.0.0
|
||||
dev: true
|
||||
|
||||
/vfile@4.2.1:
|
||||
resolution: {integrity: sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==}
|
||||
dependencies:
|
||||
'@types/unist': 2.0.10
|
||||
is-buffer: 2.0.5
|
||||
unist-util-stringify-position: 2.0.3
|
||||
vfile-message: 2.0.4
|
||||
dev: true
|
||||
|
||||
/vfile@6.0.1:
|
||||
resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==}
|
||||
dependencies:
|
||||
|
|
@ -9282,7 +9362,15 @@ packages:
|
|||
ansi-styles: 4.3.0
|
||||
string-width: 4.2.3
|
||||
strip-ansi: 6.0.1
|
||||
dev: false
|
||||
|
||||
/wrap-ansi@8.1.0:
|
||||
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
ansi-styles: 6.2.1
|
||||
string-width: 5.1.2
|
||||
strip-ansi: 7.1.0
|
||||
dev: true
|
||||
|
||||
/wrap-ansi@9.0.0:
|
||||
resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,122 @@
|
|||
import { namespaces } from '../../lib/registry';
|
||||
import fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import { categories } from './data';
|
||||
import { getCurrentPath } from '../../lib/utils/helpers';
|
||||
|
||||
const fullTests = await (await fetch('https://raw.githubusercontent.com/DIYgod/RSSHub/gh-pages/build/test-full-routes.json')).json();
|
||||
const testResult = fullTests.testResults[0].assertionResults;
|
||||
|
||||
const __dirname = getCurrentPath(import.meta.url);
|
||||
|
||||
const docs = {};
|
||||
|
||||
for (const namespace in namespaces) {
|
||||
let defaultCategory = namespaces[namespace].categories?.[0];
|
||||
if (!defaultCategory) {
|
||||
for (const path in namespaces[namespace].routes) {
|
||||
if (namespaces[namespace].routes[path].categories) {
|
||||
defaultCategory = namespaces[namespace].routes[path].categories[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!defaultCategory) {
|
||||
defaultCategory = 'other';
|
||||
}
|
||||
for (const path in namespaces[namespace].routes) {
|
||||
const realPath = `/${namespace}${path}`;
|
||||
const data = namespaces[namespace].routes[path];
|
||||
const categories = data.categories || namespaces[namespace].categories || [defaultCategory];
|
||||
// docs.json
|
||||
for (const category of categories) {
|
||||
if (!docs[category]) {
|
||||
docs[category] = {};
|
||||
}
|
||||
if (!docs[category][namespace]) {
|
||||
docs[category][namespace] = {
|
||||
routes: {},
|
||||
};
|
||||
}
|
||||
docs[category][namespace].name = namespaces[namespace].name;
|
||||
docs[category][namespace].url = namespaces[namespace].url;
|
||||
docs[category][namespace].description = namespaces[namespace].description;
|
||||
docs[category][namespace].routes[realPath] = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate markdown
|
||||
const pinyinCompare = new Intl.Collator('zh-Hans-CN-u-co-pinyin').compare;
|
||||
const isASCII = (str) => /^[\u0000-\u007F]*$/.test(str);
|
||||
|
||||
function generateMd(lang) {
|
||||
const md = {};
|
||||
for (const category in docs) {
|
||||
const nameObj = categories.find((c) => c.link.includes(category));
|
||||
if (!nameObj) {
|
||||
throw new Error(`Category not found: ${category}, please double check your spelling.`);
|
||||
}
|
||||
|
||||
md[category] = `# ${`${nameObj.icon} ${nameObj[lang]}`}\n\n`;
|
||||
|
||||
const namespaces = Object.keys(docs[category]).sort((a, b) => {
|
||||
const aname = docs[category][a].name[0];
|
||||
const bname = docs[category][b].name[0];
|
||||
const ia = isASCII(aname);
|
||||
const ib = isASCII(bname);
|
||||
if (ia && ib) {
|
||||
return aname.toLowerCase() < bname.toLowerCase() ? -1 : 1;
|
||||
} else if (ia || ib) {
|
||||
return ia > ib ? -1 : 1;
|
||||
} else {
|
||||
return pinyinCompare(aname, bname);
|
||||
}
|
||||
});
|
||||
for (const namespace of namespaces) {
|
||||
if (docs[category][namespace].name === 'Unknown') {
|
||||
docs[category][namespace].name = namespace;
|
||||
}
|
||||
md[category] += `## ${docs[category][namespace].name || namespace} ${docs[category][namespace].url ? `<Site url="${docs[category][namespace].url}"/>` : ''}\n\n`;
|
||||
if (docs[category][namespace].description) {
|
||||
md[category] += `${docs[category][namespace].description}\n\n`;
|
||||
}
|
||||
|
||||
const realPaths = Object.keys(docs[category][namespace].routes).sort((a, b) => {
|
||||
const aname = docs[category][namespace].routes[a].name[0];
|
||||
const bname = docs[category][namespace].routes[b].name[0];
|
||||
const ia = isASCII(aname);
|
||||
const ib = isASCII(bname);
|
||||
if (ia && ib) {
|
||||
return aname.toLowerCase() < bname.toLowerCase() ? -1 : 1;
|
||||
} else if (ia || ib) {
|
||||
return ia > ib ? -1 : 1;
|
||||
} else {
|
||||
return pinyinCompare(aname, bname);
|
||||
}
|
||||
});
|
||||
|
||||
for (const realPath of realPaths) {
|
||||
const data = docs[category][namespace].routes[realPath];
|
||||
const test = testResult.find((t) => t.title === realPath);
|
||||
const parsedTest = test
|
||||
? {
|
||||
code: test.status === 'passed' ? 0 : 1,
|
||||
message: test.failureMessages?.[0],
|
||||
}
|
||||
: undefined;
|
||||
md[category] += `### ${data.name} ${data.url || docs[category][namespace].url ? `<Site url="${data.url || docs[category][namespace].url}" size="sm" />` : ''}\n\n`;
|
||||
md[category] += `<Route namespace="${namespace}" :data='${JSON.stringify(data).replaceAll(`'`, ''')}' :test='${JSON.stringify(parsedTest)?.replaceAll(`'`, ''')}' />\n\n`;
|
||||
if (data.description) {
|
||||
md[category] += `${data.description}\n\n`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fs.mkdirSync(path.join(__dirname, `../../assets/build/docs/${lang}`), { recursive: true });
|
||||
for (const category in md) {
|
||||
fs.writeFileSync(path.join(__dirname, `../../assets/build/docs/${lang}/${category}.md`), md[category]);
|
||||
}
|
||||
}
|
||||
generateMd('en');
|
||||
generateMd('zh');
|
||||
|
|
@ -4,7 +4,9 @@ import { parse } from 'tldts';
|
|||
import fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import toSource from 'tosource';
|
||||
import { categories } from './data';
|
||||
|
||||
import { getCurrentPath } from '../../lib/utils/helpers';
|
||||
const __dirname = getCurrentPath(import.meta.url);
|
||||
|
||||
const maintainers: Record<string, string[]> = {};
|
||||
const radar: {
|
||||
|
|
@ -13,7 +15,6 @@ const radar: {
|
|||
[subdomain: string]: RadarItem[] | string;
|
||||
};
|
||||
} = {};
|
||||
const docs = {};
|
||||
|
||||
for (const namespace in namespaces) {
|
||||
let defaultCategory = namespaces[namespace].categories?.[0];
|
||||
|
|
@ -63,21 +64,6 @@ for (const namespace in namespaces) {
|
|||
}
|
||||
}
|
||||
}
|
||||
// docs.json
|
||||
for (const category of categories) {
|
||||
if (!docs[category]) {
|
||||
docs[category] = {};
|
||||
}
|
||||
if (!docs[category][namespace]) {
|
||||
docs[category][namespace] = {
|
||||
routes: {},
|
||||
};
|
||||
}
|
||||
docs[category][namespace].name = namespaces[namespace].name;
|
||||
docs[category][namespace].url = namespaces[namespace].url;
|
||||
docs[category][namespace].description = namespaces[namespace].description;
|
||||
docs[category][namespace].routes[realPath] = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -85,67 +71,3 @@ fs.writeFileSync(path.join(__dirname, '../../assets/build/radar-rules.json'), JS
|
|||
fs.writeFileSync(path.join(__dirname, '../../assets/build/radar-rules.js'), `(${toSource(radar)})`);
|
||||
fs.writeFileSync(path.join(__dirname, '../../assets/build/maintainers.json'), JSON.stringify(maintainers, null, 2));
|
||||
fs.writeFileSync(path.join(__dirname, '../../assets/build/routes.json'), JSON.stringify(namespaces, null, 2));
|
||||
|
||||
// Generate markdown
|
||||
const pinyinCompare = new Intl.Collator('zh-Hans-CN-u-co-pinyin').compare;
|
||||
const isASCII = (str) => /^[\u0000-\u007F]*$/.test(str);
|
||||
|
||||
function generateMd(lang) {
|
||||
const md = {};
|
||||
for (const category in docs) {
|
||||
const nameObj = categories.find((c) => c.link.includes(category));
|
||||
md[category] = `# ${`${nameObj!.icon} ${nameObj![lang]}`}\n\n`;
|
||||
|
||||
const namespaces = Object.keys(docs[category]).sort((a, b) => {
|
||||
const aname = docs[category][a].name[0];
|
||||
const bname = docs[category][b].name[0];
|
||||
const ia = isASCII(aname);
|
||||
const ib = isASCII(bname);
|
||||
if (ia && ib) {
|
||||
return aname.toLowerCase() < bname.toLowerCase() ? -1 : 1;
|
||||
} else if (ia || ib) {
|
||||
return ia > ib ? -1 : 1;
|
||||
} else {
|
||||
return pinyinCompare(aname, bname);
|
||||
}
|
||||
});
|
||||
for (const namespace of namespaces) {
|
||||
if (docs[category][namespace].name === 'Unknown') {
|
||||
docs[category][namespace].name = namespace;
|
||||
}
|
||||
md[category] += `## ${docs[category][namespace].name || namespace} ${docs[category][namespace].url ? `<Site url="${docs[category][namespace].url}"/>` : ''}\n\n`;
|
||||
if (docs[category][namespace].description) {
|
||||
md[category] += `${docs[category][namespace].description}\n\n`;
|
||||
}
|
||||
|
||||
const realPaths = Object.keys(docs[category][namespace].routes).sort((a, b) => {
|
||||
const aname = docs[category][namespace].routes[a].name[0];
|
||||
const bname = docs[category][namespace].routes[b].name[0];
|
||||
const ia = isASCII(aname);
|
||||
const ib = isASCII(bname);
|
||||
if (ia && ib) {
|
||||
return aname.toLowerCase() < bname.toLowerCase() ? -1 : 1;
|
||||
} else if (ia || ib) {
|
||||
return ia > ib ? -1 : 1;
|
||||
} else {
|
||||
return pinyinCompare(aname, bname);
|
||||
}
|
||||
});
|
||||
|
||||
for (const realPath of realPaths) {
|
||||
const data = docs[category][namespace].routes[realPath];
|
||||
md[category] += `### ${data.name} ${data.url || docs[category][namespace].url ? `<Site url="${data.url || docs[category][namespace].url}" size="sm" />` : ''}\n\n`;
|
||||
md[category] += `<Route namespace="${namespace}" :data='${JSON.stringify(data).replaceAll(`'`, ''')}' />\n\n`;
|
||||
if (data.description) {
|
||||
md[category] += `${data.description}\n\n`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fs.mkdirSync(path.join(__dirname, `../../assets/build/docs/${lang}`), { recursive: true });
|
||||
for (const category in md) {
|
||||
fs.writeFileSync(path.join(__dirname, `../../assets/build/docs/${lang}/${category}.md`), md[category]);
|
||||
}
|
||||
}
|
||||
generateMd('en');
|
||||
generateMd('zh');
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
const unified = require('unified');
|
||||
const parse = require('remark-parse');
|
||||
const got = require('got');
|
||||
import { unified } from 'unified';
|
||||
import remarkParse from 'remark-parse';
|
||||
|
||||
// @TODO maybe we could use label or some other better ways to distinguish bug/feature issues
|
||||
const matchTitle = ['路由地址', 'Routes'];
|
||||
const maintainerURL = 'https://raw.githubusercontent.com/DIYgod/RSSHub/gh-pages/build/maintainer.json';
|
||||
const maintainerURL = 'https://raw.githubusercontent.com/DIYgod/RSSHub/gh-pages/build/maintainers.json';
|
||||
const successTag = 'Bug Ping: Pinged';
|
||||
const parseFailTag = 'Bug Ping: Parsing Failed';
|
||||
const failTag = 'Bug Ping: Not Found';
|
||||
|
|
@ -15,7 +14,7 @@ const route = 'Route';
|
|||
const dndUsernames = new Set([]);
|
||||
|
||||
async function parseBodyRoutes(body, core) {
|
||||
const ast = await unified().use(parse).parse(body);
|
||||
const ast = await unified().use(remarkParse).parse(body);
|
||||
|
||||
// Is this a bug report?
|
||||
const title = ast.children[0].children[0].value.trim();
|
||||
|
|
@ -44,10 +43,8 @@ async function parseBodyRoutes(body, core) {
|
|||
}
|
||||
|
||||
async function getMaintainersByRoutes(routes, core) {
|
||||
// TODO: change me when https://github.com/actions/github-script is run on node20
|
||||
// const response = await fetch(maintainerURL);
|
||||
// const maintainers = await response.json();
|
||||
const maintainers = await got(maintainerURL).json();
|
||||
const response = await fetch(maintainerURL);
|
||||
const maintainers = await response.json();
|
||||
|
||||
return routes.map((e) => {
|
||||
const m = maintainers[e];
|
||||
|
|
@ -59,9 +56,9 @@ async function getMaintainersByRoutes(routes, core) {
|
|||
});
|
||||
}
|
||||
|
||||
module.exports = async ({ github, context, core }) => {
|
||||
export default async function callMaintainer({ github, context, core }) {
|
||||
const body = context.payload.issue.body;
|
||||
const issue_facts = {
|
||||
const issueFacts = {
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
|
|
@ -70,7 +67,7 @@ module.exports = async ({ github, context, core }) => {
|
|||
const addLabels = (labels) =>
|
||||
github.rest.issues
|
||||
.addLabels({
|
||||
...issue_facts,
|
||||
...issueFacts,
|
||||
labels,
|
||||
})
|
||||
.catch((error) => {
|
||||
|
|
@ -79,7 +76,7 @@ module.exports = async ({ github, context, core }) => {
|
|||
const updateIssueState = (state) =>
|
||||
github.rest.issues
|
||||
.update({
|
||||
...issue_facts,
|
||||
...issueFacts,
|
||||
state,
|
||||
})
|
||||
.catch((error) => {
|
||||
|
|
@ -138,7 +135,7 @@ module.exports = async ({ github, context, core }) => {
|
|||
}
|
||||
}
|
||||
|
||||
const labels = [`Count: ${successCount}/${routes.length}`];
|
||||
const labels = [''];
|
||||
|
||||
if (failedCount > 0) {
|
||||
labels.push(failTag);
|
||||
|
|
@ -160,14 +157,14 @@ module.exports = async ({ github, context, core }) => {
|
|||
// Reply to the issue and notify the maintainers (if any)
|
||||
await github.rest.issues
|
||||
.createComment({
|
||||
...issue_facts,
|
||||
...issueFacts,
|
||||
body: `${comments}
|
||||
|
||||
|
||||
> To maintainers: if you are not willing to be disturbed, list your username in \`scripts/workflow/test-issue/call-maintainer.js\`. In this way, your username will be wrapped in an inline code block when tagged so you will not be notified.
|
||||
|
||||
如果所有路由都无法匹配,issue 将会被自动关闭。如果 issue 和路由无关,请使用 \`NOROUTE\` 关键词,或者留下评论。我们会重新审核。
|
||||
If all routes can not be found, the issue will be closed automatically. Please use \`NOROUTE\` for a route-irrelevant issue or leave a comment if it is a mistake.
|
||||
如果所有路由都无法匹配,issue 将会被自动关闭。如果 issue 和路由无关,请使用 \`NOROUTE\` 关键词,或者留下评论。我们会重新审核。
|
||||
`,
|
||||
})
|
||||
.catch((error) => {
|
||||
|
|
@ -177,4 +174,4 @@ If all routes can not be found, the issue will be closed automatically. Please u
|
|||
if (failedCount && emptyCount === 0 && successCount === 0) {
|
||||
await updateIssueState('closed');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -1,14 +1,15 @@
|
|||
const noFound = 'Auto: Route No Found';
|
||||
const testFailed = 'Auto: Route Test Failed';
|
||||
const allowedUser = new Set(['dependabot[bot]', 'pull[bot]']); // dependabot and downstream PR requested by pull[bot]
|
||||
|
||||
module.exports = async ({ github, context, core }, body, number, sender) => {
|
||||
export default async function identify({ github, context, core }, body, number, sender) {
|
||||
core.debug(`sender: ${sender}`);
|
||||
core.debug(`body: ${body}`);
|
||||
// Remove all HTML comments before performing the match
|
||||
const bodyNoCmts = body.replaceAll(/<!--[\S\s]*?-->/g, '');
|
||||
const m = bodyNoCmts.match(/```routes\s+([\S\s]*?)```/);
|
||||
core.debug(`match: ${m}`);
|
||||
let res = null;
|
||||
let routes = null;
|
||||
|
||||
const issueFacts = {
|
||||
owner: context.repo.owner,
|
||||
|
|
@ -31,11 +32,11 @@ module.exports = async ({ github, context, core }, body, number, sender) => {
|
|||
core.warning(error);
|
||||
});
|
||||
|
||||
const removeLabel = () =>
|
||||
const removeLabel = (labelName = noFound) =>
|
||||
github.rest.issues
|
||||
.removeLabel({
|
||||
...issueFacts,
|
||||
name: noFound,
|
||||
name: labelName,
|
||||
})
|
||||
.catch((error) => {
|
||||
core.warning(error);
|
||||
|
|
@ -80,8 +81,13 @@ module.exports = async ({ github, context, core }, body, number, sender) => {
|
|||
.catch((error) => {
|
||||
core.warning(error);
|
||||
});
|
||||
if (pr.pull_request && pr.state === 'closed') {
|
||||
await updatePrState('open');
|
||||
if (pr.pull_request) {
|
||||
if (pr.state === 'closed') {
|
||||
await updatePrState('open');
|
||||
}
|
||||
if (pr.labels.some((e) => e.name === testFailed)) {
|
||||
await removeLabel(testFailed);
|
||||
}
|
||||
}
|
||||
|
||||
if (allowedUser.has(sender)) {
|
||||
|
|
@ -94,19 +100,25 @@ module.exports = async ({ github, context, core }, body, number, sender) => {
|
|||
}
|
||||
|
||||
if (m && m[1]) {
|
||||
res = m[1].trim().split(/\r?\n/);
|
||||
core.info(`routes detected: ${res}`);
|
||||
routes = m[1].trim().split(/\r?\n/);
|
||||
core.info(`routes detected: ${routes}`);
|
||||
|
||||
if (res.length && res[0] === 'NOROUTE') {
|
||||
if (routes.length && routes[0] === 'NOROUTE') {
|
||||
core.info('PR stated no route, passing');
|
||||
await removeLabel();
|
||||
await addLabels(['Auto: Route Test Skipped']);
|
||||
|
||||
return;
|
||||
} else if (res.length && !res.some((e) => e.includes('/:'))) {
|
||||
} else if (routes.length) {
|
||||
if (routes.some((e) => e.includes('/:'))) {
|
||||
await addLabels([noFound]);
|
||||
return createComment(`Please use actual values in \`routes\` section instead of path parameters.
|
||||
请在 \`routes\` 部分使用实际值而不是路径参数。`);
|
||||
}
|
||||
|
||||
core.exportVariable('TEST_CONTINUE', true);
|
||||
await removeLabel();
|
||||
return res;
|
||||
return routes;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -119,4 +131,4 @@ module.exports = async ({ github, context, core }, body, number, sender) => {
|
|||
}
|
||||
|
||||
throw new Error('Please follow the PR rules: failed to detect route');
|
||||
};
|
||||
}
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
/* eslint-disable no-await-in-loop */
|
||||
|
||||
module.exports = async ({ github, context, core, got }, baseUrl, routes, number) => {
|
||||
if (routes[0] === 'NOROUTE') {
|
||||
return;
|
||||
}
|
||||
|
||||
const links = routes.map((e) => {
|
||||
const l = e.startsWith('/') ? e : `/${e}`;
|
||||
return `${baseUrl}${l}`;
|
||||
});
|
||||
|
||||
let com_l = [];
|
||||
let com = `Successfully [generated](${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}) as following:\n`;
|
||||
|
||||
for (const lks of links) {
|
||||
core.info(`testing route: ${lks}`);
|
||||
// Intended, one at a time
|
||||
let success = false;
|
||||
let detail;
|
||||
try {
|
||||
// TODO: change me when https://github.com/actions/github-script is run on node20
|
||||
// const res = await fetch(lks);
|
||||
// if (!res.ok) {
|
||||
// throw res;
|
||||
// }
|
||||
// success = true;
|
||||
// detail = (await res.text()).replace(/\s+(\n|$)/g, '\n');
|
||||
const res = await got(lks);
|
||||
if (res && res.body) {
|
||||
success = true;
|
||||
detail = res.body.replaceAll(/\s+(\n|$)/g, '\n');
|
||||
}
|
||||
} catch (error) {
|
||||
// TODO: change me when https://github.com/actions/github-script is run on node20
|
||||
// detail = `HTTPError: Response code ${err.status} (${err.statusText})`;
|
||||
// const res = await err.text();
|
||||
// const errInfoList = err.body && res.match(/(?<=<pre class="message">)(.+?)(?=<\/pre>)/gs);
|
||||
detail = error.toString();
|
||||
const errInfoList = error.response && error.response.body && error.response.body.match(/(?<=<pre class="message">)(.+?)(?=<\/pre>)/gs);
|
||||
if (errInfoList) {
|
||||
detail += '\n\n';
|
||||
detail += errInfoList
|
||||
.slice(0, 3)
|
||||
.map((e) => e.trim())
|
||||
.join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
let temp_com = `
|
||||
<details>
|
||||
<summary><a href="${lks}">${lks}</a> - ${success ? 'Success ✔️' : '<b>Failed ❌</b>'}</summary>
|
||||
|
||||
\`\`\`${success ? 'rss' : ''}`;
|
||||
temp_com += `
|
||||
${detail.slice(0, 65300 - temp_com.length)}
|
||||
\`\`\`
|
||||
</details>
|
||||
`;
|
||||
if (com.length + temp_com.length >= 65500) {
|
||||
com += '\n\n...';
|
||||
com_l.push(com);
|
||||
com = temp_com;
|
||||
} else {
|
||||
com += temp_com;
|
||||
}
|
||||
}
|
||||
|
||||
if (com.length > 0) {
|
||||
com_l.push(com);
|
||||
}
|
||||
|
||||
if (com_l.length >= 5) {
|
||||
com_l = com_l.slice(0, 5);
|
||||
}
|
||||
|
||||
if (process.env.PULL_REQUEST) {
|
||||
await github.rest.issues
|
||||
.addLabels({
|
||||
issue_number: number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: ['Auto: Route Test Complete'],
|
||||
})
|
||||
.catch((error) => {
|
||||
core.warning(error);
|
||||
});
|
||||
}
|
||||
|
||||
for (const com_s of com_l) {
|
||||
// Intended, one at a time
|
||||
await github.rest.issues
|
||||
.createComment({
|
||||
issue_number: number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: com_s,
|
||||
})
|
||||
.catch((error) => {
|
||||
core.warning(error);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
import jsBeautify from 'js-beautify';
|
||||
|
||||
export default async function test({ github, context, core }, baseUrl, routes, number) {
|
||||
if (routes[0] === 'NOROUTE') {
|
||||
return;
|
||||
}
|
||||
|
||||
const links = routes.map((e) => {
|
||||
const l = e.startsWith('/') ? e : `/${e}`;
|
||||
return `${baseUrl}${l}`;
|
||||
});
|
||||
|
||||
let commentList = [];
|
||||
let comment = `Successfully [generated](${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}) as following:\n`;
|
||||
|
||||
for await (const lks of links) {
|
||||
core.info(`testing route: ${lks}`);
|
||||
// Intended, one at a time
|
||||
let success = false;
|
||||
let detail;
|
||||
|
||||
const res = await fetch(lks);
|
||||
const body = await res.text();
|
||||
if (res.ok) {
|
||||
success = true;
|
||||
detail = jsBeautify.html(body.replaceAll(/\s+(\n|$)/g, '\n'), { indent_size: 2 });
|
||||
} else {
|
||||
detail = `HTTPError: Response code ${res.status} (${res.statusText})`;
|
||||
const errInfoList = body && body.match(/(?<=<p class="message">)(.+?)(?=<\/p>)/gs);
|
||||
if (errInfoList) {
|
||||
detail += '\n\n';
|
||||
detail += errInfoList
|
||||
.slice(0, 5)
|
||||
.map((e) => (e.length > 1000 ? e.slice(0, 1000) + '...' : e).trim())
|
||||
.join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
let routeFeedback = `
|
||||
<details>
|
||||
<summary><a href="${lks}">${lks}</a> - ${success ? 'Success ✔️' : '<b>Failed ❌</b>'}</summary>
|
||||
|
||||
\`\`\`${success ? 'rss' : ''}`;
|
||||
routeFeedback += `
|
||||
${detail.slice(0, 65300 - routeFeedback.length)}
|
||||
\`\`\`
|
||||
</details>
|
||||
`;
|
||||
if (comment.length + routeFeedback.length >= 65500) {
|
||||
comment += '\n\n...';
|
||||
commentList.push(comment);
|
||||
comment = routeFeedback;
|
||||
} else {
|
||||
comment += routeFeedback;
|
||||
}
|
||||
}
|
||||
|
||||
if (comment.length > 0) {
|
||||
commentList.push(comment);
|
||||
}
|
||||
|
||||
if (commentList.length >= 5) {
|
||||
commentList = commentList.slice(0, 5);
|
||||
}
|
||||
|
||||
if (process.env.PULL_REQUEST) {
|
||||
await github.rest.issues
|
||||
.addLabels({
|
||||
issue_number: number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: ['Auto: Route Test Complete'],
|
||||
})
|
||||
.catch((error) => {
|
||||
core.warning(error);
|
||||
});
|
||||
}
|
||||
|
||||
for await (const comment of commentList) {
|
||||
// Intended, one at a time
|
||||
await github.rest.issues
|
||||
.createComment({
|
||||
issue_number: number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: comment,
|
||||
})
|
||||
.catch((error) => {
|
||||
core.warning(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"moduleResolution": "node",
|
||||
"strict": true,
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "hono/jsx",
|
||||
|
|
@ -11,7 +11,12 @@
|
|||
},
|
||||
"esModuleInterop": true,
|
||||
"noImplicitAny": false,
|
||||
"outDir": "./dist"
|
||||
"outDir": "./dist",
|
||||
"skipLibCheck": true,
|
||||
"noEmit": true,
|
||||
"incremental": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true
|
||||
},
|
||||
"include": ["./lib/**/*", "./api/vercel.ts"],
|
||||
"exclude": ["node_modules", "*.test.*"]
|
||||
|
|
|
|||
Loading…
Reference in New Issue