UNPKG

rsshub

Version:
152 lines (142 loc) 5.02 kB
import { Route } from '@/types'; import got from '@/utils/got'; import { config } from '@/config'; import { processImage } from './utils'; import { parseDate } from '@/utils/parse-date'; import ConfigNotFoundError from '@/errors/types/config-not-found'; export const route: Route = { path: '/timeline', categories: ['social-media'], example: '/zhihu/timeline', parameters: {}, features: { requireConfig: [ { name: 'ZHIHU_COOKIES', description: '', }, ], requirePuppeteer: false, antiCrawler: true, supportBT: false, supportPodcast: false, supportScihub: false, }, name: '用户关注时间线', maintainers: ['SeanChao'], handler, description: `::: warning 用户关注动态需要登录后的 Cookie 值,所以只能自建,详情见部署页面的配置模块。 :::`, }; async function handler(ctx) { const cookie = config.zhihu.cookies; if (cookie === undefined) { throw new ConfigNotFoundError('缺少知乎用户登录后的 Cookie 值'); } const response = await got({ method: 'get', url: `https://www.zhihu.com/api/v3/moments`, headers: { Cookie: cookie, }, searchParams: { desktop: true, limit: ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 15, }, }); const feeds = response.data.data; const urlBase = 'https://zhihu.com'; const buildLink = (e) => { if (!e || !e.target || !e.target.type) { return ''; } const id = e.target.id; switch (e.target.type) { case 'answer': { const questionId = e.target.question.id; return `${urlBase}/question/${questionId}/answer/${id}`; } case 'pin': case 'article': return e.target.url; case 'question': return `${urlBase}/question/${id}`; default: return; } }; /** * Returns one non-undefined/null element from a list of items * make sure there exists at least one element that is non-undefined * * @param {Array} list a list of items to be filtered */ const getOne = (list) => list.find((e) => e !== undefined && e !== null); const buildActors = (e) => { const actors = e.actors; if (!actors) { return ''; } return actors.map((e) => e.name).join(', '); }; const getContent = (content) => { if (!content || !Array.isArray(content)) { return content; } // content can be a string or an array of objects return ( content .map((e) => e.content) .filter((e) => !!e && typeof e === 'string') // some content may not be wrapped in tag, it will cause error when parsing .map((e) => `<div>${e}</div>`) .join('') ); }; const buildItem = (e) => { if (!e || !e.target) { return {}; } const link = buildLink(e); return { title: `${e.action_text_tpl.replace('{}', buildActors(e))}: ${getOne([e.target.title, e.target.question ? e.target.question.title : ''])}`, description: processImage(`<div>${getOne([e.target.content_html, getContent(e.target.content), e.target.detail, e.target.excerpt, ''])}</div>`), pubDate: parseDate(e.updated_time * 1000), link, author: e.target.author ? e.target.author.name : '', guid: link, category: [e.verb], }; }; const out = feeds .filter((e) => e.verb) .map((e) => { if (e && e.type && e.type === 'feed_group') { // A feed group contains a list of feeds whose structure is the same as a single feed const title = e.group_text.replace('{LIST_COUNT}', e.list.length); const description = e.list && Array.isArray(e.list) ? e.list .map((e) => buildItem(e)) .map((e) => `<a href="${e.link}"><b>${e.title}</b></a><br><p>${e.description}</p><br>`) .join('') : ''; const pubDate = e.list && Array.isArray(e.list) && e.list.length > 0 ? parseDate(e.list[0].updated_time * 1000) : new Date(); const guid = e.link; return { title, description, pubDate, guid, category: [e.verb], }; } return buildItem(e); }); return { title: `知乎关注动态`, link: `https://www.zhihu.com/follow`, item: out, }; }