Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pull] master from diygod:master #1373

Merged
merged 10 commits into from
May 3, 2024
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
@@ -59,7 +59,7 @@
"unicorn/explicit-length-check": 0,
"unicorn/filename-case": ["error", { "case": "kebabCase", "ignore": [".*\\.(yaml|yml)$", "RequestInProgress\\.js$"] }],
"unicorn/new-for-builtins": 0,
"unicorn/no-array-callback-reference": 0,
"unicorn/no-array-callback-reference": 1,
"unicorn/no-array-reduce": 1,
"unicorn/no-await-expression-member": 0,
"unicorn/no-empty-file": 1,
2 changes: 1 addition & 1 deletion lib/routes/caixin/templates/article.art
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@
<% const video = $('script').text().match(/initPlayer\('(.*?)','(.*?)'\)/); %>
<% const videoUrl = video[1]; %>
<% const poster = video[2]; %>
<video controls preload="none" poster="{{ poster }}" src="{{ videoUrl }}"></video>
<video controls preload="metadata" poster="{{ poster }}" src="{{ videoUrl }}"></video>
<br>
{{ /if }}

2 changes: 1 addition & 1 deletion lib/routes/douyin/templates/embed.art
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<video controls preload="none" referrerpolicy="no-referrer"
<video controls preload="metadata" referrerpolicy="no-referrer"
style="width:50%"
{{ if img }}
poster="{{ img }}"
2 changes: 1 addition & 1 deletion lib/routes/fansly/templates/media.art
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{{ if poster && src }}
<video controls preload="none" poster="{{ poster.location }}">
<video controls preload="metadata" poster="{{ poster.location }}">
<source src="{{ src.location }}" type="video/mp4">
</video>
{{ else if src }}
2 changes: 1 addition & 1 deletion lib/routes/ifeng/templates/video.art
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{{ if videoInfo.mobileUrl }}
<video controls poster="{{ videoInfo.bigPosterUrl }}" preload="none">
<video controls poster="{{ videoInfo.bigPosterUrl }}" preload="metadata">
<source src="{{ videoInfo.mobileUrl }}" type="video/mp4">
</video>
{{ /if }}
2 changes: 1 addition & 1 deletion lib/routes/instagram/templates/video.art
Original file line number Diff line number Diff line change
@@ -3,6 +3,6 @@
<br>
{{ /if }}

<video controls preload="none" poster="{{ image }}" width="{{ video.width }}">
<video controls preload="metadata" poster="{{ image }}" width="{{ video.width }}">
<source src="{{ video.url }}" type="video/mp4">
</video>
13 changes: 13 additions & 0 deletions lib/routes/ithome/templates/description.art
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{{ if images }}
{{ each images image }}
{{ if image?.src }}
<figure>
<img
{{ if image.alt }}
alt="{{ image.alt }}"
{{ /if }}
src="{{ image.src }}">
</figure>
{{ /if }}
{{ /each }}
{{ /if }}
174 changes: 118 additions & 56 deletions lib/routes/ithome/zt.ts
Original file line number Diff line number Diff line change
@@ -1,88 +1,150 @@
import { Route } from '@/types';
import { getCurrentPath } from '@/utils/helpers';
const __dirname = getCurrentPath(import.meta.url);

import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';

export const route: Route = {
path: '/zt/:id',
categories: ['new-media'],
example: '/ithome/zt/xijiayi',
parameters: { id: '专题 id' },
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
radar: [
{
source: ['ithome.com/zt/:id'],
},
],
name: '专题',
maintainers: ['nczitzk'],
handler,
description: `所有专题请见[此处](https://www.ithome.com/zt)`,
};

