refactor: enforce typecheck (#22917)

* feat(types): add upvotes, downvotes, and comments to DataItem

* refactor: fix TS2339

* refactor: fix TS2322

* refactor: fix TS2322

* refactor: fix TS2345

* refactor: fix TS2339

* refactor: fix TS2322

* refactor: fix TS2345

* refactor: fix TS2531/2532/18048/18047

* refactor: fix TS18046/TS2571

* refactor: fix TS2538

* refactor: fix TS2554/2769/2558

* refactor: fix TS2362/2363

* refactor: fix TS2353/TS2561

* refactor: fix TS2741/2739/2740

* refactor: fix TS2304 TS2307 TS2305 TS2614 TS2552 TS2551

* refactor: fix TS2352

* refactor: fix TS2604 TS17001

* refactor: fix TS2367 TS2366

* refactor: fix TS2493

* refactor: fix TS2454

* refactor: fix TS2790

* refactor: fix TS2488

* refactor: fix TS2677

* refactor: fix TS18048/18049/2532

* refactor: fix TS2322

* refactor: fix TS2344

* refactor: fix TS2339

* refactor: fix TS2339

* refactor: fix TS2365

* refactor: fix TS1117

* refactor: fix  TS2561

* refactor: fix TS2555

* refactor: fix TS2503

* refactor: fix TS2339

mobile api no longer work

* fix: clean slop

* fix: use Context instead of any

* fix: fix more typing after merge

* fix: add void return for ctx.redirect in handler

* chore: add type check to lint and husky

* fix: remove ts ignore

* chore: add oxlint rule

* fix: suppress build artifact

* fix: update getArticles to use cache

* fix: codeql

* fix(route/typeless): use async gunzip

* fix: address review

* fix: address review

* fix: double quotes escape
This commit is contained in:
Tony
2026-08-04 09:00:41 +08:00
committed by GitHub
parent 7df4f3edfb
commit bb12119123
1958 changed files with 10003 additions and 9770 deletions
+2
View File
@@ -29,6 +29,8 @@ jobs:
- run: pnpm i
- name: Install oxlint to SARIF converter
run: pnpm i -g oxlint-json-to-sarif
- name: Typecheck
run: pnpm typecheck
- name: Lint
run: pnpm exec oxlint --type-aware --config=.oxlintrc.ci.json
--format=json | oxlint-json-to-sarif > oxlint-results.sarif
+1
View File
@@ -1 +1,2 @@
lint-staged
pnpm typecheck
+11 -1
View File
@@ -641,7 +641,17 @@
// #region --- TypeScript ---
"@typescript-eslint/array-type": ["error", { "default": "array-simple" }],
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-ts-comment": [
"warn",
{
"ts-check": false,
"ts-expect-error": "allow-with-description",
"ts-ignore": "allow-with-description",
"ts-nocheck": "allow-with-description",
"minimumDescriptionLength": 3
}
],
"@typescript-eslint/consistent-indexed-object-style": "off", // stylistic
"@typescript-eslint/consistent-type-definitions": "off", // stylistic
"@typescript-eslint/dot-notation": "error", // type-aware
+1 -1
View File
@@ -14,7 +14,7 @@ describe('api/follow/config', () => {
json: (data: unknown) => data,
};
const result = handler(ctx as any) as Record<string, unknown>;
const result = (handler as (c: typeof ctx) => unknown)(ctx) as Record<string, unknown>;
expect(result).toMatchObject({
ownerUserId: 'owner',
+2 -2
View File
@@ -1,4 +1,4 @@
import type { Next } from 'hono';
import type { Context, Next } from 'hono';
import { describe, expect, it, vi } from 'vitest';
import { handler } from '@/api/radar/rules/one';
@@ -14,7 +14,7 @@ describe('api/radar/rules/one', () => {
json: vi.fn((value) => value),
};
const result = await handler(ctx as any, noopNext);
const result = await handler(ctx as unknown as Context, noopNext);
expect(ctx.req.valid).toHaveBeenCalledWith('param');
expect(ctx.json).toHaveBeenCalledWith(undefined);
+1 -1
View File
@@ -21,7 +21,7 @@ describe('app-bootstrap', () => {
const before = new Set(process.listeners('uncaughtException'));
await import('@/app-bootstrap');
const after = process.listeners('uncaughtException');
const listener = after.find((fn) => !before.has(fn));
const listener = after.find((fn) => !before.has(fn)) as ((error: Error) => void) | undefined;
expect(listener).toBeDefined();
listener?.(new Error('boom'));
+1 -1
View File
@@ -23,7 +23,7 @@ describe('request-rewriter', () => {
await app.request('/test/httperror');
// headers
const headers: Headers = fetchSpy.mock.lastCall?.[0].headers;
const headers: Headers = (fetchSpy.mock.lastCall as unknown as [Request])?.[0].headers;
expect(headers.get('user-agent')).toMatch(/Chrome/);
});
});
+3 -3
View File
@@ -2,7 +2,7 @@
// This Worker manages the RSSHub container lifecycle and proxies requests
import { Container } from '@cloudflare/containers';
import type { KVNamespace } from '@cloudflare/workers-types';
import type { DurableObjectNamespace, KVNamespace } from '@cloudflare/workers-types';
const INSTANCE_COUNT = 20;
@@ -13,7 +13,7 @@ export class RSSHubContainer extends Container {
}
interface Env {
RSSHUB_CONTAINER: DurableObjectNamespace<RSSHubContainer>;
RSSHUB_CONTAINER: DurableObjectNamespace;
CONFIG: KVNamespace;
}
@@ -36,7 +36,7 @@ export default {
// Randomly select an instance for load balancing
const instanceIndex = Math.floor(Math.random() * INSTANCE_COUNT);
const container = env.RSSHUB_CONTAINER.getByName(`rsshub-${instanceIndex}`);
const container = env.RSSHUB_CONTAINER.getByName(`rsshub-${instanceIndex}`) as unknown as RSSHubContainer;
// Start container with env vars and wait for port to be ready
await container.startAndWaitForPorts({
+5 -4
View File
@@ -1,4 +1,5 @@
import { load } from 'cheerio';
import type { Context } from 'hono';
import { afterEach, describe, expect, it, vi } from 'vitest';
import app from '@/app';
@@ -147,9 +148,9 @@ describe('error handler honeybadger', () => {
header: vi.fn(),
json: (payload: unknown) => payload,
html: (payload: unknown) => payload,
};
} as unknown as Context;
errorHandler(new Error('boom'), ctx as any);
errorHandler(new Error('boom'), ctx);
expect(notify).toHaveBeenCalledWith(expect.any(Error), {
context: { name: 'test' },
@@ -205,9 +206,9 @@ describe('error handler sentry', () => {
header: vi.fn(),
json: (payload: unknown) => payload,
html: (payload: unknown) => payload,
};
} as unknown as Context;
errorHandler(new Error('boom'), ctx as any);
errorHandler(new Error('boom'), ctx);
expect(setTag).toHaveBeenCalledWith('name', 'test');
expect(captureException).toHaveBeenCalled();
+1 -1
View File
@@ -1,6 +1,6 @@
import { afterEach, describe, expect, it, vi } from 'vitest';
const serve = vi.fn(() => ({ close: vi.fn() }));
const serve = vi.fn<(...args: any[]) => any>(() => ({ close: vi.fn() }));
const logger = {
info: vi.fn(),
warn: vi.fn(),
+4 -3
View File
@@ -1,3 +1,4 @@
import type { Context } from 'hono';
import Parser from 'rss-parser';
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from 'vitest';
@@ -451,7 +452,7 @@ describe('anti-hotlink edge cases', () => {
},
get: (key: string) => store.get(key),
set: (key: string, value: unknown) => store.set(key, value),
};
} as unknown as Context;
};
beforeAll(() => {
@@ -479,7 +480,7 @@ describe('anti-hotlink edge cases', () => {
};
const ctx = createCtx({ image_hotlink_template: 'https://img.test/${href}' }, data);
await antiHotlink(ctx as any, async () => {});
await antiHotlink(ctx, async () => {});
expect(data.image).toBe('http://invalid url');
expect(errorSpy).toHaveBeenCalled();
@@ -495,7 +496,7 @@ describe('anti-hotlink edge cases', () => {
};
const ctx = createCtx({ multimedia_hotlink_template: 'https://media.test/${href}' }, data);
await antiHotlink(ctx as any, async () => {});
await antiHotlink(ctx, async () => {});
expect(data.image).toBe('https://example.com/img.jpg');
});
+4 -3
View File
@@ -1,3 +1,4 @@
import type { Context } from 'hono';
import Parser from 'rss-parser';
import { afterAll, afterEach, describe, expect, it, vi } from 'vitest';
@@ -190,7 +191,7 @@ describe('cache', () => {
});
describe('cache middleware error handling', () => {
const setSpy = vi.fn(() => null);
const setSpy = vi.fn<(...args: any[]) => null>(() => null);
const getSpy = vi.fn(() => null);
afterAll(() => {
@@ -231,10 +232,10 @@ describe('cache middleware error handling', () => {
header: vi.fn(),
set: vi.fn(),
get: vi.fn(),
};
} as unknown as Context;
await expect(
cacheMiddleware(ctx as any, () => {
cacheMiddleware(ctx, () => {
throw new Error('boom');
})
).rejects.toThrow('boom');
+3 -2
View File
@@ -1,3 +1,4 @@
import type { Context } from 'hono';
import Parser from 'rss-parser';
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from 'vitest';
@@ -19,8 +20,8 @@ const runMiddleware = async (data: any, query: Record<string, string | undefined
},
get: (key: string) => store.get(key),
set: (key: string, value: unknown) => store.set(key, value),
};
await middleware(ctx as any, async () => {});
} as unknown as Context;
await middleware(ctx, async () => {});
return store.get('data') as any;
};
+3 -3
View File
@@ -201,7 +201,7 @@ const middleware: MiddlewareHandler = async (ctx, next) => {
const title = item.title || '';
const description = item.description || title;
const author = getAuthorString(item);
const category = item.category || [];
const category = (item.category as string[] | undefined) || [];
const isFilter =
regex instanceof RE2JS
? regex.matcher(title).find() || regex.matcher(description).find() || regex.matcher(author).find() || category.some((c) => regex.matcher(c).find())
@@ -217,7 +217,7 @@ const middleware: MiddlewareHandler = async (ctx, next) => {
const title = item.title || '';
const description = item.description || title;
const author = getAuthorString(item);
const category = item.category || [];
const category = (item.category as string[] | undefined) || [];
let isFilter = true;
if (ctx.req.query('filter_title')) {
@@ -246,7 +246,7 @@ const middleware: MiddlewareHandler = async (ctx, next) => {
const title = item.title;
const description = item.description || title;
const author = getAuthorString(item);
const category = item.category || [];
const category = (item.category as string[] | undefined) || [];
let isFilter = true;
if (ctx.req.query('filterout') || ctx.req.query('filterout_title')) {
+12 -11
View File
@@ -1,3 +1,4 @@
import type { Context } from 'hono';
import { describe, expect, it, vi } from 'vitest';
import { config } from '@/config';
@@ -19,16 +20,16 @@ const createCtx = (query: Record<string, string | undefined>, data: any, extra:
redirect: vi.fn((url: string, status: number) => ({ url, status })),
header: vi.fn(),
res: { headers: new Headers() },
};
} as unknown as Context & { json: ReturnType<typeof vi.fn>; html: ReturnType<typeof vi.fn>; render: ReturnType<typeof vi.fn>; body: ReturnType<typeof vi.fn>; redirect: ReturnType<typeof vi.fn>; header: ReturnType<typeof vi.fn> };
};
describe('template middleware', () => {
it('returns debug json when requested', async () => {
const originalDebug = config.debugInfo;
config.debugInfo = true;
config.debugInfo = 'true';
const ctx = createCtx({ format: 'debug.json' }, { item: [] }, { json: { ok: true } });
const result = await template(ctx as any, async () => {});
const result = await template(ctx, async () => {});
expect(result).toEqual({ ok: true });
expect(ctx.json).toHaveBeenCalled();
@@ -38,7 +39,7 @@ describe('template middleware', () => {
it('returns api data without rendering', async () => {
const ctx = createCtx({}, null, { apiData: { ok: true } });
const result = await template(ctx as any, async () => {});
const result = await template(ctx, async () => {});
expect(result).toEqual({ ok: true });
expect(ctx.json).toHaveBeenCalledWith({ ok: true });
@@ -46,10 +47,10 @@ describe('template middleware', () => {
it('renders debug html snippet when requested', async () => {
const originalDebug = config.debugInfo;
config.debugInfo = true;
config.debugInfo = 'true';
const ctx = createCtx({ format: '0.debug.html' }, { item: [{ description: 'Hello' }] });
const result = await template(ctx as any, async () => {});
const result = await template(ctx, async () => {});
expect(result).toBe('Hello');
expect(ctx.html).toHaveBeenCalled();
@@ -72,7 +73,7 @@ describe('template middleware', () => {
],
};
const ctx = createCtx({ format: 'rss' }, data);
await template(ctx as any, async () => {});
await template(ctx, async () => {});
expect(data.item[0].title).toBe('ABC...');
expect(data.item[0].author).toBe('Alice, Bob');
@@ -93,7 +94,7 @@ describe('template middleware', () => {
],
};
const ctx = createCtx({ format: 'json' }, data);
await template(ctx as any, async () => {});
await template(ctx, async () => {});
expect(data.item[0].pubDate).toBe('');
expect(data.item[0].updated).toBe('');
@@ -101,7 +102,7 @@ describe('template middleware', () => {
it('returns redirect response when redirect is set', async () => {
const ctx = createCtx({}, { item: [] }, { redirect: 'https://example.com' });
const result = await template(ctx as any, async () => {});
const result = await template(ctx, async () => {});
expect(result).toEqual({ url: 'https://example.com', status: 301 });
expect(ctx.redirect).toHaveBeenCalledWith('https://example.com', 301);
@@ -118,7 +119,7 @@ describe('template middleware', () => {
],
};
const ctx = createCtx({ format: 'rss3' }, data);
const result = await template(ctx as any, async () => {});
const result = await template(ctx, async () => {});
expect(ctx.json).toHaveBeenCalled();
expect(result).toHaveProperty('data');
@@ -135,7 +136,7 @@ describe('template middleware', () => {
],
};
const ctx = createCtx({ format: 'atom' }, data);
await template(ctx as any, async () => {});
await template(ctx, async () => {});
expect(ctx.render).toHaveBeenCalled();
});
+6 -4
View File
@@ -1,3 +1,4 @@
import type { Context, Next } from 'hono';
import { describe, expect, it } from 'vitest';
import { config } from '@/config';
@@ -6,7 +7,7 @@ import trace from '@/middleware/trace';
describe('trace middleware', () => {
it('skips tracing when debugInfo is disabled', async () => {
const originalDebug = config.debugInfo;
config.debugInfo = false;
config.debugInfo = false as unknown as string;
let called = false;
const ctx = {
@@ -14,12 +15,13 @@ describe('trace middleware', () => {
method: 'GET',
raw: new Request('http://localhost/test'),
},
};
const next = () => {
} as unknown as Context;
const next: Next = () => {
called = true;
return Promise.resolve();
};
await trace(ctx as any, next);
await trace(ctx, next);
expect(called).toBe(true);
config.debugInfo = originalDebug;
+4 -2
View File
@@ -1,5 +1,7 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import type { Route } from '@/types';
describe('pkg', () => {
beforeEach(() => {
vi.resetModules();
@@ -104,7 +106,7 @@ describe('pkg', () => {
],
allowEmpty: true,
}),
},
} as unknown as Route,
{
name: 'Custom Namespace',
url: 'https://example.com',
@@ -128,7 +130,7 @@ describe('pkg', () => {
path: '/hello',
name: 'Custom Response',
handler: () => new Response('ok'),
});
} as unknown as Route);
const app = (await import('@/app')).default;
const response = await app.request('/custom-response/hello');
+1 -1
View File
@@ -88,7 +88,7 @@ afterAll(() => {
const buildApp = () => {
const namespaces: NamespacesType = {};
const dev = createDevRegistry({ routesDirectory, namespaces });
const app = new Hono();
const app = new Hono<{ Variables: { fromOuter: string; data: Record<string, unknown>; apiData: Record<string, unknown> } }>();
app.use(async (ctx, next) => {
ctx.set('fromOuter', 'bridged');
await next();
+2 -1
View File
@@ -64,6 +64,7 @@ describe('registry', () => {
vi.stubEnv('DISABLE_NSFW', 'true');
const { namespaces } = await import('./registry');
// @ts-ignore build artifact of pnpm build:routes
const routesModule = await import('../assets/build/routes.json');
const rawNamespaces = (routesModule.default ?? routesModule) as Record<string, { routes?: Record<string, { features?: { nsfw?: boolean } }> }>;
const nsfwNamespaces = Object.entries(rawNamespaces).filter(([, namespace]) => Object.values(namespace.routes ?? {}).some((route) => route.features?.nsfw));
@@ -165,7 +166,7 @@ const perDirectoryMock = (fakeDirectories: Record<string, Record<string, unknown
};
const wrap = (registry: Hono) => {
const app = new Hono();
const app = new Hono<{ Variables: { data: Record<string, unknown>; apiData: Record<string, unknown> } }>();
app.use(async (ctx, next) => {
const response = await next();
const apiData = ctx.get('apiData');
+4 -2
View File
@@ -38,17 +38,19 @@ let namespaces: NamespacesType = {};
let devRegistry: DevRegistry | undefined;
if (config.isPackage) {
// @ts-ignore build artifact of pnpm build:routes
namespaces = (await import('../assets/build/routes.js')).default;
} else {
switch (process.env.NODE_ENV || process.env.VERCEL_ENV) {
case 'production':
// @ts-ignore build artifact of pnpm build:routes
namespaces = (await import('../assets/build/routes.js')).default;
break;
case 'test':
// @ts-expect-error
// @ts-expect-error TS2322 the JSON module's inferred literal type is narrower than NamespacesType
namespaces = await import('../assets/build/routes.json');
if (namespaces.default) {
// @ts-ignore
// @ts-expect-error TS2322 the JSON module's default export does not satisfy NamespacesType
namespaces = namespaces.default;
}
break;
+14 -14
View File
@@ -1,7 +1,7 @@
import { load } from 'cheerio';
import { renderToString } from 'hono/jsx/dom/server';
import type { Route } from '@/types';
import type { DataItem, Language, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -23,11 +23,11 @@ export const handler = async (ctx) => {
let items = $('div.article-list ul li')
.slice(0, limit)
.toArray()
.map((item) => {
item = $(item);
.map((item): DataItem & { link: string } => {
const $item = $(item);
const title = item.find('h3').text();
const image = item.find('img').prop('src');
const title = $item.find('h3').text();
const image = $item.find('img').prop('src');
const description = renderToString(
<>
@@ -36,22 +36,22 @@ export const handler = async (ctx) => {
<img src={image} alt={title} />
</figure>
) : null}
{item.find('div.p-row').text() ? <blockquote>{item.find('div.p-row').text()}</blockquote> : null}
{$item.find('div.p-row').text() ? <blockquote>{$item.find('div.p-row').text()}</blockquote> : null}
</>
);
return {
title,
description,
pubDate: parseDate(item.find('span.time').text()),
link: new URL(item.find('h3 a').prop('href'), rootUrl).href,
pubDate: parseDate($item.find('span.time').text()),
link: new URL($item.find('h3 a').prop('href')!, rootUrl).href,
content: {
html: description,
text: item.find('div.p-row').text(),
text: $item.find('div.p-row').text(),
},
image,
banner: image,
language,
language: language as Language,
};
});
@@ -66,14 +66,14 @@ export const handler = async (ctx) => {
const description = $$('div.articleContent').html();
item.title = title;
item.description = description;
item.description = description ?? '';
item.pubDate = timezone(parseDate($$('.time').text()), 8);
item.category = $$('meta[name="keywords"]').prop('content').split(/,/);
item.content = {
html: description,
html: description ?? '',
text: $$('div.articleContent').text(),
};
item.language = language;
item.language = language as Language;
return item;
})
@@ -91,7 +91,7 @@ export const handler = async (ctx) => {
allowEmpty: true,
image,
author: title.split(/,/).pop(),
language,
language: language as Language,
};
};
+7 -7
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -37,18 +37,18 @@ async function handler(ctx) {
const list = $(listId === '3' ? '.col-xs-12 .thumbnail > a' : '.col-md-8 .list-group > a')
.toArray()
.map((item) => {
item = $(item);
.map((item): DataItem => {
const $item = $(item);
return {
title: item.attr('title'),
link: item.attr('href').startsWith('http') ? item.attr('href') : `${baseUrl}${item.attr('href')}`,
title: $item.attr('title')!,
link: $item.attr('href')!.startsWith('http') ? $item.attr('href') : `${baseUrl}${$item.attr('href')}`,
};
})
.filter((i) => !i.link.includes('m.0818tuan.com/tb1111.php') && !i.link.includes('www.0818tuan.com/pdd/zudui.php'));
.filter((i) => !i.link!.includes('m.0818tuan.com/tb1111.php') && !i.link!.includes('www.0818tuan.com/pdd/zudui.php'));
const items = await Promise.all(
list.map((item) =>
cache.tryGet(item.link, async () => {
cache.tryGet(item.link!, async () => {
const { data: response } = await got(item.link);
const $ = load(response);
+5 -5
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -37,11 +37,11 @@ async function handler() {
const list = alist
.toArray()
.map((item) => {
item = $(item);
.map((item): DataItem & { link: string } => {
const $item = $(item);
const link = item.attr('href') || '';
const title = item.text();
const link = $item.attr('href') || '';
const title = $item.text();
const pubDate = extractDateFromURL(link);
return {
+9 -9
View File
@@ -3,7 +3,7 @@ import { load } from 'cheerio';
import type { Element } from 'domhandler';
import type { Context } from 'hono';
import type { Data, DataItem, Route } from '@/types';
import type { Data, DataItem, Language, Route } from '@/types';
import { ViewType } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
@@ -25,7 +25,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
let items: DataItem[] = $('table#home-table tr:not(.gore)')
.slice(0, limit)
.toArray()
.map((el): Element => {
.map((el) => {
const $el: Cheerio<Element> = $(el);
const $categoryEl: Cheerio<Element> = $el.find('td.category');
@@ -44,11 +44,11 @@ export const handler = async (ctx: Context): Promise<Data> => {
},
]
: undefined,
category: $categoryEl.html(),
catalogue: $catalogueEl.html(),
category: $categoryEl.html() ?? undefined,
catalogue: $catalogueEl.html() ?? undefined,
title,
size: $el.find('td.size').text(),
date: $dateEl.html(),
date: $dateEl.html() ?? undefined,
});
const pubDateStr: string | undefined = $dateEl.text();
const linkUrl: string | undefined = $el.find('td.title a').attr('href');
@@ -68,7 +68,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
image,
banner: image,
updated: upDatedStr ? parseDate(upDatedStr, 'DD.MM.YYYY') : undefined,
language,
language: language as Language,
};
return processedItem;
@@ -81,7 +81,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
}
return cache.tryGet(item.link, async (): Promise<DataItem> => {
const detailResponse = await ofetch(item.link);
const detailResponse = await ofetch(item.link!);
const $$: CheerioAPI = load(detailResponse);
const description: string | undefined =
@@ -117,7 +117,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
allowEmpty: true,
image: $('div.logo img').attr('src') ? new URL($('div.logo img').attr('src') as string, baseUrl).href : undefined,
author: $('meta[property="og:site_name"]').attr('content'),
language,
language: language as Language,
id: targetUrl,
};
};
@@ -146,7 +146,7 @@ To subscribe to [Movie HD 1080p](https://0xxx.ws?category=Movie-HD-1080p), where
supportBT: false,
supportPodcast: false,
supportScihub: false,
nfsw: true,
nsfw: true,
},
radar: [
{
+7 -7
View File
@@ -3,7 +3,7 @@ import { load } from 'cheerio';
import type { Element } from 'domhandler';
import type { Context } from 'hono';
import type { Data, DataItem, Route } from '@/types';
import type { Data, DataItem, Language, Route } from '@/types';
import { ViewType } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
@@ -25,7 +25,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
let items: DataItem[] = $('ul.l_newshot li dl.lhotnew2')
.slice(0, limit)
.toArray()
.map((el): Element => {
.map((el) => {
const $el: Cheerio<Element> = $(el);
const $aEl: Cheerio<Element> = $el.find('dd h1 a');
@@ -55,7 +55,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
image,
banner: image,
updated: upDatedStr ? parseRelativeDate(upDatedStr) : undefined,
language,
language: language as Language,
};
return processedItem;
@@ -69,14 +69,14 @@ export const handler = async (ctx: Context): Promise<Data> => {
}
return cache.tryGet(item.link, async (): Promise<DataItem> => {
const detailResponse = await ofetch(item.link);
const detailResponse = await ofetch(item.link!);
const $$: CheerioAPI = load(detailResponse);
const title: string = $$('div.entity_title h1 a').text();
const image: string | undefined = $$('div.entity_thumb img.img-responsive').attr('src');
const description: string | undefined = renderDescription({
description: $$('div.entity_content').html(),
description: $$('div.entity_content').html() ?? undefined,
});
const pubDateStr: string | undefined = detailResponse.match(/var\stime\s=\s"(.*?)";/)?.[1];
const categoryEls: Element[] = $$('div.entity_tag span a').toArray();
@@ -95,7 +95,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
image,
banner: image,
updated: upDatedStr ? parseDate(upDatedStr) : item.updated,
language,
language: language as Language,
};
return {
@@ -118,7 +118,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
allowEmpty: true,
image: $('a.navbar-brand img').attr('src') ? new URL($('a.navbar-brand img').attr('src') as string, baseUrl).href : undefined,
author,
language,
language: language as Language,
id: $('meta[property="og:url"]').attr('content'),
};
};
+3 -3
View File
@@ -1,7 +1,7 @@
import { load } from 'cheerio';
import iconv from 'iconv-lite';
import type { Route } from '@/types';
import type { Language, Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -51,7 +51,7 @@ export const handler = async (ctx) => {
image,
banner: item.picUrl,
updated: parseDate(item.rtime, 'X'),
language,
language: language as Language,
};
}) ?? [];
@@ -66,7 +66,7 @@ export const handler = async (ctx) => {
allowEmpty: true,
image,
author: $('meta[property="og:site_name"]').prop('content'),
language,
language: language as Language,
};
};
+3 -3
View File
@@ -3,7 +3,7 @@ import { load } from 'cheerio';
import type { Context } from 'hono';
import { renderToString } from 'hono/jsx/dom/server';
import type { Data, DataItem, Route } from '@/types';
import type { Data, DataItem, Language, Route } from '@/types';
import { ViewType } from '@/types';
import ofetch from '@/utils/ofetch';
import { parseDate } from '@/utils/parse-date';
@@ -70,7 +70,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
image,
banner: image,
updated: updated ? parseDate(updated) : undefined,
language,
language: language as Language,
};
return processedItem;
@@ -86,7 +86,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
allowEmpty: true,
image: $('img').first().attr('src') ? new URL($('img').first().attr('src') as string, baseUrl).href : undefined,
author: $('div#webnameDiv').text(),
language,
language: language as Language,
id: targetUrl,
};
};
+3 -3
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -42,9 +42,9 @@ async function handler(ctx) {
const list = $('#newList > ul > li')
.toArray()
.map((item) => ({
.map((item): DataItem & { link: string } => ({
title: $(item).find('a').text(),
link: new URL($(item).find('a').attr('href'), link).href,
link: new URL($(item).find('a').attr('href')!, link).href,
pubDate: parseDate($(item).find('span').text().slice(1, -1)),
}));
+13 -13
View File
@@ -68,29 +68,29 @@ async function handler(ctx) {
const items = $('.columns')
.toArray()
.map((item) => {
item = $(item);
const $item = $(item);
const id = item.find('.title a').text();
const size = item.find('.title span').text();
const pubDate = item.find('.subtitle a').attr('href').split('/date/').pop();
const description = item.find('.has-text-grey-dark').text();
const actresses = item
const id = $item.find('.title a').text();
const size = $item.find('.title span').text();
const pubDate = $item.find('.subtitle a').attr('href')!.split('/date/').pop();
const description = $item.find('.has-text-grey-dark').text();
const actresses = $item
.find('.panel-block')
.toArray()
.map((a) => $(a).text().trim());
const tags = item
const tags = $item
.find('.tag')
.toArray()
.map((t) => $(t).text().trim());
const magnet = item.find('a[title="Magnet torrent"]').attr('href');
const link = item.find('a[title="Download .torrent"]').attr('href');
const image = item.find('.image').attr('src');
const magnet = $item.find('a[title="Magnet torrent"]').attr('href');
const link = $item.find('a[title="Download .torrent"]').attr('href');
const image = $item.find('.image').attr('src');
return {
title: `${id} ${size}`,
pubDate: parseDate(pubDate, 'YYYY/MM/DD'),
link: new URL(item.find('a').first().attr('href'), rootUrl).href,
description: renderToString(<JavDescription image={image} id={id} size={size} pubDate={pubDate} description={description} actresses={actresses} tags={tags} magnet={magnet} link={link} />),
pubDate: parseDate(pubDate!, 'YYYY/MM/DD'),
link: new URL($item.find('a').first().attr('href')!, rootUrl).href,
description: renderToString(<JavDescription image={image} id={id} size={size} pubDate={pubDate!} description={description} actresses={actresses} tags={tags} magnet={magnet} link={link} />),
author: actresses.join(', '),
category: [...tags, ...actresses],
enclosure_type: 'application/x-bittorrent',
+14 -14
View File
@@ -68,31 +68,31 @@ async function handler(ctx) {
const items = $('.columns')
.toArray()
.map((item) => {
item = $(item);
const $item = $(item);
const id = item.find('.title a').text();
const size = item.find('.title span').text();
const pubDate = item.find('.subtitle a').attr('href').split('/date/').pop();
const description = item.find('.has-text-grey-dark').text();
const actresses = item
const id = $item.find('.title a').text();
const size = $item.find('.title span').text();
const pubDate = $item.find('.subtitle a').attr('href')!.split('/date/').pop();
const description = $item.find('.has-text-grey-dark').text();
const actresses = $item
.find('.panel-block')
.toArray()
.map((a) => $(a).text().trim());
const tags = item
const tags = $item
.find('.tag')
.toArray()
.map((t) => $(t).text().trim());
const magnet = item.find('a[title="Magnet torrent"]').attr('href');
const link = item.find('a[title="Download .torrent"]').attr('href');
const onErrorAttr = item.find('.image').attr('onerror');
const magnet = $item.find('a[title="Magnet torrent"]').attr('href');
const link = $item.find('a[title="Download .torrent"]').attr('href');
const onErrorAttr = $item.find('.image').attr('onerror');
const backupImageRegex = /this\.src='(.*?)'/;
const match = backupImageRegex.exec(onErrorAttr);
const image = match ? match[1] : item.find('.image').attr('src');
const match = backupImageRegex.exec(onErrorAttr!);
const image = match ? match[1] : $item.find('.image').attr('src');
return {
title: `${id} ${size}`,
pubDate: parseDate(pubDate, 'YYYY/MM/DD'),
link: new URL(item.find('a').first().attr('href'), rootUrl).href,
pubDate: parseDate(pubDate!, 'YYYY/MM/DD'),
link: new URL($item.find('a').first().attr('href')!, rootUrl).href,
description: renderToString(
<>
{image ? <img src={image} /> : null}
+5 -5
View File
@@ -38,12 +38,12 @@ async function handler(ctx) {
.slice(0, limit)
.toArray()
.map((item) => {
item = $(item);
const itemImg = item.find('a.img img');
const $item = $(item);
const itemImg = $item.find('a.img img');
return {
title: item.find('h4 a').text(),
link: item.find('a').first().attr('href'),
pubDate: timezone(parseDate(item.find('.time').text()), 8),
title: $item.find('h4 a').text(),
link: $item.find('a').first().attr('href'),
pubDate: timezone(parseDate($item.find('.time').text()), 8),
imgsrc: itemImg.attr('src') ?? itemImg.attr('_src'),
};
});
+1 -1
View File
@@ -42,7 +42,7 @@ const renderDescription = (pg, description, itunes_duration, info) =>
</div>
{info ? (
<div>
<audio src={`https://music.163.com/song/media/outer/url?id=${pg.mainTrackId}.mp3`} controls="controls"></audio>
<audio src={`https://music.163.com/song/media/outer/url?id=${pg.mainTrackId}.mp3`} controls></audio>
<p>: {itunes_duration}</p>
<p>
<a href={`https://music.163.com/program/${pg.id}`}></a>
+12 -11
View File
@@ -2,7 +2,7 @@ import { load } from 'cheerio';
import iconv from 'iconv-lite';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Route } from '@/types';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -140,24 +140,25 @@ async function handler(ctx) {
.eq(timeRange[time].index + (category === 'whole' ? (type === 'click' ? -1 : 2) : type === 'click' ? 0 : 2))
.find('table tbody tr td a')
.toArray()
.map((item) => {
item = $(item);
.map((item): DataItem => {
const $item = $(item);
return {
link: item.attr('href'),
link: $item.attr('href'),
title: '',
};
});
const items = await Promise.all(
list.map((item) =>
cache.tryGet(item.link, async () => {
cache.tryGet(item.link!, async () => {
try {
let link;
if (['auto', 'house', 'travel'].includes(category)) {
const category = item.link.split('.163.com', 1)[0].split('//').pop().split('.').pop();
link = `https://3g.163.com/${category}/article/${item.link.split('/').pop()}`;
const category = item.link!.split('.163.com', 1)[0].split('//').pop()!.split('.').pop();
link = `https://3g.163.com/${category}/article/${item.link!.split('/').pop()}`;
} else {
const pathname = new URL(item.link).pathname;
const pathname = new URL(item.link!).pathname;
link = `https://3g.163.com${pathname}`;
}
@@ -175,8 +176,8 @@ async function handler(ctx) {
elem.attribs.src = elem.attribs['data-src'] ?? elem.attribs.src;
});
item.title = content('meta[property="og:title"]').attr('content').replace('_手机网易网', '');
item.pubDate = parseDate(content('meta[property="og:release_date"]').attr('content'));
item.title = content('meta[property="og:title"]').attr('content')!.replace('_手机网易网', '');
item.pubDate = parseDate(content('meta[property="og:release_date"]').attr('content')!);
item.description = content('.article-body').html();
} catch {
return '';
@@ -190,6 +191,6 @@ async function handler(ctx) {
return {
title: `网易新闻${timeRange[time].title}${type === 'click' ? '点击' : '跟帖'}榜 - ${cfg.title}`,
link: currentUrl,
item: items.filter(Boolean),
item: items.filter(Boolean) as DataItem[],
};
}
+2 -2
View File
@@ -65,10 +65,10 @@ async function handler() {
const initialState = JSON.parse(
$('script')
.text()
.match(/window\.__INITIAL_STATE__=(.*);\(function\(\)\{var/)[1]
.match(/window\.__INITIAL_STATE__=(.*);\(function\(\)\{var/)![1]
);
const list = Object.values(initialState.courseindex.myModules).flatMap((mod) =>
const list = Object.values<Record<string, any>>(initialState.courseindex.myModules).flatMap((mod) =>
mod.contents.map((item) => ({
title: `${item.title} - ${item.subtitle}`,
author: item.authorName,
+7 -7
View File
@@ -61,8 +61,8 @@ async function handler(ctx) {
if (urls) {
items = urls.slice(0, ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 50).map((item) => ({
link: item.match(/url:"(.*)",/)[1],
}));
link: item.match(/url:"(.*)",/)![1],
})) as DataItem[];
} else {
const $ = load(data);
@@ -70,16 +70,16 @@ async function handler(ctx) {
.slice(0, ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 50)
.toArray()
.map((_, item) => {
item = $(item);
const $item = $(item as any);
return {
link: item.attr('href'),
link: $item.attr('href'),
};
});
}) as DataItem[];
}
items = await Promise.all(
items.map((item) =>
cache.tryGet(item.link, async () => {
cache.tryGet(item.link!, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
@@ -89,7 +89,7 @@ async function handler(ctx) {
item.title = content('h1').text();
item.author = content('script')
.text()
.match(/renjian_author = '(.*)'/)[1];
.match(/renjian_author = '(.*)'/)![1];
item.description = content('#endText').html() ?? content('#content').html();
item.pubDate = timezone(parseDate(content('.pub_time').text() ?? content('.post_info').text().split('来源:', 1)[0].trim()), 8);
+1 -1
View File
@@ -32,7 +32,7 @@ const parseDyArticle = (item) =>
}
const url = new URL(i.attribs.src);
if (url.host === 'nimg.ws.126.net') {
i.attribs.src = url.searchParams.get('url');
i.attribs.src = url.searchParams.get('url') ?? '';
}
});
+2 -2
View File
@@ -1,4 +1,4 @@
import type { Route } from '@/types';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import { renderDescription } from './templates/description';
@@ -70,7 +70,7 @@ async function handler(ctx) {
cache.tryGet(`18comic:album:${item.id}`, async () => {
const chapterApiUrl = `${getApiUrl()}/chapter?id=${item.id}`;
const chapterResult = await processApiItems(chapterApiUrl);
const result = {};
const result: DataItem = { title: '' };
const chapterNum = index + 1;
result.title = `${chapterNum}${item.name === '' ? chapterNum : item.name}`;
result.link = `${rootUrl}/photo/${item.id}`;
+6 -6
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -53,13 +53,13 @@ async function handler(ctx) {
let items = $('.title')
.toArray()
.map((item) => {
item = $(item);
.map((item): DataItem & { guid: string } => {
const $item = $(item);
return {
title: item.text(),
link: `${rootUrl}${item.parent().attr('href')}`,
guid: `https://18comic.org${item.parent().attr('href')}`,
title: $item.text(),
link: `${rootUrl}${$item.parent().attr('href')}`,
guid: `https://18comic.org${$item.parent().attr('href')}`,
};
});
+10 -4
View File
@@ -1,4 +1,4 @@
import type { Route } from '@/types';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import { parseDate } from '@/utils/parse-date';
@@ -67,11 +67,17 @@ async function handler(ctx) {
const results = await Promise.all(
filteredItemsByCategory.map((item) =>
cache.tryGet(`18comic:search:${item.id}`, async () => {
const result = { title: item.name, link: `${rootUrl}/album/${item.id}`, guid: `18comic:/album/${item.id}`, updated: parseDate(item.update_at) };
const result: DataItem = {
title: item.name,
link: `${rootUrl}/album/${item.id}`,
guid: `18comic:/album/${item.id}`,
updated: parseDate(item.update_at),
};
const apiUrl = `${getApiUrl()}/album?id=${item.id}`;
const apiResult = await processApiItems(apiUrl);
result.pubDate = new Date(apiResult.addtime * 1000);
result.category = apiResult.tags.map((tag) => tag);
const tags = apiResult.tags.map((tag) => tag);
result.category = tags;
result.author = apiResult.author.map((a) => a).join(', ');
result.description = renderDescription({
introduction: apiResult.description,
@@ -83,7 +89,7 @@ async function handler(ctx) {
// `https://cdn-msp3.${domain}/media/photos/${item.id}/00003.webp`,
],
cover: `https://cdn-msp3.${domain}/media/albums/${item.id}_3x4.jpg`,
category: result.category,
category: tags,
});
return result;
})
+9 -8
View File
@@ -3,6 +3,7 @@ import CryptoJS from 'crypto-js';
import { config } from '@/config';
import ConfigNotFoundError from '@/errors/types/config-not-found';
import type { DataItem } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import md5 from '@/utils/md5';
@@ -99,13 +100,13 @@ const ProcessItems = async (ctx, currentUrl, rootUrl) => {
let items = $('.video-title')
.slice(0, ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 20)
.toArray()
.map((item) => {
item = $(item);
.map((item): DataItem & { guid: string } => {
const $item = $(item);
return {
title: item.text().trim(),
link: `${rootUrl}${item.prev().find('a').attr('href')}`,
guid: `18comic:${item.prev().find('a').attr('href')}`,
title: $item.text().trim(),
link: `${rootUrl}${$item.prev().find('a').attr('href')}`,
guid: `18comic:${$item.prev().find('a').attr('href')}`,
};
});
@@ -116,8 +117,8 @@ const ProcessItems = async (ctx, currentUrl, rootUrl) => {
const content = load(detailResponse.data);
item.pubDate = parseDate(content('div[itemprop="datePublished"]').first().attr('content'));
item.updated = parseDate(content('div[itemprop="datePublished"]').last().attr('content'));
item.pubDate = parseDate(content('div[itemprop="datePublished"]').first().attr('content')!);
item.updated = parseDate(content('div[itemprop="datePublished"]').last().attr('content')!);
item.category = content('span[data-type="tags"]')
.first()
.find('a')
@@ -133,7 +134,7 @@ const ProcessItems = async (ctx, currentUrl, rootUrl) => {
introduction: content('#intro-block .p-t-5').text(),
images: content('.img_zoom_img img')
.toArray()
.map((image) => content(image).attr('data-original')),
.map((image) => content(image).attr('data-original')!),
cover: content('.thumb-overlay img').first().attr('src'),
category: item.category,
});
+7 -7
View File
@@ -4,7 +4,7 @@ import type { Element } from 'domhandler';
import type { Context } from 'hono';
import { renderToString } from 'hono/jsx/dom/server';
import type { Data, DataItem, Route } from '@/types';
import type { Data, DataItem, Language, Route } from '@/types';
import { ViewType } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
@@ -24,7 +24,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
let items: DataItem[] = $('article.newsplus')
.slice(0, limit)
.toArray()
.map((el): Element => {
.map((el) => {
const $el: Cheerio<Element> = $(el);
const title: string = $el.find('h2.entry-title').text();
@@ -43,7 +43,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
image,
banner: image,
updated: upDatedStr ? parseDate(upDatedStr) : undefined,
language,
language: language as Language,
};
return processedItem;
@@ -57,7 +57,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
}
return cache.tryGet(item.link, async (): Promise<DataItem> => {
const detailResponse = await ofetch(item.link);
const detailResponse = await ofetch(item.link!);
const $$: CheerioAPI = load(detailResponse);
$$('div.entry-content img.alignnone').each((_, el) => {
@@ -85,7 +85,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
pubDate: pubDateStr ? parseDate(pubDateStr) : item.pubDate,
category: categories,
updated: upDatedStr ? parseDate(upDatedStr) : item.updated,
language,
language: language as Language,
};
const extraLinkEls: Element[] = $$('ul.related_post li a').toArray();
@@ -112,7 +112,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
$$('ul.related_post').parent().remove();
const description: string | undefined = $$('div.entry-content').html();
const description: string | undefined = $$('div.entry-content').html() ?? undefined;
processedItem = {
...processedItem,
@@ -142,7 +142,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
allowEmpty: true,
image: $('h3.site-title img').attr('src'),
author: title.split(/-/).pop()?.trim(),
language,
language: language as Language,
id: targetUrl,
};
};
+8 -8
View File
@@ -2,7 +2,7 @@ import { load } from 'cheerio';
import iconv from 'iconv-lite';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Route } from '@/types';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -10,12 +10,12 @@ import timezone from '@/utils/timezone';
import { isValidHost } from '@/utils/valid-host';
const setCookie = function (cookieName, cookieValue, seconds, path, domain, secure?) {
let expires = null;
let expires: Date | null = null;
if (seconds !== -1) {
expires = new Date();
expires.setTime(expires.getTime() + seconds);
}
return [encodeURI(cookieName), '=', encodeURI(cookieValue), expires ? '; expires=' + expires.toGMTString() : '', path ? '; path=' + path : '/', domain ? '; domain=' + domain : '', secure ? '; secure' : ''].join('');
return [encodeURI(cookieName), '=', encodeURI(cookieValue), expires ? '; expires=' + expires.toUTCString() : '', path ? '; path=' + path : '/', domain ? '; domain=' + domain : '', secure ? '; secure' : ''].join('');
};
export const route: Route = {
@@ -72,12 +72,12 @@ async function handler(ctx) {
let items = $('.center-center-jiazi')
.find('a[title]')
.toArray()
.map((item) => {
item = $(item);
.map((item): DataItem & { link: string } => {
const $item = $(item);
return {
title: item.attr('title'),
link: `https:${item.attr('href')}`,
title: $item.attr('title')!,
link: `https:${$item.attr('href')}`,
};
});
@@ -100,7 +100,7 @@ async function handler(ctx) {
item.author = content('.uname, .user-name').first().text();
item.description = content('.post-cont').first().html() || content('.thread-cont').html();
item.pubDate = timezone(parseDate(content('.cont-top-left meta').attr('content')), 8);
item.pubDate = timezone(parseDate(content('.cont-top-left meta').attr('content')!), 8);
return item;
})
+18 -18
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { DataItem, Language, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -13,7 +13,7 @@ export const handler = async (ctx) => {
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 50;
const queryString = Object.entries(ctx.req.query())
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value as string)}`)
.join('&');
const currentUrl = new URL(`${params && params.endsWith('.htm') ? params : `${params}.htm`}${queryString ? `?${queryString}` : ''}`, rootUrl).href;
@@ -27,24 +27,24 @@ export const handler = async (ctx) => {
let items = $('li.media.thread.tap:not(li.hidden-sm)')
.slice(0, limit)
.toArray()
.map((item) => {
item = $(item);
.map((item): DataItem & { link: string } => {
const $item = $(item);
const subjectEl = item.find('div.subject').children('a').first();
const subjectEl = $item.find('div.subject').children('a').first();
return {
title: subjectEl.text(),
pubDate: timezone(parseDate(item.find('span.date').text()), 8),
link: new URL(subjectEl.prop('href'), rootUrl).href,
pubDate: timezone(parseDate($item.find('span.date').text()), 8),
link: new URL(subjectEl.prop('href')!, rootUrl).href,
category: [
item.find('a.text-secondary').text().replaceAll('[]', ''),
...item
$item.find('a.text-secondary').text().replaceAll('[]', ''),
...$item
.find('a.badge')
.toArray()
.map((c) => $(c).text()),
].filter(Boolean),
author: item.find('a.username').text(),
language,
author: $item.find('a.username').text(),
language: language as Language,
};
});
@@ -59,7 +59,7 @@ export const handler = async (ctx) => {
if (title) {
const description = $$('div.message.break-all').html();
const image = new URL($$('img').first().prop('src'), rootUrl).href;
const image = new URL($$('img').first().prop('src')!, rootUrl).href;
item.title = title;
item.description = description;
@@ -73,14 +73,14 @@ export const handler = async (ctx) => {
};
item.image = image;
item.banner = image;
item.language = language;
item.language = language as Language;
const torrents = $$('ul.attachlist li a');
if (torrents.length > 0) {
const torrent = torrents.first();
item.enclosure_url = new URL(torrent.prop('href'), rootUrl).href;
item.enclosure_url = new URL(torrent.prop('href')!, rootUrl).href;
item.enclosure_type = 'application/x-bittorrent';
item.enclosure_title = torrent.text();
}
@@ -92,7 +92,7 @@ export const handler = async (ctx) => {
);
const author = 'BT 之家 1LOU 站';
const image = new URL($('img.logo-2').prop('src'), rootUrl).href;
const image = new URL($('img.logo-2').prop('src')!, rootUrl).href;
return {
title: `${$('title').text().split(/-/, 1)[0]} - ${author}`,
@@ -102,7 +102,7 @@ export const handler = async (ctx) => {
allowEmpty: true,
image,
author,
language,
language: language as Language,
};
};
@@ -138,9 +138,9 @@ export const route: Route = {
{
source: ['1lou.me/:params'],
target: (_, url) => {
url = new URL(url);
const parsedUrl = new URL(url);
return `/1lou${url.href.replace(rootUrl, '')}`;
return `/1lou${parsedUrl.href.replace(rootUrl, '')}`;
},
},
],
+9 -9
View File
@@ -2,7 +2,7 @@ import { load } from 'cheerio';
import { raw } from 'hono/html';
import { renderToString } from 'hono/jsx/dom/server';
import type { Route } from '@/types';
import type { Language, Route } from '@/types';
import got from '@/utils/got';
export const handler = async (ctx) => {
@@ -27,11 +27,11 @@ export const handler = async (ctx) => {
.slice(0, limit)
.toArray()
.map((item) => {
item = $(item);
const $item = $(item);
const title = item.find('span.photos-feed-data-title').first().text() || 'Untitled';
const image = item.find('img').prop('src');
const author = item.find('span.photos-feed-data-name').first().text();
const title = $item.find('span.photos-feed-data-title').first().text() || 'Untitled';
const image = $item.find('img').prop('src');
const author = $item.find('span.photos-feed-data-name').first().text();
const text = `${title} by ${author}`;
@@ -46,7 +46,7 @@ export const handler = async (ctx) => {
</>
);
const id = item.find('img[id]').prop('id').split(/-/).pop();
const id = $item.find('img[id]').prop('id').split(/-/).pop();
const guid = `1x-${id}`;
return {
@@ -62,14 +62,14 @@ export const handler = async (ctx) => {
},
image,
banner: image,
language,
language: language as Language,
enclosure_url: image,
enclosure_type: image ? `image/${image.split(/\./).pop()}` : undefined,
enclosure_title: title,
};
});
const image = new URL($('img.themedlogo').prop('src'), rootUrl).href;
const image = new URL($('img.themedlogo').prop('src')!, rootUrl).href;
return {
title: $('title').text(),
@@ -79,7 +79,7 @@ export const handler = async (ctx) => {
allowEmpty: true,
image,
author: $('meta[property="og:site_name"]').prop('content'),
language,
language: language as Language,
};
};
+8 -8
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
import { parseDate } from '@/utils/parse-date';
@@ -71,7 +71,7 @@ async function handler(ctx) {
const onclickValue = $('.button').first().attr('onclick');
const targetUrl = onclickValue?.match(/window\.open\('([^']+)'/)?.[1];
return { url: new URL(targetUrl, 'https://2048.info').href };
return { url: new URL(targetUrl!, 'https://2048.info').href };
});
// 获取重定向后的url
const redirectResponse = await ofetch.raw(domainInfo.url);
@@ -109,13 +109,13 @@ async function handler(ctx) {
.last()
.nextAll('.tr3')
.toArray()
.map((item) => {
item = $(item).find('a.subject');
.map((item): DataItem & { link: string; guid: string } => {
const $item = $(item).find('a.subject');
return {
title: item.text(),
link: `${currentHost}/${item.attr('href')}`,
guid: `${rootUrl}/2048/${item.attr('href')}`,
title: $item.text(),
link: `${currentHost}/${$item.attr('href')}`,
guid: `${rootUrl}/2048/${$item.attr('href')}`,
};
})
.filter((item) => !item.link.includes('undefined'));
@@ -143,7 +143,7 @@ async function handler(ctx) {
});
item.author = content('.fl.black').first().text();
item.pubDate = timezone(parseDate(content('span.fl.gray').first().attr('title')), 8);
item.pubDate = timezone(parseDate(content('span.fl.gray').first().attr('title')!), 8);
const readTpc = content('#read_tpc').first();
const copyLink = content('#copytext')?.first()?.text();
+6 -6
View File
@@ -3,7 +3,7 @@ import { load } from 'cheerio';
import type { Context } from 'hono';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Data, DataItem, Route } from '@/types';
import type { Data, DataItem, Language, Route } from '@/types';
import { ViewType } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
@@ -110,7 +110,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
image,
banner: image,
updated: updated ? parseDate(updated, 'X') : undefined,
language,
language: language as Language,
};
return processedItem;
@@ -124,7 +124,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
}
return cache.tryGet(item.link, async (): Promise<DataItem> => {
const detailResponse = await ofetch(item.link);
const detailResponse = await ofetch(item.link!);
const $$: CheerioAPI = load(detailResponse);
$$('div.rela-box').remove();
@@ -135,7 +135,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
const pubDateStr: string | undefined = $$('div.author-infos span')
.text()
.match(/(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2})/)?.[1];
const categories: string[] = $$('meta[name="keywords"]').attr('content')?.split(/,/) ?? item.category ?? [];
const categories: DataItem['category'] = $$('meta[name="keywords"]').attr('content')?.split(/,/) ?? item.category ?? [];
const upDatedStr: string | undefined = pubDateStr;
const processedItem: DataItem = {
@@ -148,7 +148,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
text: description,
},
updated: upDatedStr ? timezone(parseDate(upDatedStr), 8) : item.updated,
language,
language: language as Language,
};
return {
@@ -169,7 +169,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
item: items,
allowEmpty: true,
author,
language,
language: language as Language,
id: targetUrl,
};
};
+7 -7
View File
@@ -1,7 +1,7 @@
import { load } from 'cheerio';
import iconv from 'iconv-lite';
import type { Route } from '@/types';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -45,10 +45,10 @@ async function handler(ctx) {
const list = $('tbody[id^="normalthread_"]')
.toArray()
.map((item) => {
item = $(item);
const xst = item.find('a.s.xst');
const author = item.find('td.by cite a').eq(0).text();
.map((item): DataItem => {
const $item = $(item);
const xst = $item.find('a.s.xst');
const author = $item.find('td.by cite a').eq(0).text();
return {
title: xst.text(),
link: xst.attr('href'),
@@ -58,7 +58,7 @@ async function handler(ctx) {
// console.log(list);
const items = await Promise.all(
list.map((item) =>
cache.tryGet(item.link, async () => {
cache.tryGet(item.link!, async () => {
const detailResponse = await got(item.link, {
responseType: 'buffer',
});
@@ -67,7 +67,7 @@ async function handler(ctx) {
const first_post = content('td[id^="postmessage_"]').first();
const dateobj = content('em[id^="authorposton"]').first();
item.description = first_post.html();
item.pubDate = timezone(parseDate(dateobj.find('span').attr('title'), 'YYYY-M-D HH:mm:ss'), 8);
item.pubDate = timezone(parseDate(dateobj.find('span').attr('title')!, 'YYYY-M-D HH:mm:ss'), 8);
return item;
})
+1 -1
View File
@@ -42,7 +42,7 @@ export const getWafTokenId = () =>
const payload = $('script')
.text()
.match(/atob\('(.*?)'\)\),/)?.[1];
const response = solveWafChallenge(payload);
const response = solveWafChallenge(payload!);
const tokenIdResponse = await ofetch.raw(rootUrl, {
headers: {
+4 -4
View File
@@ -33,12 +33,12 @@ async function handler(ctx) {
const listSelector = type === 'resource' ? $('.ZQ_Left .Llis_4 .lis li, .zq_left .rigtbox7 li').toArray() : $('.ZQ_Left .lis, .zq_left .newsleft li').toArray();
const list = listSelector.map((i) => {
i = $(i);
const a = i.find('a[href]').last();
const time = i.find('.time');
const $i = $(i);
const a = $i.find('a[href]').last();
const time = $i.find('.time');
return {
title: a.text(),
description: i.find('.miaoshu').text(),
description: $i.find('.miaoshu').text(),
link: a.attr('href'),
pubDate: time.length ? parseDate(time.text().trim()) : null, // 2020-12-31
};
+8 -8
View File
@@ -42,21 +42,21 @@ async function handler(ctx) {
const list = $(isArcPost ? '.selectarcpost' : '.selectpost')
.toArray()
.map((item) => {
item = $(item);
const $item = $(item);
if (isArcPost) {
return {
title: item.find('.bt').text(),
link: item.attr('href'),
description: item.find('p').text(),
pubDate: timezone(parseDate(item.find('.time').text()), 8),
title: $item.find('.bt').text(),
link: $item.attr('href'),
description: $item.find('p').text(),
pubDate: timezone(parseDate($item.find('.time').text()), 8),
};
}
const a = item.find('.text a');
const a = $item.find('.text a');
return {
title: a.first().text(),
link: a.attr('href'),
description: item.find('.miaoshu').text(),
pubDate: timezone(parseDate(item.find('.time').text()), 8),
description: $item.find('.miaoshu').text(),
pubDate: timezone(parseDate($item.find('.time').text()), 8),
};
});
+2 -2
View File
@@ -12,9 +12,9 @@ const parseArticle = (item) =>
if (item.link.startsWith('https://dl.3dmgame.com/')) {
const lis = $('.patchtop .lis');
const [, category, pubDate, author] = lis.text().match(/([^\n]*)\n.*([^\n]*)\n.*([^\n]*)\n/s);
const [, category, pubDate, author] = lis.text().match(/([^\n]*)\n.*([^\n]*)\n.*([^\n]*)\n/s)!;
item.description = lis.html() + $('.L_title').html() + $('.GmL_1').html();
item.description = lis.html()! + $('.L_title').html()! + $('.GmL_1').html();
item.category = category;
item.pubDate = timezone(parseDate(pubDate), 8);
item.author = author;
+18 -18
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { Language, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -24,19 +24,19 @@ export const handler = async (ctx) => {
let items = $('ul.excerpt li')
.toArray()
.filter((item) => {
item = $(item);
const $item = $(item);
const link = item.find('h2 a').prop('href');
const isAdItem = item.find('span.cat').text().includes('423Down');
const link = $item.find('h2 a').prop('href');
const isAdItem = $item.find('span.cat').text().includes('423Down');
return new RegExp(domain).test(link) && !isAdItem;
return new RegExp(domain).test(link!) && !isAdItem;
})
.slice(0, limit)
.map((item) => {
item = $(item);
const $item = $(item);
const title = item.find('h2').text();
const image = item.find('a.pic img').prop('src');
const title = $item.find('h2').text();
const image = $item.find('a.pic img').prop('src');
const description = renderDescription({
images: image
? [
@@ -46,25 +46,25 @@ export const handler = async (ctx) => {
},
]
: undefined,
intro: item.find('div.note').text(),
intro: $item.find('div.note').text(),
});
return {
title,
description,
pubDate: parseDate(item.find('span.time').text(), 'MM-DD'),
link: item.find('h2 a').prop('href'),
category: item
pubDate: parseDate($item.find('span.time').text(), 'MM-DD'),
link: $item.find('h2 a').prop('href'),
category: $item
.find('span.cat a')
.toArray()
.map((c) => $(c).text()),
content: {
html: description,
text: item.find('div.note').text(),
text: $item.find('div.note').text(),
},
image,
banner: image,
language,
language: language as Language,
enclosure_url: image,
enclosure_type: image ? `image/${image.split(/\./).pop()}` : undefined,
enclosure_title: title,
@@ -73,13 +73,13 @@ export const handler = async (ctx) => {
items = await Promise.all(
items.map((item) =>
cache.tryGet(item.link, async () => {
cache.tryGet(item.link!, async () => {
const { data: detailResponse } = await got(item.link);
const $$ = load(detailResponse);
const title = $$('h1.meta-tit a').text();
const description = item.description + renderDescription({ description: $$('div.entry').html() });
const description = item.description + renderDescription({ description: $$('div.entry').html() ?? undefined });
item.title = title;
item.description = description;
@@ -91,7 +91,7 @@ export const handler = async (ctx) => {
html: description,
text: $$('div.entry').text(),
};
item.language = language;
item.language = language as Language;
return item;
})
@@ -109,7 +109,7 @@ export const handler = async (ctx) => {
allowEmpty: true,
image,
author: title.split(/-/).pop()?.trim(),
language,
language: language as Language,
};
};
+21 -20
View File
@@ -2,7 +2,7 @@ import { load } from 'cheerio';
import { raw } from 'hono/html';
import { renderToString } from 'hono/jsx/dom/server';
import type { Route } from '@/types';
import type { DataItem, Language, Route } from '@/types';
import cache from '@/utils/cache';
import md5 from '@/utils/md5';
import ofetch from '@/utils/ofetch';
@@ -98,11 +98,12 @@ async function handler(ctx) {
let items = $('div.nex_cmo_piv a')
.slice(0, limit)
.toArray()
.map((item) => {
item = $(item);
.map((item): DataItem & { link: string } => {
const $item = $(item);
return {
link: new URL(item.prop('href'), rootUrl).href,
link: new URL($item.prop('href')!, rootUrl).href,
title: '',
};
});
@@ -145,28 +146,28 @@ async function handler(ctx) {
$$('div.nex_drama_intros em').first().remove();
$$('strong font').each((_, el) => {
el = $$(el);
const $el = $$(el);
el.parent().remove();
$el.parent().remove();
});
const title = $$('div.nex_drama_Top h5').text();
const description = $$('div.nex_drama_intros').html();
const picture =
$$('div.nex_drama_pic')
.html()
.html()!
.match(/background:url\((.*?)\)/)?.[1] ?? '';
const details = $$('li.nex_drama_Detail_li, li.nex_drama_Detail_lis dd')
.toArray()
.map((li) => {
li = $$(li);
const $li = $$(li);
const key = li
const key = $li
.find('em')
.text()
.replaceAll(/|\s/g, '');
const value = li.find('span').length === 0 ? li.contents().last().text().trim() : li.find('span').text().trim();
const value = $li.find('span').length === 0 ? $li.contents().last().text().trim() : $li.find('span').text().trim();
return { [key]: value };
});
@@ -177,10 +178,10 @@ async function handler(ctx) {
? $$('td.t_f strong')
.toArray()
.map((l) => {
l = $$(l);
const $l = $$(l);
const title = l.contents().first().text();
const link = l.next().prop('href') ?? l.nextUntil('a').next().prop('href');
const title = $l.contents().first().text();
const link = $l.next().prop('href') ?? $l.nextUntil('a').next().prop('href');
item.enclosure_url ??= link;
item.enclosure_type ??= 'application/x-bittorrent';
@@ -188,7 +189,7 @@ async function handler(ctx) {
return {
title,
tags: l
tags: $l
.contents()
.last()
.text()
@@ -199,15 +200,15 @@ async function handler(ctx) {
: $$('div.newfujian')
.toArray()
.map((l) => {
l = $$(l);
const $l = $$(l);
return {
title: l.find('p.filename').prop('title') || l.find('p.filename').text(),
tags: l
title: $l.find('p.filename').prop('title') || $l.find('p.filename').text(),
tags: $l
.find('div.fileaq')
.text()
.match(/【(.*?)】/g),
link: l.find('div.down_2 a').prop('href'),
link: $l.find('div.down_2 a').prop('href'),
};
});
@@ -238,7 +239,7 @@ async function handler(ctx) {
links,
});
item.pubDate = timezone(parseDate(pubDate, 'YYYY-M-D HH:mm:ss'), 8);
item.category = Object.values(mergedDetails)
item.category = Object.values<string>(mergedDetails)
.flatMap((c) => c.split(/\s/))
.filter(Boolean);
item.author = mergedDetails['导演'];
@@ -268,6 +269,6 @@ async function handler(ctx) {
allowEmpty: true,
image,
author: $('meta[name="application-name"]').prop('content'),
language,
language: language as Language,
};
}
+9 -9
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -34,21 +34,21 @@ async function handler() {
});
const data = response.data;
if (!data) {
return;
return null;
}
const $ = load(data);
let out = $('div.container div.list_list.mtop10 ul li')
.find('a')
.toArray()
.map((item) => {
item = $(item);
const link = rootUrl + item.attr('href');
.map((item): DataItem & { link: string } => {
const $item = $(item);
const link = rootUrl + $item.attr('href');
const reg = /^(.+) - (.*) - (.+)$/;
const keyword = reg.exec(item.text().trim());
const keyword = reg.exec($item.text().trim());
return {
title: keyword[1],
author: keyword[2],
pubDate: timezone(parseDate(keyword[3], 'YYYY-MM-DD'), 8),
title: keyword![1],
author: keyword![2],
pubDate: timezone(parseDate(keyword![3], 'YYYY-MM-DD'), 8),
link,
};
});
+2 -1
View File
@@ -6,6 +6,7 @@ import got from '@/utils/got';
import logger from '@/utils/logger';
import ofetch from '@/utils/ofetch';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
import { getToken, sign } from './utils';
@@ -53,7 +54,7 @@ async function getFullcontent(item, cookie = '') {
return {
title: item.title,
link: item.url,
pubDate: parseDate(item.pubdate, 8),
pubDate: timezone(parseDate(item.pubdate), 8),
description: fullContent || item.abstract, // Return item.abstract if fullContent is null
};
}
+2 -2
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { DataItem, Route } from '@/types';
import type { DataItem, Language, Route } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
@@ -57,7 +57,7 @@ async function handler(ctx) {
item,
image: $book('.bi-img img').attr('src'),
author: $book('.bi-wt a').text(),
language: 'zh-cn',
language: 'zh-CN' as Language,
};
}
+19 -18
View File
@@ -2,6 +2,7 @@ import { load } from 'cheerio';
import { renderToString } from 'hono/jsx/dom/server';
import iconv from 'iconv-lite';
import type { DataItem, Language } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -18,21 +19,21 @@ const fetchItems = async (limit, currentUrl) => {
let items = $('p.line')
.toArray()
.map((item) => {
item = $(item);
.map((item): DataItem => {
const $item = $(item);
const a = item.find('a');
const a = $item.find('a');
return {
title: a.text(),
link: new URL(a.prop('href'), rootUrl).href,
author: item.find('span').last().text(),
link: new URL(a.prop('href')!, rootUrl).href,
author: $item.find('span').last().text(),
};
});
items = await Promise.all(
items.map((item) =>
cache.tryGet(item.link, async () => {
cache.tryGet(item.link!, async () => {
try {
const { data: detailResponse } = await got(item.link, {
responseType: 'buffer',
@@ -42,16 +43,16 @@ const fetchItems = async (limit, currentUrl) => {
const details = content('div.mohe-content p')
.toArray()
.map((detail) => {
detail = content(detail);
const as = detail.find('a');
.map((detail): { label: string; value: any } => {
const $detail = content(detail);
const as = $detail.find('a');
return {
label: detail.find('span.c-l-depths').text().split(//, 1)[0],
label: $detail.find('span.c-l-depths').text().split(//, 1)[0],
value:
as.length === 0
? content(
detail
$detail
.contents()
.toArray()
.find((c) => c.nodeType === 3)
@@ -59,27 +60,27 @@ const fetchItems = async (limit, currentUrl) => {
.text()
.trim()
: {
href: new URL(as.first().prop('href'), rootUrl).href,
href: new URL(as.first().prop('href')!, rootUrl).href,
text: as.first().text().trim(),
},
};
});
const pubDate = details.find((detail) => detail.label === '更新').value;
const pubDate = details.find((detail) => detail.label === '更新')!.value;
item.title = content('h1').contents().first().text();
item.description = renderDescription({
images: [
{
src: new URL(content('a.mohe-imgs img').prop('src'), rootUrl).href,
src: new URL(content('a.mohe-imgs img').prop('src')!, rootUrl).href,
alt: item.title,
},
],
details,
});
item.author = details.find((detail) => detail.label === '作者').value;
item.category = [details.find((detail) => detail.label === '状态').value, details.find((detail) => detail.label === '类型').value.text].filter(Boolean);
item.guid = `56kog-${item.link.match(/\/(\d+)\.html$/)[1]}#${pubDate}`;
item.author = details.find((detail) => detail.label === '作者')!.value;
item.category = [details.find((detail) => detail.label === '状态')!.value, details.find((detail) => detail.label === '类型')!.value.text].filter(Boolean);
item.guid = `56kog-${item.link!.match(/\/(\d+)\.html$/)![1]}#${pubDate}`;
item.pubDate = timezone(parseDate(pubDate), 8);
} catch {
// no-empty
@@ -97,7 +98,7 @@ const fetchItems = async (limit, currentUrl) => {
title: $('title').text(),
link: currentUrl,
description: $('meta[name="description"]').prop('content'),
language: $('html').prop('lang'),
language: $('html').prop('lang') as Language,
icon,
logo: icon,
subtitle: $('meta[name="keywords"]').prop('content'),
+9 -7
View File
@@ -24,7 +24,7 @@ function appendRentalAPIParams(urlString) {
}
async function getToken() {
const html = await client('https://rent.591.com.tw').text();
const html = await (client('https://rent.591.com.tw') as any).text();
const $ = load(html);
const csrfToken = $('meta[name="csrf-token"]').attr('content');
@@ -39,12 +39,14 @@ async function getToken() {
async function getHouseList(houseListURL) {
const csrfToken = await getToken();
const data = await client({
url: houseListURL,
headers: {
'X-CSRF-TOKEN': csrfToken,
},
}).json();
const data = await (
client({
url: houseListURL,
headers: {
'X-CSRF-TOKEN': csrfToken,
},
}) as any
).json();
const {
data: { data: houseList },
+2 -2
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { Language, Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -84,6 +84,6 @@ async function handler(ctx) {
title: '五大唱片 - 新货上架',
link: url,
item: items,
language: 'zh-tw',
language: 'zh-TW' as Language,
};
}
+1 -1
View File
@@ -45,7 +45,7 @@ export const route: Route = {
item,
image: $('.bookimg2>img').attr('src'),
author: $('.booknav2>p:first-of-type>a').text(),
language: 'zh-cn',
language: 'zh-CN',
};
},
};
+7 -6
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -45,19 +45,20 @@ async function handler(ctx) {
let items = $('#d_list ul li, #thread_list li, .t_l .t_subject')
.toArray()
.slice(0, limit)
.map((item) => {
item = $(item);
.map((item): DataItem => {
const $item = $(item);
const a = item.find('a').first();
const a = $item.find('a').first();
return {
link: `${rootUrl}/${id}/${a.attr('href')}`,
title: '',
};
});
items = await Promise.all(
items.map((item) =>
cache.tryGet(item.link, async () => {
cache.tryGet(item.link!, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
@@ -69,7 +70,7 @@ async function handler(ctx) {
item.author = detailResponse.data.match(/送交者:[^>]*>([^<]*)<\/a>/)[1].trim();
item.pubDate = timezone(parseDate(detailResponse.data.match(/于 (.*) 已读/)[1], 'YYYY-MM-DD h:m'), 8);
item.description = content('pre')
.html()
.html()!
.replaceAll('<p></p>', '')
.replaceAll(/<font color="#E6E6DD">6park.com<\/font>/g, '');
+8 -8
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -58,23 +58,23 @@ async function handler(ctx) {
let items = $('#d_list ul li, #thread_list li, .t_l .t_subject')
.toArray()
.slice(0, limit)
.map((item) => {
item = $(item);
.map((item): DataItem => {
const $item = $(item);
const a = item.find('a').first();
const a = $item.find('a').first();
const link = a.attr('href');
return {
title: a.text(),
link: link.startsWith('http') ? link : `${rootUrl}/${link.startsWith('view') ? `newspark/${link}` : link}`,
link: link!.startsWith('http') ? link : `${rootUrl}/${link!.startsWith('view') ? `newspark/${link}` : link}`,
};
});
items = await Promise.all(
items
.filter((item) => /6parknews\.com/.test(item.link))
.filter((item) => /6parknews\.com/.test(item.link!))
.map((item) =>
cache.tryGet(item.link, async () => {
cache.tryGet(item.link!, async () => {
try {
const detailResponse = await got({
method: 'get',
@@ -88,7 +88,7 @@ async function handler(ctx) {
item.title = content('h2').text();
item.author = matches[1].trim();
item.pubDate = timezone(parseDate(matches[2], 'YYYY-MM-DD h:m'), 8);
item.description = content('#shownewsc').html().replaceAll('<p></p>', '');
item.description = content('#shownewsc').html()!.replaceAll('<p></p>', '');
} catch {
// no-empty
}
+6 -6
View File
@@ -4,7 +4,7 @@ import type { Element } from 'domhandler';
import type { Context } from 'hono';
import iconv from 'iconv-lite';
import type { Data, DataItem, Route } from '@/types';
import type { Data, DataItem, Language, Route } from '@/types';
import { ViewType } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
@@ -28,7 +28,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
let items: DataItem[] = $('ul.list li')
.slice(0, limit)
.toArray()
.map((el): Element => {
.map((el) => {
const $el: Cheerio<Element> = $(el);
const title: string = $el.find('a').text();
@@ -47,7 +47,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
guid,
id: guid,
updated: upDatedStr ? parseDate(upDatedStr, ['MM-DD', 'YYYY-MM-DD']) : undefined,
language,
language: language as Language,
};
return processedItem;
@@ -60,7 +60,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
}
return cache.tryGet(item.link, async (): Promise<DataItem> => {
const detailResponse = await ofetch(item.link, {
const detailResponse = await ofetch(item.link!, {
responseType: 'arrayBuffer',
});
const $$: CheerioAPI = load(iconv.decode(Buffer.from(detailResponse), encoding));
@@ -92,7 +92,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
image,
banner: image,
updated: upDatedStr ? parseDate(upDatedStr) : item.updated,
language,
language: language as Language,
};
const $enclosureEl: Cheerio<Element> = $$('td a[href^="magnet"]').last();
@@ -125,7 +125,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
item: items,
allowEmpty: true,
image: new URL('images/logo.gif', baseUrl).href,
language,
language: language as Language,
id: targetUrl,
};
};
+19 -19
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { DataItem, Language, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -24,12 +24,12 @@ export const handler = async (ctx) => {
let items = $('section.box-content div.card a.card-title')
.slice(0, limit)
.toArray()
.map((item) => {
item = $(item).parent();
.map((item): DataItem => {
const $item = $(item).parent();
const title = item.find('a.card-title').text();
const title = $item.find('a.card-title').text();
const src = item.find('a.card-image img').prop('data-src');
const src = $item.find('a.card-image img').prop('data-src');
const image = src?.startsWith('//') ? `https:${src}` : src;
const description = renderDescription({
@@ -42,9 +42,9 @@ export const handler = async (ctx) => {
]
: undefined,
});
const pubDate = item.find('div.card-info span.item').last().text();
const pubDate = $item.find('div.card-info span.item').last().text();
const href = item.find('a.card-title').prop('href');
const href = $item.find('a.card-title').prop('href');
return {
title,
@@ -53,22 +53,22 @@ export const handler = async (ctx) => {
link: href?.startsWith('//') ? `https:${href}` : href,
category: [
...new Set([
...item
...$item
.find('span.tag-title')
.toArray()
.map((c) => $(c).text()),
item.find('div.card-info span.item').first().text(),
$item.find('div.card-info span.item').first().text(),
]),
].filter(Boolean),
image,
banner: image,
language,
language: language as Language,
};
});
items = await Promise.all(
items.map((item) =>
cache.tryGet(item.link, async () => {
cache.tryGet(item.link!, async () => {
const { data: detailResponse } = await got(item.link);
const $$ = load(detailResponse);
@@ -76,18 +76,18 @@ export const handler = async (ctx) => {
$$('i.p-status').remove();
$$('div.image-text-content p img.lazy').each((_, el) => {
el = $$(el);
const $el = $$(el);
const src = el.prop('data-src');
const src = $el.prop('data-src');
const image = src?.startsWith('//') ? `https:${src}` : src;
el.parent().replaceWith(
$el.parent().replaceWith(
renderDescription({
images: image
? [
{
src: image,
alt: el.prop('title') ?? '',
alt: $el.prop('title') ?? '',
},
]
: undefined,
@@ -104,13 +104,13 @@ export const handler = async (ctx) => {
item.title = title;
item.description = description;
item.pubDate = timezone(parseDate($$('p.push-time').text().split(//).pop()), 8);
item.pubDate = timezone(parseDate($$('p.push-time').text().split(//).pop()!), 8);
item.author = $$('a.push-username').contents().first().text();
item.content = {
html: description,
text: $$('div.image-text-content').first().text(),
};
item.language = language;
item.language = language as Language;
return item;
})
@@ -118,7 +118,7 @@ export const handler = async (ctx) => {
);
const title = $('title').text();
const image = new URL($('a.logo img').prop('src'), rootUrl).href;
const image = new URL($('a.logo img').prop('src')!, rootUrl).href;
return {
title: `${title} | ${$('div.actived').text()}`,
@@ -128,7 +128,7 @@ export const handler = async (ctx) => {
allowEmpty: true,
image,
author: $('meta[property="og:site_name"]').prop('content'),
language,
language: language as Language,
};
};
+9 -9
View File
@@ -2,7 +2,7 @@ import { load } from 'cheerio';
import { raw } from 'hono/html';
import { renderToString } from 'hono/jsx/dom/server';
import type { Route } from '@/types';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -64,23 +64,23 @@ async function handler(ctx) {
let items = $('.video')
.toArray()
.map((item) => {
item = $(item);
.map((item): DataItem & { poster?: string; video?: string } => {
const $item = $(item);
const title = item.find('.video-title a');
const title = $item.find('.video-title a');
return {
title: title.text(),
author: item.find('.video-channel').text(),
pubDate: parseDate(item.find('.small').text()),
author: $item.find('.video-channel').text(),
pubDate: parseDate($item.find('.small').text()),
link: title.attr('href'),
poster: item.find('img').attr('data-src'),
video: item.find('video').attr('data-src'),
poster: $item.find('img').attr('data-src'),
video: $item.find('video').attr('data-src'),
};
});
items = await Promise.all(
items.map((item) =>
cache.tryGet(item.link, async () => {
cache.tryGet(item.link!, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
+10 -10
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { DataItem, Language, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -22,20 +22,20 @@ export const handler = async (ctx) => {
let items = $('div.left-news ul li')
.slice(0, limit)
.toArray()
.map((item) => {
item = $(item);
.map((item): DataItem => {
const $item = $(item);
return {
title: item.find('a').text(),
pubDate: timezone(parseDate(item.find('span').text()), 8),
link: item.find('a').prop('href'),
language,
title: $item.find('a').text(),
pubDate: timezone(parseDate($item.find('span').text()), 8),
link: $item.find('a').prop('href'),
language: language as Language,
};
});
items = await Promise.all(
items.map((item) =>
cache.tryGet(item.link, async () => {
cache.tryGet(item.link!, async () => {
const { data: detailResponse } = await got(item.link);
const $$ = load(detailResponse);
@@ -50,7 +50,7 @@ export const handler = async (ctx) => {
html: description,
text: $$('div.txt').text(),
};
item.language = language;
item.language = language as Language;
return item;
})
@@ -68,7 +68,7 @@ export const handler = async (ctx) => {
allowEmpty: true,
image,
author: title.split(/-/).pop()?.trim(),
language,
language: language as Language,
};
};
+9 -9
View File
@@ -2,7 +2,7 @@ import { load } from 'cheerio';
import { renderToString } from 'hono/jsx/dom/server';
import iconv from 'iconv-lite';
import type { Route } from '@/types';
import type { DataItem, Language, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -103,20 +103,20 @@ async function handler(ctx) {
.find('a')
.slice(0, limit)
.toArray()
.map((item) => {
item = $(item);
.map((item): DataItem => {
const $item = $(item);
const link = item.prop('href');
const link = $item.prop('href');
return {
title: item.text(),
link: link.startsWith('http') ? link : new URL(link, rootUrl).href,
title: $item.text(),
link: link!.startsWith('http') ? link : new URL(link!, rootUrl).href,
};
});
items = await Promise.all(
items.map((item) =>
cache.tryGet(item.link, async () => {
cache.tryGet(item.link!, async () => {
const { data: detailResponse } = await got(item.link, {
responseType: 'buffer',
});
@@ -127,7 +127,7 @@ async function handler(ctx) {
content('i.pstatus').remove();
content('div.crly').remove();
const pubDate = content('span.pub-time').text() || content('span.fby span').first().prop('title') || content('span.fby').first().text().split('发表于').pop().trim();
const pubDate = content('span.pub-time').text() || content('span.fby span').first().prop('title') || content('span.fby').first().text().split('发表于').pop()!.trim();
content('img').each((_, el) => {
content(el).replaceWith(
@@ -160,7 +160,7 @@ async function handler(ctx) {
title: `${$('span.country, h2').text()} - ${description.split(',').pop()}`,
link: currentUrl,
description,
language: 'zh-cn',
language: 'zh-CN' as Language,
icon,
logo: icon,
subtitle: $('meta[name="keywords"]').prop('content').trim(),
+9 -9
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import { getSubPath } from '@/utils/common-utils';
import got from '@/utils/got';
@@ -41,18 +41,18 @@ export async function handler(ctx) {
let items = $('div[data-column="Two-Third"] .article-title .article-link')
.toArray()
.map((item) => {
item = $(item);
.map((item): DataItem => {
const $item = $(item);
return {
title: item.text(),
link: `${rootUrl}${item.attr('href')}`,
title: $item.text(),
link: `${rootUrl}${$item.attr('href')}`,
};
});
items = await Promise.all(
items.map((item) =>
cache.tryGet(item.link, async () => {
cache.tryGet(item.link!, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
@@ -61,12 +61,12 @@ export async function handler(ctx) {
const content = load(detailResponse.data);
item.description = content('.text-long').html();
item.title = content('meta[name="cXenseParse:mdc-title"]').attr('content');
item.title = content('meta[name="cXenseParse:mdc-title"]').attr('content')!;
item.author = content('meta[name="cXenseParse:author"]').attr('content');
item.pubDate = parseDate(content('meta[name="cXenseParse:recs:publishtime"]').attr('content'));
item.pubDate = parseDate(content('meta[name="cXenseParse:recs:publishtime"]').attr('content')!);
item.category = content('meta[name="cXenseParse:mdc-keywords"]')
.toArray()
.map((keyword) => content(keyword).attr('content'));
.map((keyword) => content(keyword).attr('content')!);
return item;
})
+9 -9
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -53,25 +53,25 @@ async function handler(ctx) {
let items = $('.row .well')
.toArray()
.map((item) => {
item = $(item);
.map((item): DataItem & { poster?: string } => {
const $item = $(item);
return {
title: item.find('.video-title').text(),
link: item.find('a').attr('href'),
poster: item.find('.img-responsive').attr('src'),
title: $item.find('.video-title').text(),
link: $item.find('a').attr('href'),
poster: $item.find('.img-responsive').attr('src'),
};
});
items = await Promise.all(
items.map((item) =>
cache.tryGet(`91porn:${lang}:${new URL(item.link).searchParams.get('viewkey')}`, async () => {
cache.tryGet(`91porn:${lang}:${new URL(item.link!).searchParams.get('viewkey')}`, async () => {
const { data } = await got(item.link);
const $ = load(data);
item.pubDate = parseDate($('.title-yakov').eq(0).text(), 'YYYY-MM-DD');
item.description = renderIndexDescription({
link: item.link,
poster: item.poster,
link: item.link!,
poster: item.poster!,
});
item.author = $('.title-yakov a span').text();
delete item.poster;
+9 -9
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -56,25 +56,25 @@ async function handler(ctx) {
let items = $('.row .well')
.toArray()
.map((item) => {
item = $(item);
.map((item): DataItem & { poster?: string } => {
const $item = $(item);
return {
title: item.find('.video-title').text(),
link: item.find('a').attr('href'),
poster: item.find('.img-responsive').attr('src'),
title: $item.find('.video-title').text(),
link: $item.find('a').attr('href'),
poster: $item.find('.img-responsive').attr('src'),
};
});
items = await Promise.all(
items.map((item) =>
cache.tryGet(`91porn:${lang}:${new URL(item.link).searchParams.get('viewkey')}`, async () => {
cache.tryGet(`91porn:${lang}:${new URL(item.link!).searchParams.get('viewkey')}`, async () => {
const { data } = await got(item.link);
const $ = load(data);
item.pubDate = parseDate($('.title-yakov').eq(0).text(), 'YYYY-MM-DD');
item.description = renderIndexDescription({
link: item.link,
poster: item.poster,
link: item.link!,
poster: item.poster!,
});
item.author = $('.title-yakov a span').text();
delete item.poster;
+6 -5
View File
@@ -1,6 +1,7 @@
import { load } from 'cheerio';
import { renderToString } from 'hono/jsx/dom/server';
import type { DataItem } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
@@ -19,21 +20,21 @@ const ProcessItems = async (ctx, title, currentUrl) => {
let items = $('div.list-body')
.toArray()
.map((item) => {
item = $(item);
.map((item): DataItem => {
const $item = $(item);
const a = item.find('a');
const a = $item.find('a');
return {
title: a.text(),
link: a.attr('href'),
guid: a.attr('href').replace('95mm.vip', '95mm.org'),
guid: a.attr('href')!.replace('95mm.vip', '95mm.org'),
};
});
items = await Promise.all(
items.map((item) =>
cache.tryGet(item.link, async () => {
cache.tryGet(item.link!, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
+2 -2
View File
@@ -64,7 +64,7 @@ async function handler(ctx) {
const items = await Promise.all(
feed.items.splice(0, limit).map((item) =>
cache.tryGet(item.link, async () => {
cache.tryGet(item.link!, async () => {
const response = await got({
method: 'get',
url: item.link,
@@ -72,7 +72,7 @@ async function handler(ctx) {
const description = utils.ProcessFeed(response.data);
const single = {
title: item.title,
title: item.title!,
description,
pubDate: item.pubDate,
link: item.link,
+1 -1
View File
@@ -6,7 +6,7 @@ const ProcessFeed = (data) => {
const cover = $('meta[property="og:image"]');
if (cover.length > 0) {
$(`<img src=${cover[0].attribs.content}>`).insertBefore(content[0].firstChild);
$(`<img src=${cover[0].attribs.content}>`).insertBefore(content[0].firstChild!);
}
// remove useless DOMs
+17 -17
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { DataItem, Language, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -24,15 +24,15 @@ export const handler = async (ctx) => {
let items = $('a.a9-rich-card-list_item')
.slice(0, limit)
.toArray()
.map((item) => {
item = $(item);
.map((item): DataItem => {
const $item = $(item);
const image = item.find('img.a9-rich-card-list_image');
const title = item.find('div.a9-rich-card-list_label').text();
const image = $item.find('img.a9-rich-card-list_image');
const title = $item.find('div.a9-rich-card-list_label').text();
return {
title,
link: new URL(item.prop('href'), rootUrl).href,
link: new URL($item.prop('href')!, rootUrl).href,
description: renderDescription({
images: image
? [
@@ -43,28 +43,28 @@ export const handler = async (ctx) => {
]
: undefined,
}),
pubDate: timezone(parseDate(item.find('div.a9-rich-card-list_infos').text()), 8),
language,
pubDate: timezone(parseDate($item.find('div.a9-rich-card-list_infos').text()), 8),
language: language as Language,
};
});
items = await Promise.all(
items.map((item) =>
cache.tryGet(item.link, async () => {
cache.tryGet(item.link!, async () => {
const { data: detailResponse } = await got(item.link);
const $$ = load(detailResponse);
$$('ignore_js_op img, p img').each((_, el) => {
el = $$(el);
const $el = $$(el);
el.parent().replaceWith(
$el.parent().replaceWith(
renderDescription({
images: el.prop('file')
images: $el.prop('file')
? [
{
src: el.prop('file'),
alt: el.next().find('div.xs0 p').first().text(),
src: $el.prop('file'),
alt: $el.next().find('div.xs0 p').first().text(),
},
]
: undefined,
@@ -74,7 +74,7 @@ export const handler = async (ctx) => {
item.title = $$('h1.ts, div.c-article-main_content-title').text();
item.description = renderDescription({
description: $$('td.t_f, div.c-article-main_contentraw').first().html(),
description: $$('td.t_f, div.c-article-main_contentraw').first().html() ?? undefined,
});
item.author =
$$('b a.blue').first().text() ||
@@ -96,7 +96,7 @@ export const handler = async (ctx) => {
),
8
);
item.language = language;
item.language = language as Language;
return item;
})
@@ -114,7 +114,7 @@ export const handler = async (ctx) => {
allowEmpty: true,
image,
author: title.split(/-/).pop(),
language,
language: language as Language,
};
};
+3 -3
View File
@@ -2,7 +2,7 @@ import type { CheerioAPI } from 'cheerio';
import { load } from 'cheerio';
import type { Context } from 'hono';
import type { Data, DataItem, Route } from '@/types';
import type { Data, DataItem, Language, Route } from '@/types';
import { ViewType } from '@/types';
import ofetch from '@/utils/ofetch';
import { parseDate } from '@/utils/parse-date';
@@ -76,7 +76,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
image,
banner: image,
updated: updated ? parseDate(updated) : undefined,
language,
language: language as Language,
};
return processedItem;
@@ -92,7 +92,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
allowEmpty: true,
image: $('header#header-div img').attr('src'),
author: title.split(/-/).pop(),
language,
language: language as Language,
id: targetUrl,
};
};
+7 -7
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -56,18 +56,18 @@ async function handler(ctx) {
const list = $('post-title a')
.toArray()
.map((item) => {
item = $(item);
.map((item): DataItem => {
const $item = $(item);
return {
title: item.text(),
link: item.attr('href'),
title: $item.text(),
link: $item.attr('href'),
};
});
const items = await Promise.all(
list.map((item) =>
cache.tryGet(item.link, async () => {
cache.tryGet(item.link!, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
@@ -79,7 +79,7 @@ async function handler(ctx) {
item.description = content('#contentleft').html();
item.author = content('meta[itemprop="author"]').attr('content');
item.pubDate = parseDate(content('meta[property="article:published_time"]').attr('content'));
item.pubDate = parseDate(content('meta[property="article:published_time"]').attr('content')!);
return item;
})
+5 -5
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { DataItem, Language, Route } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
import { parseDate } from '@/utils/parse-date';
@@ -67,7 +67,7 @@ async function handler(ctx) {
});
let items = response.collection.slice(0, limit).map((i) => {
const item = {
const item: DataItem = {
title: i.title.children ?? i.title,
link: i.link.startsWith('https://') ? i.link : new URL(i.link, rootUrl).href,
description: renderDescription({
@@ -107,11 +107,11 @@ async function handler(ctx) {
.children()
.each((_, el) => {
const element = content(el);
if (element.prop('tagName').toLowerCase() === 'figure') {
if (element.prop('tagName')!.toLowerCase() === 'figure') {
element.replaceWith(
renderDescription({
image: {
src: element.find('img').prop('src').split(/\?/, 1)[0],
src: element.find('img').prop('src')!.split(/\?/, 1)[0],
alt: element.find('figcaption').text().trim(),
},
})
@@ -178,7 +178,7 @@ async function handler(ctx) {
title: $('title').first().text(),
link: currentUrl,
description: $('meta[property="og:description"]').prop('content'),
language: $('html').prop('lang'),
language: $('html').prop('lang') as Language,
image: $('meta[property="og:image"]').prop('content').split('?', 1)[0],
icon,
logo: icon,
+2 -2
View File
@@ -1,6 +1,6 @@
import { raw } from 'hono/html';
import type { FC } from 'hono/jsx';
import { renderToString } from 'hono/jsx/dom/server';
import type { JSX } from 'hono/jsx/jsx-runtime';
type DescriptionData = {
image?: {
@@ -15,7 +15,7 @@ type DescriptionData = {
};
const AbcDescription = ({ image, enclosure, description }: DescriptionData) => {
const enclosureTag = enclosure?.type?.split('/', 1)[0] as keyof JSX.IntrinsicElements | undefined;
const enclosureTag = enclosure?.type?.split('/', 1)[0] as unknown as FC | undefined;
return (
<>
+5 -5
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { Language, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -64,7 +64,7 @@ export const handler = async (ctx) => {
},
image,
banner: image,
language,
language: language as Language,
};
});
@@ -79,7 +79,7 @@ export const handler = async (ctx) => {
const description =
item.description +
renderDescription({
description: $$('div.khl-article-page-storybody').html(),
description: $$('div.khl-article-page-storybody').html() ?? undefined,
});
item.title = title;
@@ -99,7 +99,7 @@ export const handler = async (ctx) => {
)
);
const image = new URL($('a.navbar-brand img').prop('src'), rootUrl).href;
const image = new URL($('a.navbar-brand img').prop('src')!, rootUrl).href;
return {
title: $('title').text(),
@@ -109,7 +109,7 @@ export const handler = async (ctx) => {
allowEmpty: true,
image,
author: $('meta[property="og:site_name"]').prop('content'),
language,
language: language as Language,
};
};
@@ -20,7 +20,7 @@ const AccessBriefingDescription = ({ images, intro, description }: DescriptionDa
? images.map((image) =>
image?.src ? (
<figure>
<img alt={image.height ?? image.width ?? image.alt} src={image.src} />
<img alt={(image.height ?? image.width ?? image.alt) as string | undefined} src={image.src} />
</figure>
) : null
)
+1 -1
View File
@@ -133,7 +133,7 @@ async function handler(ctx) {
const $ = load(response.data);
const articleInfo = $('.main script')
.text()
.match(/window.articleInfo = (.*);\n\s*window.likeDomain/)[1];
.match(/window.articleInfo = (.*);\n\s*window.likeDomain/)![1];
const data = JSON.parse(articleInfo);
item.description = data.parts[0].content;
+8 -8
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { DataItem, Language, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -38,19 +38,19 @@ async function handler(ctx) {
let items = $('div.text01 ul li a[title]')
.slice(0, limit)
.toArray()
.map((item) => {
item = $(item);
.map((item): DataItem => {
const $item = $(item);
return {
title: item.prop('title'),
link: new URL(item.prop('href'), rootUrl).href,
pubDate: timezone(parseDate(item.find('span[title]').prop('title')), 8),
title: $item.prop('title')!,
link: new URL($item.prop('href')!, rootUrl).href,
pubDate: timezone(parseDate($item.find('span[title]').prop('title')), 8),
};
});
items = await Promise.all(
items.map((item) =>
cache.tryGet(item.link, async () => {
cache.tryGet(item.link!, async () => {
const { data: detailResponse } = await got(item.link);
const content = load(detailResponse);
@@ -71,7 +71,7 @@ async function handler(ctx) {
title: `${author} - ${subtitle}`,
link: currentUrl,
description: $('meta[property="og:description"]').prop('content'),
language: 'zh',
language: 'zh' as Language,
subtitle,
author,
};
+7 -7
View File
@@ -56,28 +56,28 @@ async function handler(ctx) {
const $ = load(html);
title = $('meta[property="og:title"]').attr('content');
title = $('meta[property="og:title"]').attr('content')!;
return $('.issue-item')
.toArray()
.map((item) => {
item = $(item);
const $item = $(item);
const a = item.find('.issue-item_title a');
const doi = item.find('input[name="doi"]').attr('value');
const a = $item.find('.issue-item_title a');
const doi = $item.find('input[name="doi"]').attr('value');
return {
doi,
guid: doi,
title: a.text(),
link: `${rootUrl}${a.attr('href')}`,
pubDate: parseDate(item.find('.pub-date-value').text(), 'MMMM D, YYYY'),
author: item
pubDate: parseDate($item.find('.pub-date-value').text(), 'MMMM D, YYYY'),
author: $item
.find('.issue-item_loa li')
.toArray()
.map((a) => $(a).text())
.join(', '),
description: renderDescription(item.find('.issue-item_img').html(), item.find('.hlFld-Abstract').html()),
description: renderDescription($item.find('.issue-item_img').html(), $item.find('.hlFld-Abstract').html()),
};
});
},
+6 -6
View File
@@ -3,7 +3,7 @@ import { load } from 'cheerio';
import type { Element } from 'domhandler';
import type { Context } from 'hono';
import type { Data, DataItem, Route } from '@/types';
import type { Data, DataItem, Language, Route } from '@/types';
import { ViewType } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
@@ -25,7 +25,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
let items: DataItem[] = $('div.article_1')
.slice(0, limit)
.toArray()
.map((el): Element => {
.map((el) => {
const $el: Cheerio<Element> = $(el);
const title: string = $el.find('p.article_2_p').text();
@@ -51,7 +51,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
image,
banner: image,
updated: upDatedStr ? parseDate(upDatedStr) : undefined,
language,
language: language as Language,
};
return processedItem;
@@ -65,7 +65,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
}
return cache.tryGet(item.link, async (): Promise<DataItem> => {
const detailResponse = await ofetch(item.link);
const detailResponse = await ofetch(item.link!);
const $$: CheerioAPI = load(detailResponse);
const title: string = $$('p.infoTitle_left').text();
@@ -89,7 +89,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
text: description,
},
updated: upDatedStr ? timezone(parseDate(upDatedStr), 8) : item.updated,
language,
language: language as Language,
};
return {
@@ -109,7 +109,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
allowEmpty: true,
image: $('img.navi_logo').attr('src'),
author: $('meta[name="author"]').attr('content'),
language,
language: language as Language,
id: targetUrl,
};
};
+6 -6
View File
@@ -3,7 +3,7 @@ import { load } from 'cheerio';
import type { Element } from 'domhandler';
import type { Context } from 'hono';
import type { Data, DataItem, Route } from '@/types';
import type { Data, DataItem, Language, Route } from '@/types';
import { ViewType } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
@@ -25,7 +25,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
let items: DataItem[] = $('div.article_1')
.slice(0, limit)
.toArray()
.map((el): Element => {
.map((el) => {
const $el: Cheerio<Element> = $(el);
const title: string = $el.find('p.article_2_p').text();
@@ -51,7 +51,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
image,
banner: image,
updated: upDatedStr ? parseDate(upDatedStr) : undefined,
language,
language: language as Language,
};
return processedItem;
@@ -65,7 +65,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
}
return cache.tryGet(item.link, async (): Promise<DataItem> => {
const detailResponse = await ofetch(item.link);
const detailResponse = await ofetch(item.link!);
const $$: CheerioAPI = load(detailResponse);
const title: string = $$('p.infoTitle_left').text();
@@ -89,7 +89,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
text: description,
},
updated: upDatedStr ? timezone(parseDate(upDatedStr), 8) : item.updated,
language,
language: language as Language,
};
return {
@@ -109,7 +109,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
allowEmpty: true,
image: $('img.navi_logo').attr('src'),
author: $('meta[name="author"]').attr('content'),
language,
language: language as Language,
id: targetUrl,
};
};
+10 -9
View File
@@ -1,7 +1,7 @@
import { load } from 'cheerio';
import { renderToString } from 'hono/jsx/dom/server';
import type { Route } from '@/types';
import type { DataItem, Language, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -64,17 +64,18 @@ async function handler(ctx) {
let items = $('h4.title a')
.toArray()
.map((item) => {
item = $(item);
.map((item): DataItem => {
const $item = $(item);
return {
link: `${rootUrl}${item.attr('href').split('&', 1)[0]}`,
title: '',
link: `${rootUrl}${$item.attr('href')!.split('&', 1)[0]}`,
};
});
items = await Promise.all(
items.map((item) =>
cache.tryGet(item.link, async () => {
cache.tryGet(item.link!, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
@@ -85,16 +86,16 @@ async function handler(ctx) {
item.doi = content('meta[name="citation_doi"]').attr('content');
item.guid = item.doi;
item.title = content('meta[name="citation_title"]').attr('content');
item.title = content('meta[name="citation_title"]').attr('content')!;
item.author = content('.author')
.toArray()
.map((a) => content(a).text().trim())
.join(', ');
item.pubDate = parseDate(content('meta[name="citation_publication_date"]').attr('content'), 'YYYY/MM');
item.pubDate = parseDate(content('meta[name="citation_publication_date"]').attr('content')!, 'YYYY/MM');
item.description = renderToString(
<AeawebDescription
description={content('meta[name="twitter:description"]')
.attr('content')
.attr('content')!
.replace(/\(\w+ \d+\)( - )?/, '')}
/>
);
@@ -109,7 +110,7 @@ async function handler(ctx) {
description,
link: currentUrl,
item: items,
language: $('html').attr('lang'),
language: $('html').attr('lang') as Language,
};
}
+1
View File
@@ -1,3 +1,4 @@
import type { Route } from '@/types';
import got from '@/utils/got';
const categoryMap = {
+6 -6
View File
@@ -3,7 +3,7 @@ import { load } from 'cheerio';
import type { Element } from 'domhandler';
import type { Context } from 'hono';
import type { Data, DataItem, Route } from '@/types';
import type { Data, DataItem, Language, Route } from '@/types';
import { ViewType } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
@@ -22,7 +22,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
let items: DataItem[] = $('article.article')
.slice(0, limit)
.toArray()
.map((el): Element => {
.map((el) => {
const $el: Cheerio<Element> = $(el);
const $aEl: Cheerio<Element> = $el.find('header.container h1 a').first();
@@ -56,7 +56,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
image,
banner: image,
updated: upDatedStr ? parseDate(upDatedStr) : undefined,
language,
language: language as Language,
};
return processedItem;
@@ -70,7 +70,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
}
return cache.tryGet(item.link, async (): Promise<DataItem> => {
const detailResponse = await ofetch(item.link);
const detailResponse = await ofetch(item.link!);
const $$: CheerioAPI = load(detailResponse);
const title: string = $$('header.article-header h1').text();
@@ -101,7 +101,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
image,
banner: image,
updated: upDatedStr ? parseDate(upDatedStr) : item.updated,
language,
language: language as Language,
};
return {
@@ -123,7 +123,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
allowEmpty: true,
image: $('img.main-logo').attr('src') ? new URL($('img.main-logo').attr('src') as string, baseUrl).href : undefined,
author: title.split(/\|/).pop(),
language,
language: language as Language,
id: targetUrl,
};
};
+3 -3
View File
@@ -39,11 +39,11 @@ async function handler(ctx) {
.find('li')
.toArray()
.map((item) => {
item = $(item);
const a = item.find('a');
const $item = $(item);
const a = $item.find('a');
return {
title: a.text(),
link: a.attr('href').replace('http://', 'https://'),
link: a.attr('href')!.replace('http://', 'https://'),
};
})
.toReversed();
+6 -6
View File
@@ -39,20 +39,20 @@ async function handler() {
const list = $('.video_item')
.toArray()
.map((item) => {
item = $(item);
const link = item.find('a').attr('href').replace('http://', 'https://');
.map((item): DataItem => {
const $item = $(item);
const link = $item.find('a').attr('href')!.replace('http://', 'https://');
return {
title: item.text(),
title: $item.text(),
link,
guid: `${link}#${item.find('.video_item--info').text()}`,
guid: `${link}#${$item.find('.video_item--info').text()}`,
};
});
const items: DataItem[] = await pMap(
list,
(item) =>
cache.tryGet(item.link, async () => {
cache.tryGet(item.link!, async () => {
const detailResponse = await got(item.link);
const content = load(detailResponse.data);
+6 -6
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { Language, Route } from '@/types';
import ofetch from '@/utils/ofetch';
import { baseUrl } from './utils';
@@ -40,11 +40,11 @@ async function handler() {
const items = $('.ag-topic')
.toArray()
.map((item) => {
item = $(item);
const $item = $(item);
return {
title: item.find('.ag-topic__link').text(),
description: item.find('.ag-topic__summery').text(),
link: `${baseUrl}${item.find('.ag-topic__link').attr('href')}`,
title: $item.find('.ag-topic__link').text(),
description: $item.find('.ag-topic__summery').text(),
link: `${baseUrl}${$item.find('.ag-topic__link').attr('href')}`,
};
});
@@ -53,6 +53,6 @@ async function handler() {
link,
description: $('head meta[name=description]').attr('content'),
item: items,
language: $('html').attr('lang'),
language: $('html').attr('lang') as Language,
};
}
+5 -5
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { Language, Route } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
@@ -39,10 +39,10 @@ async function handler(ctx) {
const list = $('.ag-post-item__link')
.toArray()
.map((item) => {
item = $(item);
const $item = $(item);
return {
title: item.text(),
link: `${baseUrl}${item.attr('href')}`,
title: $item.text(),
link: `${baseUrl}${$item.attr('href')}`,
};
});
@@ -53,6 +53,6 @@ async function handler(ctx) {
link,
description: ldJson['@graph'][0].description,
item: items,
language: $('html').attr('lang'),
language: $('html').attr('lang') as Language,
};
}
+5 -5
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { Language, Route } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
@@ -43,10 +43,10 @@ async function handler(ctx) {
const list = $('.ag-post-list .ag-post-item__link')
.toArray()
.map((item) => {
item = $(item);
const $item = $(item);
return {
title: item.text(),
link: `${baseUrl}${item.attr('href')}`,
title: $item.text(),
link: `${baseUrl}${$item.attr('href')}`,
};
});
@@ -57,6 +57,6 @@ async function handler(ctx) {
link,
description: $('head meta[name=description]').attr('content'),
item: items,
language: $('html').attr('lang'),
language: $('html').attr('lang') as Language,
};
}
+7 -7
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -48,18 +48,18 @@ async function handler(ctx) {
let items = $('.card span:not(.comments) a')
.slice(0, ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 50)
.toArray()
.map((item) => {
item = $(item);
.map((item): DataItem => {
const $item = $(item);
return {
title: item.text(),
link: item.attr('href'),
title: $item.text(),
link: $item.attr('href'),
};
});
items = await Promise.all(
items.map((item) =>
cache.tryGet(item.link, async () => {
cache.tryGet(item.link!, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
@@ -68,7 +68,7 @@ async function handler(ctx) {
const content = load(detailResponse.data);
item.author = content('meta[name="author"]').attr('content');
item.pubDate = parseDate(content('meta[property="article:published_time"]').attr('content'));
item.pubDate = parseDate(content('meta[property="article:published_time"]').attr('content')!);
item.description = content('.post-content').html();
return item;
+7 -7
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -37,12 +37,12 @@ async function handler() {
const list = $('div article')
.toArray()
.slice(0, -1) // last one is a dummy
.map((item) => {
item = $(item);
const meta = item.find('h5').first().text();
.map((item): DataItem => {
const $item = $(item);
const meta = $item.find('h5').first().text();
return {
title: item.find('h3').text(),
link: item.find('h3 a').attr('href'),
title: $item.find('h3').text(),
link: $item.find('h3 a').attr('href'),
author: meta.split('|', 1)[0].trim(),
pubDate: parseDate(meta.split('|', 2)[1].trim()),
};
@@ -50,7 +50,7 @@ async function handler() {
const items = await Promise.all(
list.map((item) =>
cache.tryGet(item.link, async () => {
cache.tryGet(item.link!, async () => {
const response = await got(item.link);
const $ = load(response.data);
$('h1').remove();
+14 -14
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { DataItem, Language, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -24,15 +24,15 @@ export const handler = async (ctx) => {
let items = $('div.list_li_con, div.nxw_video_com')
.slice(0, limit)
.toArray()
.map((item) => {
item = $(item);
.map((item): DataItem => {
const $item = $(item);
const a = item.find('a').first();
const a = $item.find('a').first();
const title = a.text();
const image = item.find('img').first().prop('src') ? new URL(item.find('img').first().prop('src'), rootUrl).href : undefined;
const image = $item.find('img').first().prop('src') ? new URL($item.find('img').first().prop('src')!, rootUrl).href : undefined;
const description = renderDescription({
intro: item.find('p.con_text').text() || undefined,
intro: $item.find('p.con_text').text() || undefined,
images: image
? [
{
@@ -46,21 +46,21 @@ export const handler = async (ctx) => {
return {
title,
description,
pubDate: parseDate(item.find('span.con_date_span').text() || `${item.find('div.com_time_p2').text().trim()}${item.find('div.com_time_p1').text()}`, ['YYYY-MM-DD', 'YYYY.MM.DD']),
link: new URL(a.prop('href'), currentUrl).href,
pubDate: parseDate($item.find('span.con_date_span').text() || `${$item.find('div.com_time_p2').text().trim()}${$item.find('div.com_time_p1').text()}`, ['YYYY-MM-DD', 'YYYY.MM.DD']),
link: new URL(a.prop('href')!, currentUrl).href,
content: {
html: description,
text: item.find('p.con_text').text() || undefined,
text: $item.find('p.con_text').text() || undefined,
},
image,
banner: image,
language,
language: language as Language,
};
});
items = await Promise.all(
items.map((item) =>
cache.tryGet(item.link, async () => {
cache.tryGet(item.link!, async () => {
const { data: detailResponse } = await got(item.link);
const $$ = load(detailResponse);
@@ -78,7 +78,7 @@ export const handler = async (ctx) => {
html: description,
text: $$('div.content_body_box').text(),
};
item.language = language;
item.language = language as Language;
item.enclosure_url = $$('div.content_body_box video').prop('src') ?? undefined;
item.enclosure_type = item.enclosure_url ? 'video/mp4' : undefined;
@@ -89,7 +89,7 @@ export const handler = async (ctx) => {
)
);
const image = new URL($('div.logo img').prop('src'), rootUrl).href;
const image = new URL($('div.logo img').prop('src')!, rootUrl).href;
return {
title: $('title').text(),
@@ -97,7 +97,7 @@ export const handler = async (ctx) => {
item: items,
allowEmpty: true,
image,
language,
language: language as Language,
};
};
+8 -8
View File
@@ -1,6 +1,6 @@
import { load } from 'cheerio';
import type { Route } from '@/types';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -44,15 +44,15 @@ async function handler() {
const list = $('#wp_news_w9')
.find('li')
.toArray()
.map((item) => {
item = $(item);
const date = item.find('.column-news-date').text();
.map((item): DataItem => {
const $item = $(item);
const date = $item.find('.column-news-date').text();
// 置顶链接自带http前缀,其他不带,需要手动判断
const a = item.find('a').attr('href');
const link = a.slice(0, 4) === 'http' ? a : rootUrl + a;
const a = $item.find('a').attr('href');
const link = a!.startsWith('http') ? a : rootUrl + a;
return {
title: item.find('a').attr('title'),
title: $item.find('a').attr('title')!,
link,
pubDate: timezone(parseDate(date), 8),
};
@@ -60,7 +60,7 @@ async function handler() {
const items = await Promise.all(
list.map((item) =>
cache.tryGet(item.link, async () => {
cache.tryGet(item.link!, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
+1 -1
View File
@@ -61,7 +61,7 @@ export const route: Route = {
return {
title: 'AI日报',
description: '每天三分钟关注AI行业趋势',
language: 'zh-cn',
language: 'zh-CN',
link: 'https://www.aibase.com/zh/daily',
item: items,
allowEmpty: true,
+1 -1
View File
@@ -63,7 +63,7 @@ export const handler = async (ctx) => {
const items = processItems(apiProcs?.slice(0, limit) ?? []);
const image = new URL($('img.logo').prop('src'), rootUrl).href;
const image = new URL($('img.logo').prop('src')!, rootUrl).href;
const author = $('title').text().split(/_/).pop();
+1 -1
View File
@@ -50,7 +50,7 @@ export const route: Route = {
return {
title: 'AI新闻资讯',
description: 'AI新闻资讯 - 不错过全球AI革新的每一个时刻',
language: 'zh-cn',
language: 'zh-CN',
link: 'https://www.aibase.com/zh/news',
item: items,
allowEmpty: true,

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