rsshub
Version:
Make RSS Great Again!
150 lines (121 loc) • 5.31 kB
text/typescript
import { Route, ViewType } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
export const route: Route = {
path: '/:section?/:type?/:user?',
categories: ['programming'],
view: ViewType.Articles,
example: '/hackernews/threads/comments_list/dang',
parameters: {
section: {
description: 'Content section, default to `index`',
},
type: {
description: 'Link type, default to `sources`',
},
user: {
description: 'Set user, only valid in `threads` and `submitted` sections',
},
},
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
radar: [
{
source: ['news.ycombinator.com/:section', 'news.ycombinator.com/'],
},
],
name: 'User',
maintainers: ['nczitzk', 'xie-dongping'],
handler,
description: `Subscribe to the content of a specific user`,
};
async function handler(ctx) {
const section = ctx.req.param('section') ?? 'index';
const type = ctx.req.param('type') ?? 'sources';
const user = ctx.req.param('user') ?? '';
const rootUrl = 'https://news.ycombinator.com';
const sectionUrl = section === 'index' ? '' : `/${section}`;
let optUrl = user === '' ? '' : '?id=' + user;
if (section === 'over') {
optUrl = user === '' ? '?points=100' : '?points=' + user;
}
const currentUrl = `${rootUrl}${sectionUrl}${optUrl}`;
const response = await got(currentUrl);
const $ = load(response.data);
const list = $('.athing')
.slice(0, ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 30)
.toArray()
.map((thing) => {
thing = $(thing);
const item = {};
item.guid = thing.attr('id');
item.title = thing.find('.titleline').children('a').text();
item.category = thing.find('.sitestr').text();
item.author = thing.next().find('.hnuser').text();
item.pubDate = parseDate(thing.find('.age').attr('title') ?? thing.next().find('.age').attr('title'));
item.link = `${rootUrl}/item?id=${item.guid}`;
item.origin = thing.find('.titleline').children('a').attr('href');
item.onStory = thing.find('.onstory').text().slice(2);
item.comments = thing.next().find('a').last().text().split(' comment')[0];
item.upvotes = thing.next().find('.score').text().split(' point')[0];
item.currentComment = thing.find('.comment').text();
item.guid = type === 'sources' ? item.guid : `${item.guid}${item.comments === 'discuss' ? '' : `-${item.comments}`}`;
item.description = `<a href="${item.link}">Comments on Hacker News</a> | <a href="${item.origin}">Source</a>`;
return item;
});
const items = await Promise.all(
list.map((item) =>
cache.tryGet(item.guid, async () => {
if (item.comments !== 'discuss' && type === 'comments') {
const detailResponse = await got({
method: 'get',
url: item.link,
});
const content = load(detailResponse.data);
content('.reply').remove();
item.description = '';
content('.comtr').each(function () {
const author = content(this).find('.hnuser');
const comment = content(this).find('.commtext');
item.description +=
`<div><div><small><a href="${rootUrl}/${author.attr('href')}">${author.text()}</a></small>` +
`  <small><a href="${rootUrl}/item?id=${content(this).attr('id')}">` +
`${content(this).find('.age').attr('title')}</a></small></div>`;
const commentText = comment.clone();
commentText.find('p').remove();
commentText.html(`<p>${commentText.text()}</p>`);
commentText.append(
comment
.find('p')
.toArray()
.map((p) => `<p>${content(p).html()}</p>`)
);
item.description += `<div>${commentText.html()}</div></div>`;
});
} else if (item.comments !== 'discuss' && type === 'comments_list') {
item.title = item.onStory;
item.description = item.currentComment;
}
if (Number.isNaN(item.comments)) {
item.comments = 0;
}
item.link = type === 'sources' ? item.origin : item.link;
delete item.origin;
return item;
})
)
);
return {
title: $('title').text(),
link: currentUrl,
item: items,
};
}