mirror of
https://github.com/DIYgod/RSSHub.git
synced 2025-12-02 01:58:11 +08:00
feat(route): add HelloGitHub (#11000)
* feat(route): add HelloGitHub * fix typo * fix: wrong templates * fix typo
This commit is contained in:
@@ -397,23 +397,58 @@ GitHub 官方也提供了一些 RSS:
|
||||
|
||||
## HelloGitHub
|
||||
|
||||
### 文章列表
|
||||
### 热门
|
||||
|
||||
<Route author="moke8" example="/hellogithub/article" path="/hellogithub/article"/>
|
||||
<Route author="nczitzk" example="/hellogithub/hot" path="/hellogithub/hot/:id?" :paramsDesc="['标签 id,可在对应标签页 URL 中找到,默认为全部标签']">
|
||||
|
||||
### 编程语言排行榜
|
||||
以下为部分标签:
|
||||
|
||||
<Route author="moke8" example="/hellogithub/ranking" path="/hellogithub/ranking/:type?" :paramsDesc="['分类,见下表']">
|
||||
| id | 标签 |
|
||||
| ---------- | ------ |
|
||||
| Z8PipJsHCX | Python |
|
||||
| YQHn0gERoi | C |
|
||||
| WTbsu5GAfC | CLI |
|
||||
| juBLV86qa5 | 机器学习 |
|
||||
| D4JBAUo967 | Rust |
|
||||
| dFA60uKLgr | GUI |
|
||||
| 0LByh3tjUO | 教程 |
|
||||
| 4lpGK0sUyk | Web 应用 |
|
||||
| yrZkGsUC9M | C++ |
|
||||
| mbP20HIEYD | Ruby |
|
||||
|
||||
| 编程语言排行 | 数据库排行 | 服务端语言排行 |
|
||||
| ------ | ----- | --------- |
|
||||
| tiobe | db | webserver |
|
||||
</Route>
|
||||
|
||||
### 最近
|
||||
|
||||
<Route author="nczitzk" example="/hellogithub/last" path="/hellogithub/last/:id?" :paramsDesc="['标签 id,可在对应标签页 URL 中找到,默认为全部标签']">
|
||||
|
||||
部分标签见上表
|
||||
|
||||
</Route>
|
||||
|
||||
### 文章
|
||||
|
||||
<Route author="moke8 nczitzk" example="/hellogithub/article" path="/hellogithub/article/:sort?/:id?" :paramsDesc="['排序方式,见下表,默认为 `hot`,即热门', '标签 id,可在对应标签页 URL 中找到,默认为全部标签']">
|
||||
|
||||
| 热门 | 最近 |
|
||||
| --- | ---- |
|
||||
| hot | last |
|
||||
|
||||
</Route>
|
||||
|
||||
### 排行榜
|
||||
|
||||
<Route author="moke8 nczitzk" example="/hellogithub/report" path="/hellogithub/report/:type?" :paramsDesc="['分类,见下表,默认为编程语言排行榜']">
|
||||
|
||||
| 编程语言 | 服务器 | 数据库 |
|
||||
| ----- | -------- | ---------- |
|
||||
| tiobe | netcraft | db-engines |
|
||||
|
||||
</Route>
|
||||
|
||||
### 月刊
|
||||
|
||||
<Route author="moke8" example="/hellogithub/month" path="/hellogithub/month"/>
|
||||
<Route author="moke8 nczitzk" example="/hellogithub/volume" path="/hellogithub/volume"/>
|
||||
|
||||
## Hex-Rays
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
const got = require('@/utils/got');
|
||||
const cheerio = require('cheerio');
|
||||
const { parseDate } = require('@/utils/parse-date');
|
||||
const timezone = require('@/utils/timezone');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const rootUrl = 'https://hellogithub.com';
|
||||
const currentUrl = 'https://hellogithub.com/article';
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: currentUrl,
|
||||
});
|
||||
const $ = cheerio.load(response.data);
|
||||
const list = $('.content .post')
|
||||
.slice(0, 10)
|
||||
.map((_, item) => {
|
||||
const a = $(item).find('a.post-title').eq(0).eq(0);
|
||||
const link = rootUrl + a.attr('href');
|
||||
return {
|
||||
title: a.text(),
|
||||
link,
|
||||
description: $(item).find('.post-description')[0].children[0].data,
|
||||
pubDate: timezone(parseDate($(item).find('.post-meta').text(), 'YYYY-MM-DD HH:mm:ss'), +8),
|
||||
};
|
||||
})
|
||||
.get();
|
||||
|
||||
const items = await Promise.all(
|
||||
list.map((item) =>
|
||||
ctx.cache.tryGet(item.link, async () => {
|
||||
const detailResponse = await got({
|
||||
method: 'get',
|
||||
url: item.link,
|
||||
});
|
||||
const content = cheerio.load(detailResponse.data);
|
||||
item.description = content('.markdown-body').html();
|
||||
return item;
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
ctx.state.data = {
|
||||
title: 'HelloGitHub - Article',
|
||||
link: currentUrl,
|
||||
item: items,
|
||||
};
|
||||
};
|
||||
102
lib/v2/hellogithub/index.js
Normal file
102
lib/v2/hellogithub/index.js
Normal file
@@ -0,0 +1,102 @@
|
||||
const got = require('@/utils/got');
|
||||
const cheerio = require('cheerio');
|
||||
const { parseDate } = require('@/utils/parse-date');
|
||||
const { art } = require('@/utils/render');
|
||||
const path = require('path');
|
||||
|
||||
const sorts = {
|
||||
hot: '热门',
|
||||
last: '最近',
|
||||
};
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const sort = ctx.params.sort ?? 'hot';
|
||||
const id = ctx.params.id ?? '';
|
||||
const limit = ctx.query.limit ? parseInt(ctx.query.limit) : 20;
|
||||
|
||||
const rootUrl = 'https://hellogithub.com';
|
||||
const apiRootUrl = 'https://api.hellogithub.com';
|
||||
const currentUrl = `${rootUrl}/?sort_by=${sort}${id ? `&tid=${id}` : ''}`;
|
||||
const apiUrl = `${apiRootUrl}/v1/?sort_by=${sort}${id ? `&tid=${id}` : ''}&page=1`;
|
||||
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: apiUrl,
|
||||
});
|
||||
|
||||
let buildId, tag;
|
||||
if (id) {
|
||||
const tagUrl = `${rootUrl}/tags/${id}`;
|
||||
|
||||
const tagResponse = await got({
|
||||
method: 'get',
|
||||
url: tagUrl,
|
||||
});
|
||||
|
||||
const $ = cheerio.load(tagResponse.data);
|
||||
|
||||
tag = $('meta[property="og:title"]').attr('content').split(' ').pop();
|
||||
buildId = tagResponse.data.match(/"buildId":"(.*?)",/)[1];
|
||||
}
|
||||
|
||||
if (!buildId) {
|
||||
const buildResponse = await got({
|
||||
method: 'get',
|
||||
url: rootUrl,
|
||||
});
|
||||
|
||||
buildId = buildResponse.data.match(/"buildId":"(.*?)",/)[1];
|
||||
}
|
||||
|
||||
let items = response.data.data.slice(0, limit).map((item) => ({
|
||||
guid: item.item_id,
|
||||
title: item.title,
|
||||
author: item.author,
|
||||
link: `${rootUrl}/repository/${item.item_id}`,
|
||||
description: item.description,
|
||||
pubDate: parseDate(item.updated_at),
|
||||
}));
|
||||
|
||||
items = await Promise.all(
|
||||
items.map((item) =>
|
||||
ctx.cache.tryGet(item.link, async () => {
|
||||
const detailUrl = `${rootUrl}/_next/data/${buildId}/repository/${item.guid}.json`;
|
||||
|
||||
const detailResponse = await got({
|
||||
method: 'get',
|
||||
url: detailUrl,
|
||||
});
|
||||
|
||||
const data = detailResponse.data.pageProps.repo;
|
||||
|
||||
item.title = `${data.name}: ${data.title}`;
|
||||
item.category = [`No.${data.volume_name}`, ...data.tags.map((t) => t.name)];
|
||||
item.description = art(path.join(__dirname, 'templates/description.art'), {
|
||||
name: data.full_name,
|
||||
description: data.description,
|
||||
summary: data.summary,
|
||||
image: data.image_url,
|
||||
stars: data.stars ?? data.stars_str,
|
||||
isChinese: data.has_chinese,
|
||||
language: data.primary_lang,
|
||||
isActive: data.is_active,
|
||||
license: data.license,
|
||||
isOrganization: data.is_org,
|
||||
forks: data.forks,
|
||||
openIssues: data.open_issues,
|
||||
subscribers: data.subscribers,
|
||||
homepage: data.homepage,
|
||||
url: data.url,
|
||||
});
|
||||
|
||||
return item;
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
ctx.state.data = {
|
||||
title: `HelloGithub - ${sorts[sort]}${tag || ''}项目`,
|
||||
link: currentUrl,
|
||||
item: items,
|
||||
};
|
||||
};
|
||||
@@ -1,5 +1,9 @@
|
||||
module.exports = {
|
||||
'/article': ['moke8'],
|
||||
'/ranking/:type?': ['moke8'],
|
||||
'/month': ['moke8'],
|
||||
'/article/:sort?/:id?': ['moke8', 'nczitzk'],
|
||||
'/hot/:id?': ['nczitzk'],
|
||||
'/last/:id?': ['nczitzk'],
|
||||
'/month': ['moke8', 'nczitzk'],
|
||||
'/ranking/:type?': ['moke8', 'nczitzk'],
|
||||
'/report/:type?': ['nczitzk'],
|
||||
'/volume': ['nczitzk'],
|
||||
};
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
const got = require('@/utils/got');
|
||||
const cheerio = require('cheerio');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const rootUrl = 'https://hellogithub.com';
|
||||
const currentUrl = 'https://hellogithub.com/article';
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: rootUrl,
|
||||
});
|
||||
const $ = cheerio.load(response.data);
|
||||
let count = $('.pricing-table-price').eq(0).text();
|
||||
count = count.replace(/[^0-9]/gi, '');
|
||||
let start = count - 10;
|
||||
const PromiseArr = [];
|
||||
for (start; start <= count; start++) {
|
||||
PromiseArr.push(
|
||||
new Promise((resolve) => {
|
||||
const link = `https://hellogithub.com/periodical/volume/${start}/`;
|
||||
const issueNum = start;
|
||||
return ctx.cache.tryGet(link, async () => {
|
||||
const detailResponse = await got({
|
||||
method: 'get',
|
||||
url: link,
|
||||
});
|
||||
const $ = cheerio.load(detailResponse.data);
|
||||
return resolve({
|
||||
title: '第' + issueNum + '期',
|
||||
link,
|
||||
description: $('.content').html(),
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
const items = await Promise.all(PromiseArr);
|
||||
ctx.state.data = {
|
||||
title: 'HelloGitHub - Article',
|
||||
link: currentUrl,
|
||||
item: items,
|
||||
};
|
||||
};
|
||||
@@ -3,22 +3,46 @@ module.exports = {
|
||||
_name: 'HelloGitHub',
|
||||
'.': [
|
||||
{
|
||||
title: '文章列表',
|
||||
docs: 'https://docs.rsshub.app/programming.html#hellogithub',
|
||||
source: ['/article', '/article/?url=/periodical/volume/'],
|
||||
target: '/hellogithub/article',
|
||||
title: '热门',
|
||||
docs: 'https://docs.rsshub.app/programming.html#hellogithub-re-men',
|
||||
source: ['/'],
|
||||
target: (params, url) => {
|
||||
const sort = new URL(url).searchParams.get('sort_by');
|
||||
const id = new URL(url).searchParams.get('tid');
|
||||
return `/hellogithub${sort ? `/sort` : ''}${id ? `/id` : ''}`;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '编程语言排行榜',
|
||||
docs: 'https://docs.rsshub.app/programming.html#hellogithub',
|
||||
source: '/report/:type/?url=/periodical/volume/',
|
||||
target: '/hellogithub/ranking/:type',
|
||||
title: '最近',
|
||||
docs: 'https://docs.rsshub.app/programming.html#hellogithub-zui-jin',
|
||||
source: ['/'],
|
||||
target: (params, url) => {
|
||||
const sort = new URL(url).searchParams.get('sort_by');
|
||||
const id = new URL(url).searchParams.get('tid');
|
||||
return `/hellogithub${sort ? `/sort` : ''}${id ? `/id` : ''}`;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '文章',
|
||||
docs: 'https://docs.rsshub.app/programming.html#hellogithub-wen-zhang',
|
||||
source: ['/'],
|
||||
target: (params, url) => {
|
||||
const sort = new URL(url).searchParams.get('sort_by');
|
||||
const id = new URL(url).searchParams.get('tid');
|
||||
return `/hellogithub/article${sort ? `/sort` : ''}${id ? `/id` : ''}`;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '排行榜',
|
||||
docs: 'https://docs.rsshub.app/programming.html#hellogithub-pai-hang-bang',
|
||||
source: ['/report/:type', '/'],
|
||||
target: '/hellogithub/report/:type',
|
||||
},
|
||||
{
|
||||
title: '月刊',
|
||||
docs: 'https://docs.rsshub.app/programming.html#hellogithub',
|
||||
source: '/periodical/volume/',
|
||||
target: '/hellogithub/month',
|
||||
docs: 'https://docs.rsshub.app/programming.html#hellogithub-yue-kan',
|
||||
source: ['/periodical/volume/:id', '/'],
|
||||
target: '/hellogithub/volume',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
const got = require('@/utils/got');
|
||||
const cheerio = require('cheerio');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
let type;
|
||||
switch (ctx.params.type) {
|
||||
case 'db':
|
||||
type = 'db-engines';
|
||||
break;
|
||||
case 'webserver':
|
||||
type = 'netcraft';
|
||||
break;
|
||||
default:
|
||||
type = 'tiobe';
|
||||
break;
|
||||
}
|
||||
const rootUrl = 'https://hellogithub.com/report/' + type;
|
||||
ctx.state.data = {
|
||||
title: 'HelloGitHub - ranking',
|
||||
link: rootUrl,
|
||||
item: await ctx.cache.tryGet(rootUrl, async () => {
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: rootUrl,
|
||||
});
|
||||
const $ = cheerio.load(response.data);
|
||||
const content = $('.content');
|
||||
const title = $('.header').find('h1');
|
||||
|
||||
return [
|
||||
{
|
||||
title: title.text(),
|
||||
link: rootUrl,
|
||||
description: content.html(),
|
||||
},
|
||||
];
|
||||
}),
|
||||
};
|
||||
};
|
||||
56
lib/v2/hellogithub/report.js
Normal file
56
lib/v2/hellogithub/report.js
Normal file
@@ -0,0 +1,56 @@
|
||||
const got = require('@/utils/got');
|
||||
const { parseDate } = require('@/utils/parse-date');
|
||||
const { art } = require('@/utils/render');
|
||||
const path = require('path');
|
||||
|
||||
const types = {
|
||||
tiobe: '编程语言',
|
||||
netcraft: '服务器',
|
||||
'db-engines': '数据库',
|
||||
};
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
let type = ctx.params.type ?? 'tiobe';
|
||||
|
||||
type = type === 'webserver' ? 'netcraft' : type === 'db' ? 'db-engines' : type;
|
||||
|
||||
const rootUrl = 'https://hellogithub.com';
|
||||
const currentUrl = `${rootUrl}/report/${type}`;
|
||||
|
||||
const buildResponse = await got({
|
||||
method: 'get',
|
||||
url: rootUrl,
|
||||
});
|
||||
|
||||
const buildId = buildResponse.data.match(/"buildId":"(.*?)",/)[1];
|
||||
|
||||
const apiUrl = `${rootUrl}/_next/data/${buildId}/report/${type}.json`;
|
||||
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: apiUrl,
|
||||
});
|
||||
|
||||
const data = response.data.pageProps;
|
||||
|
||||
const items = [
|
||||
{
|
||||
guid: `${type}:${data.year}${data.month}`,
|
||||
title: `${data.year}年${data.month}月${types[type]}排行榜`,
|
||||
link: currentUrl,
|
||||
pubDate: parseDate(`${data.year}-${data.month}`, 'YYYY-M'),
|
||||
description: art(path.join(__dirname, 'templates/report.art'), {
|
||||
tiobe_list: type === 'tiobe' ? data.list : undefined,
|
||||
active_list: data.active_list,
|
||||
all_list: data.all_list,
|
||||
db_list: type === 'db-engines' ? data.list : undefined,
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
ctx.state.data = {
|
||||
title: `HelloGitHub - ${types[type]}排行榜`,
|
||||
link: currentUrl,
|
||||
item: items,
|
||||
};
|
||||
};
|
||||
@@ -1,5 +1,8 @@
|
||||
module.exports = (router) => {
|
||||
router.get('/article', require('./article'));
|
||||
router.get('/month', require('./month'));
|
||||
router.get('/ranking/:type?', require('./ranking'));
|
||||
router.get('/article/:sort?/:id?', require('./index'));
|
||||
router.get('/month', require('./volume'));
|
||||
router.get('/ranking/:type?', require('./report'));
|
||||
router.get('/report/:type?', require('./report'));
|
||||
router.get('/volume', require('./volume'));
|
||||
router.get('/:sort?/:id?', require('./index'));
|
||||
};
|
||||
|
||||
99
lib/v2/hellogithub/templates/description.art
Normal file
99
lib/v2/hellogithub/templates/description.art
Normal file
@@ -0,0 +1,99 @@
|
||||
{{ if image }}
|
||||
<figure>
|
||||
<img src="{{ image }}">
|
||||
</figure>
|
||||
{{ /if }}
|
||||
<table>
|
||||
<tbody>
|
||||
{{ if homepage }}
|
||||
<tr>
|
||||
<th>Homepage</th>
|
||||
<td>{{ homepage }}</td>
|
||||
</tr>
|
||||
{{ /if }}
|
||||
{{ if name && url }}
|
||||
<tr>
|
||||
<th>GitHub Repo</th>
|
||||
<td><a href="{{ url }}">{{ name }}</a></td>
|
||||
</tr>
|
||||
{{ /if }}
|
||||
{{ if description }}
|
||||
<tr>
|
||||
<th>Description</th>
|
||||
<td>{{ description }}</td>
|
||||
</tr>
|
||||
{{ /if }}
|
||||
{{ if summary }}
|
||||
<tr>
|
||||
<th>Summary</th>
|
||||
<td>{{ summary }}</td>
|
||||
</tr>
|
||||
{{ /if }}
|
||||
{{ if stars }}
|
||||
<tr>
|
||||
<th>Stars</th>
|
||||
<td>{{ stars }}</td>
|
||||
</tr>
|
||||
{{ /if }}
|
||||
{{ if forks }}
|
||||
<tr>
|
||||
<th>Forks</th>
|
||||
<td>{{ forks }}</td>
|
||||
</tr>
|
||||
{{ /if }}
|
||||
{{ if subscribers }}
|
||||
<tr>
|
||||
<th>Subscribers</th>
|
||||
<td>{{ subscribers }}</td>
|
||||
</tr>
|
||||
{{ /if }}
|
||||
{{ if language }}
|
||||
<tr>
|
||||
<th>Language</th>
|
||||
<td>{{ language }}</td>
|
||||
</tr>
|
||||
{{ /if }}
|
||||
{{ if license }}
|
||||
<tr>
|
||||
<th>License</th>
|
||||
<td>{{ license }}</td>
|
||||
</tr>
|
||||
{{ /if }}
|
||||
<tr>
|
||||
<th>Is in Chinese</th>
|
||||
<td>
|
||||
{{ if isChinese }}
|
||||
Yes
|
||||
{{ else }}
|
||||
No
|
||||
{{ /if }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Is Organization</th>
|
||||
<td>
|
||||
{{ if isOrganization }}
|
||||
Yes
|
||||
{{ else }}
|
||||
No
|
||||
{{ /if }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Is Active</th>
|
||||
<td>
|
||||
{{ if isActive }}
|
||||
Yes
|
||||
{{ else }}
|
||||
No
|
||||
{{ /if }}
|
||||
</td>
|
||||
</tr>
|
||||
{{ if openIssues }}
|
||||
<tr>
|
||||
<th>Open Issues</th>
|
||||
<td>{{ openIssues }}</td>
|
||||
</tr>
|
||||
{{ /if }}
|
||||
</tbody>
|
||||
</table>
|
||||
118
lib/v2/hellogithub/templates/report.art
Normal file
118
lib/v2/hellogithub/templates/report.art
Normal file
@@ -0,0 +1,118 @@
|
||||
{{ if tiobe_list }}
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>排名</th>
|
||||
<th>编程语言</th>
|
||||
<th>流行度</th>
|
||||
<th>对比上月</th>
|
||||
<th>年度明星语言</th>
|
||||
</tr>
|
||||
{{ each tiobe_list l }}
|
||||
<tr>
|
||||
<td>{{ l.position }}</td>
|
||||
<td>{{ l.name }}</td>
|
||||
<td>{{ l.rating }}</td>
|
||||
<td>
|
||||
{{ if l.change }}
|
||||
{{ l.change }}
|
||||
{{ else }}
|
||||
新上榜
|
||||
{{ /if }}
|
||||
</td>
|
||||
<td>{{ l.star }}</td>
|
||||
</tr>
|
||||
{{ /each }}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ /if }}
|
||||
|
||||
{{ if all_list }}
|
||||
<h1>市场份额排名</h1>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>排名</th>
|
||||
<th>服务器</th>
|
||||
<th>占比</th>
|
||||
<th>对比上月</th>
|
||||
<th>总数</th>
|
||||
</tr>
|
||||
{{ each all_list l }}
|
||||
<tr>
|
||||
<td>{{ l.position }}</td>
|
||||
<td>{{ l.name }}</td>
|
||||
<td>{{ l.rating }}</td>
|
||||
<td>
|
||||
{{ if l.change }}
|
||||
{{ l.change }}
|
||||
{{ else }}
|
||||
新上榜
|
||||
{{ /if }}
|
||||
</td>
|
||||
<td>{{ l.total }}</td>
|
||||
</tr>
|
||||
{{ /each }}
|
||||
</tbody>
|
||||
</table>
|
||||
<br>
|
||||
{{ /if }}
|
||||
|
||||
{{ if active_list }}
|
||||
<h1>活跃网站排名</h1>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>排名</th>
|
||||
<th>服务器</th>
|
||||
<th>占比</th>
|
||||
<th>对比上月</th>
|
||||
<th>总数</th>
|
||||
</tr>
|
||||
{{ each active_list l }}
|
||||
<tr>
|
||||
<td>{{ l.position }}</td>
|
||||
<td>{{ l.name }}</td>
|
||||
<td>{{ l.rating }}</td>
|
||||
<td>
|
||||
{{ if l.change }}
|
||||
{{ l.change }}
|
||||
{{ else }}
|
||||
新上榜
|
||||
{{ /if }}
|
||||
</td>
|
||||
<td>{{ l.total }}</td>
|
||||
</tr>
|
||||
{{ /each }}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ /if }}
|
||||
|
||||
{{ if db_list }}
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>排名</th>
|
||||
<th>数据库</th>
|
||||
<th>分数</th>
|
||||
<th>对比上月</th>
|
||||
<th>类型</th>
|
||||
</tr>
|
||||
{{ each db_list l }}
|
||||
<tr>
|
||||
<td>{{ l.position }}</td>
|
||||
<td>{{ l.name }}</td>
|
||||
<td>{{ l.rating }}</td>
|
||||
<td>
|
||||
{{ if l.change }}
|
||||
{{ l.change }}
|
||||
{{ else }}
|
||||
新上榜
|
||||
{{ /if }}
|
||||
</td>
|
||||
<td>{{ l.db_model }}</td>
|
||||
</tr>
|
||||
{{ /each }}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ /if }}
|
||||
36
lib/v2/hellogithub/templates/volume.art
Normal file
36
lib/v2/hellogithub/templates/volume.art
Normal file
@@ -0,0 +1,36 @@
|
||||
{{ if data }}
|
||||
{{ each data d }}
|
||||
<div>
|
||||
<h1>{{ d.category_name }}</h1>
|
||||
{{ each d.items item }}
|
||||
<div>
|
||||
<h2>
|
||||
<a href="{{ item.github_url }}">{{ item.name }}</a>
|
||||
</h2>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Stars</th>
|
||||
<td>{{ item.stars }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Forks</th>
|
||||
<td>{{ item.forks }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Watch</th>
|
||||
<td>{{ item.watch }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>{{@ item.description | render }}</p>
|
||||
{{ if item.image_url }}
|
||||
<figure>
|
||||
<img src="{{ item.image_url }}">
|
||||
</figure>
|
||||
{{ /if }}
|
||||
</div>
|
||||
{{ /each }}
|
||||
</div>
|
||||
{{ /each }}
|
||||
{{ /if }}
|
||||
66
lib/v2/hellogithub/volume.js
Normal file
66
lib/v2/hellogithub/volume.js
Normal file
@@ -0,0 +1,66 @@
|
||||
const got = require('@/utils/got');
|
||||
const { parseDate } = require('@/utils/parse-date');
|
||||
const { art } = require('@/utils/render');
|
||||
const path = require('path');
|
||||
const md = require('markdown-it')({
|
||||
html: true,
|
||||
});
|
||||
|
||||
art.defaults.imports.render = function (string) {
|
||||
return md.render(string);
|
||||
};
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const limit = ctx.query.limit ? parseInt(ctx.query.limit) : 30;
|
||||
|
||||
const rootUrl = 'https://hellogithub.com';
|
||||
const apiRootUrl = 'https://api.hellogithub.com';
|
||||
const currentUrl = `${rootUrl}/periodical/volume/`;
|
||||
const apiUrl = `${apiRootUrl}/v1/volume/all/`;
|
||||
|
||||
const buildResponse = await got({
|
||||
method: 'get',
|
||||
url: rootUrl,
|
||||
});
|
||||
|
||||
const buildId = buildResponse.data.match(/"buildId":"(.*?)",/)[1];
|
||||
|
||||
const response = await got({
|
||||
method: 'get',
|
||||
url: apiUrl,
|
||||
});
|
||||
|
||||
let items = response.data.data.slice(0, limit).map((item) => ({
|
||||
guid: item.num,
|
||||
title: `No.${item.num}`,
|
||||
link: `${rootUrl}/periodical/volume/${item.guid}`,
|
||||
}));
|
||||
|
||||
items = await Promise.all(
|
||||
items.map((item) =>
|
||||
ctx.cache.tryGet(item.link, async () => {
|
||||
const detailUrl = `${rootUrl}/_next/data/${buildId}/periodical/volume/${item.guid}.json`;
|
||||
|
||||
const detailResponse = await got({
|
||||
method: 'get',
|
||||
url: detailUrl,
|
||||
});
|
||||
|
||||
const data = detailResponse.data;
|
||||
|
||||
item.pubDate = parseDate(data.pageProps.volume.publish_at);
|
||||
item.description = art(path.join(__dirname, 'templates/volume.art'), {
|
||||
data: data.pageProps.volume.data,
|
||||
});
|
||||
|
||||
return item;
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
ctx.state.data = {
|
||||
title: 'HelloGithub - 月刊',
|
||||
link: currentUrl,
|
||||
item: items,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user