async function handler(ctx) {
const id = ctx.req.param('id');
export const handler = async (ctx) => {
const { id = 'xijiayi' } = ctx.req.param();
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 50;

const rootUrl = 'https://www.ithome.com';
const currentUrl = `${rootUrl}/zt/${id}`;
const currentUrl = new URL(`zt/${id}`, rootUrl).href;

const { data: response } = await got(currentUrl);

const response = await got({
method: 'get',
url: currentUrl,
});
const $ = load(response);

const $ = load(response.data);
const author = 'IT之家';
const language = 'zh';

const list = $('.newsbody a')
.map((_, item) => {
let items = $('div.newsbody')
.slice(0, limit)
.toArray()
.map((item) => {
item = $(item);

const title = item.find('h2').text();
const image = item.find('img').prop('data-original') ?? item.find('img').prop('src');

return {
title: item.text(),
link: item.attr('href'),
title,
pubDate: timezone(
parseDate(
item
.find('span.time script')
.text()
.match(/'(.*?)'/)
),
+8
),
link: item.find('a').first().prop('href'),
author: item.find('div.editor').contents().first().text(),
image,
banner: image,
language,
};
})
.get();
});

const items = await Promise.all(
list.map((item) =>
items = await Promise.all(
items.map((item) =>
cache.tryGet(item.link, async () => {
const detailResponse = await got({
method: 'get',
url: item.link,
});
const { data: detailResponse } = await got(item.link);

const $$ = load(detailResponse);

$$('p.ad-tips, a.topic-bar').remove();

const content = load(detailResponse.data);
const post = content('.post_content');
$$('div#paragraph p img').each((_, el) => {
el = $$(el);

post.find('img[data-original]').each((_, ele) => {
ele = $(ele);
ele.attr('src', ele.attr('data-original'));
ele.removeAttr('class');
ele.removeAttr('data-original');
const src = el.prop('data-original');

if (src) {
el.replaceWith(
art(path.join(__dirname, 'templates/description.art'), {
images: [
{
src,
alt: el.prop('alt'),
},
],
})
);
}
});

item.description = post.html();
item.author = content('#author_baidu').text().replace('作者:', '');
item.pubDate = timezone(parseDate(content('#pubtime_baidu').text()), +8);
const title = $$('h1').text();
const description = $$('div#paragraph').html();
const image = $$('div#paragraph img').first().prop('src');

item.title = title;
item.description = description;
item.pubDate = timezone(parseDate($$('span#pubtime_baidu').text()), +8);
item.category = $$('div.cv a')
.toArray()
.map((c) => $$(c).text())
.slice(1);
item.author = $$('span#author_baidu').contents().last().text() || $$('span#source_baidu').contents().last().text() || $$('span#editor_baidu').contents().last().text();
item.content = {
html: description,
text: $$('div#paragraph').text(),
};
item.image = image;
item.banner = image;
item.language = language;

return item;
})
)
);

const image = new URL($('meta[property="og:image"]').prop('content'), rootUrl).href;

return {
title: `${$('title').text()} - IT之家`,
title: `${author} - ${$('title').text()}`,
description: $('meta[name="description"]').prop('content'),
link: currentUrl,
item: items,
allowEmpty: true,
image,
author,
language,
};
}
};

export const route: Route = {
path: '/zt/:id?',
name: '专题',
url: 'ithome.com',
maintainers: ['nczitzk'],
handler,
example: '/ithome/zt/xijiayi',
parameters: { category: '专题 id,默认为 xijiayi,即 [喜加一](https://www.ithome.com/zt/xijiayi),可在对应专题页 URL 中找到' },
description: `:::tip
更多专题请见 [IT之家专题](https://www.ithome.com/zt)
:::`,
categories: ['new-media'],

features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportRadar: true,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
radar: [
{
source: ['ithome.com/zt/:id'],
target: '/zt/:id',
},
],
};
2 changes: 1 addition & 1 deletion lib/routes/kcna/utils.ts
Original file line number Diff line number Diff line change
@@ -54,7 +54,7 @@ const fetchVideo = (ctx, url) =>
const js = $('script[type="text/javascript"]:not([src])').html();
let sources = js.match(/<[^>]*source[^>]+src[^>]+>/g);
sources = sources && sources.map((item) => item.replaceAll("'", '"').replaceAll(/src="([^"]+)"/g, `src="${rootUrl}$1"`));
return `<video controls preload="none">${sources.join('\n')}</video>`;
return `<video controls preload="metadata">${sources.join('\n')}</video>`;
});

export { parseJucheDate, fixDesc, fetchPhoto, fetchVideo };
10 changes: 5 additions & 5 deletions lib/routes/linkedin/utils.ts
Original file line number Diff line number Diff line change
@@ -87,11 +87,11 @@ function parseJobSearch(data) {
const jobs = $('li')
.map((i, elem) => {
const elemHtml = $(elem);
const link = elemHtml.find('a.base-card__full-link').attr('href').split('?')[0];
const title = elemHtml.find('h3.base-search-card__title').text().trim();
const company = elemHtml.find('h4.base-search-card__subtitle').text().trim();
const location = elemHtml.find('span.job-search-card__location').text().trim();
const pubDate = elemHtml.find('time').attr('datetime');
const link = elemHtml.find('a.base-card__full-link, a.base-card--link')?.attr('href')?.split('?')[0];
const title = elemHtml.find('h3.base-search-card__title')?.text()?.trim();
const company = elemHtml.find('h4.base-search-card__subtitle')?.text()?.trim();
const location = elemHtml.find('span.job-search-card__location')?.text()?.trim();
const pubDate = elemHtml.find('time')?.attr('datetime');

return new Job(title, link, company, location, pubDate);
})
63 changes: 63 additions & 0 deletions lib/routes/magnumphotos/magazine.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Route } from '@/types';
import cache from '@/utils/cache';
import parser from '@/utils/rss-parser';
import ofetch from '@/utils/ofetch';
import { load } from 'cheerio';
const host = 'https://www.magnumphotos.com';
export const route: Route = {
path: '/magazine',
categories: ['picture'],
example: '/magnumphotos/magazine',
parameters: {},
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
radar: [
{
source: ['magnumphotos.com/'],
},
],
name: 'Magazine',
maintainers: ['EthanWng97'],
handler,
url: 'magnumphotos.com/',
};

async function handler() {
const rssUrl = `${host}/feed/`;
const feed = await parser.parseURL(rssUrl);
const items = await Promise.all(
feed.items.map((item) =>
cache.tryGet(item.link, async () => {
if (!item.link) {
return;
}
const data = await ofetch(item.link);
const $ = load(data);
const description = $('#content');
description.find('ul.share').remove();
description.find('h1').remove();

return {
title: item.title,
pubDate: item.pubDate,
link: item.link,
category: item.categories,
description: description.html(),
};
})
)
);

return {
title: 'Magnum Photos',
link: host,
description: 'Magnum is a community of thought, a shared human quality, a curiosity about what is going on in the world, a respect for what is going on and a desire to transcribe it visually',
item: items,
};
}
6 changes: 6 additions & 0 deletions lib/routes/magnumphotos/namespace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { Namespace } from '@/types';

export const namespace: Namespace = {
name: 'Magnum Photos',
url: 'magnumphotos.com',
};
2 changes: 1 addition & 1 deletion lib/routes/mingpao/templates/fancybox.art
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{{ each media }}
{{ if $value.video }}
<video controls preload="none" poster="{{ $value.video.replace('.mp4', '.jpg') }}"><source src="{{ $value.video }}" type="video/mp4"></video>
<video controls preload="metadata" poster="{{ $value.video.replace('.mp4', '.jpg') }}"><source src="{{ $value.video }}" type="video/mp4"></video>
{{ else }}
<figure><img src="{{ $value.href }}" alt="{{ $value.title }}"><figcaption>{{ $value.title }}</figcaption></figure>
{{ /if }}
2 changes: 1 addition & 1 deletion lib/routes/missav/templates/preview.art
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<video controls preload="none" poster="{{ poster }}">
<video controls preload="metadata" poster="{{ poster }}">
<source src="{{ video }}" type="{{ type }}">
</video>
2 changes: 1 addition & 1 deletion lib/routes/pikabu/templates/video.art
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{{ if videoId }}
<iframe id="ytplayer" type="text/html" width="640" height="360" src="https://www.youtube-nocookie.com/embed/{{ videoId }}" frameborder="0" allowfullscreen></iframe>
{{ else if webm || mp4 }}
<video controls preload="none" poster="{{ preview }}" width="{{ width }}">
<video controls preload="metadata" poster="{{ preview }}" width="{{ width }}">
{{ if webm }}<source src="{{ webm }}" type="video/webm">{{ /if }}
{{ if mp4 }}<source src="{{ mp4 }}" type="video/mp4">{{ /if }}
</video>
2 changes: 1 addition & 1 deletion lib/routes/pornhub/templates/description.art
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{{ if previewVideo }}
<video controls preload="none" poster="{{ poster }}">
<video controls preload="metadata" poster="{{ poster }}">
<source src="{{ previewVideo }}" type="video/webm">
</video>
{{ /if }}
2 changes: 1 addition & 1 deletion lib/routes/sina/templates/video.art
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{{ if videoUrl }}
<video controls poster="{{ poster }}" preload="none">
<video controls poster="{{ poster }}" preload="metadata">
<source src="{{ videoUrl }}">
</video>
{{ /if }}
2 changes: 2 additions & 0 deletions lib/routes/techcrunch/news.ts
Original file line number Diff line number Diff line change
@@ -44,6 +44,8 @@ async function handler() {
const description = $('#root');
description.find('.article__title').remove();
description.find('.article__byline__meta').remove();
description.find('.mobile-header-nav').remove();
description.find('.desktop-nav').remove();
return {
title: item.title,
pubDate: item.pubDate,
23 changes: 23 additions & 0 deletions lib/routes/theinitium/author.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Route } from '@/types';
import { processFeed } from './utils';

const handler = (ctx) => processFeed('author', ctx);

export const route: Route = {
path: '/author/:type/:language?',
name: '作者',
maintainers: ['AgFlore'],
parameters: {
type: '作者 ID,可从作者主页 URL 中获取,如 `https://theinitium.com/author/ninghuilulu`',
language: '语言,简体`zh-hans`,繁体`zh-hant`,缺省为简体',
},
radar: [
{
source: ['theinitium.com/author/:type'],
target: '/author/:type',
},
],
handler,
example: '/theinitium/author/ninghuilulu/zh-hans',
categories: ['new-media'],
};
28 changes: 28 additions & 0 deletions lib/routes/theinitium/channel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Route } from '@/types';
import { processFeed } from './utils';

const handler = (ctx) => processFeed('channel', ctx);

export const route: Route = {
path: '/channel/:type?/:language?',
name: '专题・栏目',
maintainers: ['prnake'],
parameters: {
type: '栏目,缺省为最新',
language: '语言,简体`zh-hans`,繁体`zh-hant`,缺省为简体',
},
radar: [
{
source: ['theinitium.com/channel/:type'],
target: '/channel/:type',
},
],
handler,
example: '/theinitium/channel/latest/zh-hans',
categories: ['new-media'],
description: `Type 栏目:
| 最新 | 深度 | What’s New | 广场 | 科技 | 风物 | 特约 | ... |
| ------ | ------- | ---------- | ----------------- | ---------- | ------- | -------- | --- |
| latest | feature | news-brief | notes-and-letters | technology | culture | pick_up | ... |`,
};
48 changes: 48 additions & 0 deletions lib/routes/theinitium/follow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Route } from '@/types';
import { processFeed } from './utils';

const handler = (ctx) => processFeed('follow', ctx);

export const route: Route = {
path: '/follow/articles/:language?',
name: '个人订阅追踪动态',
maintainers: ['AgFlore'],
parameters: {
language: '语言,简体`zh-hans`,繁体`zh-hant`,缺省为简体',
},
radar: [
{
title: '作者',
source: ['theinitium.com/author/:type'],
target: '/author/:type',
},
],
handler,
example: '/theinitium/author/ninghuilulu/zh-hans',
categories: ['new-media'],
description: 'Web 版认证 token 和 iOS 内购回执认证 token 只需选择其一填入即可。你也可选择直接在环境设置中填写明文的用户名和密码',
features: {
requireConfig: [
{
name: 'INITIUM_BEARER_TOKEN',
optional: true,
description: `端传媒 Web 版认证 token。获取方式:登陆后打开端传媒站内任意页面,打开浏览器开发者工具中 “网络”(Network) 选项卡,筛选 URL 找到任一个地址为 \`api.initium.com\` 开头的请求,点击检查其 “消息头”,在 “请求头” 中找到Authorization字段,将其值复制填入配置即可。你的配置应该形如 \`INITIUM_BEARER_TOKEN: 'Bearer eyJxxxx......xx_U8'\`。使用 token 部署的好处是避免占据登陆设备数的额度,但这个 token 一般有效期为两周,因此只可作临时测试使用。`,
},
{
name: 'INITIUM_IAP_RECEIPT',
optional: true,
description: `端传媒 iOS 版内购回执认证 token。获取方式:登陆后打开端传媒 iOS app 内任意页面,打开抓包工具,筛选 URL 找到任一个地址为 \`api.initium.com\` 开头的请求,点击检查其 “消息头”,在 “请求头” 中找到 \`X-IAP-Receipt\` 字段,将其值复制填入配置即可。你的配置应该形如 \`INITIUM_IAP_RECEIPT: ef81dee9e4e2fe084a0af1ea82da2f7b16e75f756db321618a119fa62b52550e\`。`,
},
{
name: 'INITIUM_USERNAME',
optional: true,
description: `端传媒用户名 (邮箱)`,
},
{
name: 'INITIUM_PASSWORD',
optional: true,
description: `端传媒密码`,
},
],
},
};
23 changes: 23 additions & 0 deletions lib/routes/theinitium/tags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Route } from '@/types';
import { processFeed } from './utils';

const handler = (ctx) => processFeed('tags', ctx);

export const route: Route = {
path: '/tags/:type/:language?',
name: '话题・标签',
maintainers: ['AgFlore'],
parameters: {
type: '话题 ID,可从话题页 URL 中获取,如 `https://theinitium.com/tags/2019_10/`',
language: '语言,简体`zh-hans`,繁体`zh-hant`,缺省为简体',
},
radar: [
{
source: ['theinitium.com/tags/:type'],
target: '/tags/:type',
},
],
handler,
example: '/theinitium/tags/2019_10/zh-hans',
categories: ['new-media'],
};
33 changes: 19 additions & 14 deletions lib/routes/theinitium/full.ts → lib/routes/theinitium/utils.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
import { Route } from '@/types';
import { Context } from 'hono';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { config } from '@/config';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import { FetchError } from 'ofetch';

const TOKEN = 'Basic YW5vbnltb3VzOkdpQ2VMRWp4bnFCY1ZwbnA2Y0xzVXZKaWV2dlJRY0FYTHY=';

export const route: Route = {
path: '/:model?/:type?/:language?',
name: 'Unknown',
maintainers: [],
handler,
};

async function handler(ctx) {
export const processFeed = async (model: string, ctx: Context) => {
// model是channel/tag/etc.,而type是latest/feature/quest-academy这些一级栏目/标签/作者名的slug名。如果是追踪的话,那就是model是follow,type是articles。
const model = ctx.req.param('model') ?? 'channel';
const type = ctx.req.param('type') ?? 'latest';
const language = ctx.req.param('language') ?? 'zh-hans';
let listUrl;
@@ -38,6 +32,8 @@ async function handler(ctx) {
listUrl = `https://api.theinitium.com/api/v2/tag/articles/?language=${language}&slug=${type}`;
listLink = `https://theinitium.com/tags/${type}/`;
break;
default:
throw new InvalidParameterError('wrong model');
}

const key = {
@@ -92,9 +88,18 @@ async function handler(ctx) {
'X-IAP-Receipt': key.iapReceipt || '',
};

const response = await got(listUrl, {
headers,
});
let response;
try {
response = await got(listUrl, {
headers,
});
} catch (error) {
if (error instanceof FetchError && error.statusCode === 401) {
// 401 说明 token 过期了,将它删掉
await cache.set('initium:token', '');
}
throw error;
}

const name = response.data.name || (response.data[model] && response.data[model].name) || '追踪';
// 从v1直升的channel和tags里面是digests,v2新增的author和follow出来都是results
@@ -175,4 +180,4 @@ async function handler(ctx) {
item: items,
image,
};
}
};
2 changes: 1 addition & 1 deletion lib/routes/tiktok/templates/user.art
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{{ if useIframe }}
<iframe src="https://www.tiktok.com/embed/{{ id }}" height="757" frameborder="0" referrerpolicy="no-referrer"></iframe>
{{ else }}
<video controls loop poster="{{ poster }}" preload="none">
<video controls loop poster="{{ poster }}" preload="metadata">
<source src="{{ source }}">
</video>
{{ /if }}
121 changes: 121 additions & 0 deletions lib/routes/upc/jwc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// 导入必要的模组
import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import cache from '@/utils/cache';
import timezone from '@/utils/timezone';

const typeDict = {
tzgg: '', // 默认为所有通知
'18519': '教学·运行-', // 教学·运行
'18520': '学业·学籍-', // 学业·学籍
'18521': '教学·研究-', // 教学·研究
'18522': '课程·教材-', // 课程·教材
'18523': '实践·教学-', // 实践·教学
'18524': '创新·创业-', // 创新·创业
yywwz: '语言·文字-', // 语言·文字
jxwjy: '继续·教育-', // 继续·教育
bkwzs: '本科·招生-', // 本科·招生
};

// module.exports = async (ctx) => {
const handler = async (ctx) => {
// 从 URL 参数中获取通知分类
const { type = 'tzgg' } = ctx.req.param();
// console.log(type);
const baseUrl = 'https://jwc.upc.edu.cn';
const { data: response } = await got(`${baseUrl}/${type}/list.htm`);
// console.log(`${baseUrl}/${typeDict[type]}/list.htm`);
const $ = load(response);
// const listItems = $('ul.news_list').find('li');
// console.log(`List item count: ${listItems.length}`);
// const list = $('ul.news_list') 只会得到第一个li
const list = $('ul.news_list')
.find('li')
// 使用“toArray()”方法将选择的所有 DOM 元素以数组的形式返回。
.toArray()
// 使用“map()”方法遍历数组,并从每个元素中解析需要的数据。
.map((item) => {
// console.log(item);
item = $(item);
const a = item.find('a').first();
let linkStr = a.attr('href');
// 若链接不是以http开头,则加上前缀
if (a.attr('href').startsWith('http://')) {
// 改为https访问
linkStr.replace('http://', 'https://');
} else {
linkStr = `${baseUrl}${a.attr('href')}`;
}
return {
title: a.text(),
link: linkStr,
pubDate: timezone(parseDate(item.find('.news_meta').text()), +8), // 添加发布日期查询
};
});

const items = await Promise.all(
list.map((item) =>
cache.tryGet(item.link, async () => {
const { data: response } = await got(item.link);
const $ = load(response);
// 选择类名为“comment-body”的第一个元素
item.description = $('.read').first().html();
// item.pubDate = $('.arti_update').html() === null ? '' : $('.arti_update').html().slice(5, 15);
// item.publisher = $('.arti_publisher').html();
item.author = $('.arti_publisher').html();
// console.log($('.arti_update').html().slice(5, 15));
// 上面每个列表项的每个属性都在此重用,
// 并增加了一个新属性“description”
return item;
})
)
);

/* ctx.state.data = {
// 源标题
title: `${typeDict[type]}教务处通知-中国石油大学(华东)`,
// 源链接
link: `https://jwc.upc.edu.cn/tzgg/list.htm`,
// 源文章
item: items,
}; */

return {
// 源标题
title: `${typeDict[type]}教务处通知-中国石油大学(华东)`,
// 源链接
link: `${baseUrl}/${type}/list.htm`,
// 源文章
item: items,
};
};

export const route: Route = {
path: '/jwc/:type?',
categories: ['university'],
example: '/upc/jwc/tzgg',
parameters: { type: '分类,见下表,其值与对应网页url路径参数一致,默认为所有通知' },
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: true,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
radar: [
{
source: ['jwc.upc.edu.cn', 'jwc.upc.edu.cn/:type/list.htm'],
target: '/jwc/:type?',
},
],
name: '教务处通知公告',
maintainers: ['sddzhyc'],
description: `| 所有通知 | 教学·运行 | 学业·学籍 | 教学·研究 | 课程·教材 | 实践·教学 | 创新·创业 | 语言·文字 | 继续·教育 | 本科·招生 |
| -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
| tzgg | 18519 | 18520 | 18521 | 18522 | 18523 | 18524 | yywwz | jxwjy | bkwzs |`,
url: 'jwc.upc.edu.cn/tzgg/list.htm',
handler,
};
2 changes: 1 addition & 1 deletion lib/routes/zhihu/topic.ts
Original file line number Diff line number Diff line change
@@ -107,7 +107,7 @@ async function handler(ctx) {
case 'zvideo':
title = item.title;
description = `${item.description}<br>
<video controls poster="${item.video.thumbnail}" preload="none">
<video controls poster="${item.video.thumbnail}" preload="metadata">
<source src="${item.video.playlist.fhd?.url ?? item.video.playlist.hd?.url ?? item.video.playlist.ld?.url ?? item.video.playlist.sd?.url}" type="video/mp4">
</video>`;
link = item.url;
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -55,7 +55,7 @@
"@hono/zod-openapi": "0.11.0",
"@notionhq/client": "2.2.15",
"@postlight/parser": "2.2.3",
"@sentry/node": "7.112.2",
"@sentry/node": "7.113.0",
"@tonyrl/rand-user-agent": "2.0.61",
"aes-js": "3.1.2",
"art-template": "4.13.2",
@@ -74,7 +74,7 @@
"etag": "1.8.1",
"fanfou-sdk": "5.0.0",
"form-data": "4.0.0",
"googleapis": "135.1.0",
"googleapis": "136.0.0",
"hono": "4.2.9",
"html-to-text": "9.0.5",
"https-proxy-agent": "7.0.4",
64 changes: 32 additions & 32 deletions pnpm-lock.yaml