UNPKG

rsshub

Version:
119 lines (107 loc) 4.57 kB
import { Route } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; import { art } from '@/utils/render'; import { parseDate } from '@/utils/parse-date'; import path from 'node:path'; const baseUrl = 'https://seekingalpha.com'; export const route: Route = { path: '/:symbol/:category?', categories: ['finance'], example: '/seekingalpha/TSM/transcripts', parameters: { symbol: 'Stock symbol', category: 'Category, see below, `news` by default' }, features: { antiCrawler: true, }, radar: [ { source: ['seekingalpha.com/symbol/:symbol/:category', 'seekingalpha.com/symbol/:symbol/earnings/:category'], target: '/:symbol/:category', }, ], name: 'Summary', maintainers: ['TonyRL'], handler, description: `| Analysis | News | Transcripts | Press Releases | Related Analysis | | -------- | ---- | ----------- | -------------- | ---------------- | | analysis | news | transcripts | press-releases | related-analysis |`, }; const getMachineCookie = () => cache.tryGet('seekingalpha:machine_cookie', async () => { const response = await ofetch.raw(baseUrl); return response.headers.getSetCookie().map((c) => c.split(';')[0]); }); const apiParams = { article: { slug: '/articles', include: 'author,primaryTickers,secondaryTickers,otherTags,presentations,presentations.slides,author.authorResearch,author.userBioTags,co_authors,promotedService,sentiments', }, news: { slug: '/news', include: 'author,primaryTickers,secondaryTickers,otherTags', }, pr: { slug: '/press_releases', include: 'acquireService,primaryTickers', }, }; async function handler(ctx) { const { category = 'news', symbol } = ctx.req.param(); const pageUrl = `${baseUrl}/symbol/${symbol.toUpperCase()}/${category === 'transcripts' ? `earnings/${category}` : category}`; const machineCookie = await getMachineCookie(); const response = await ofetch(`${baseUrl}/api/v3/symbols/${symbol.toUpperCase()}/${category}`, { headers: { cookie: machineCookie.join('; '), }, query: { 'filter[since]': 0, 'filter[until]': 0, id: symbol.toLowerCase(), include: 'author,primaryTickers,secondaryTickers,sentiments', 'page[size]': ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : category === 'news' ? 40 : 20, 'page[number]': 1, }, }); const list = response.data?.map((item) => ({ title: item.attributes.title, link: new URL(item.links.self, baseUrl).href, pubDate: parseDate(item.attributes.publishOn), author: response.included.find((i) => i.id === item.relationships.author.data.id).attributes.nick, id: item.id, articleType: item.links.self.split('/')[1], })); const items = list ? await Promise.all( list.map((item) => cache.tryGet(item.link, async () => { const response = await ofetch(`${baseUrl}/api/v3${apiParams[item.articleType].slug}/${item.id}`, { headers: { cookie: machineCookie.join('; '), }, query: { include: apiParams[item.articleType].include, }, }); item.category = response.included.filter((i) => i.type === 'tag').map((i) => (i.attributes.company ? `${i.attributes.company} (${i.attributes.name})` : i.attributes.name)); item.description = (response.data.attributes.summary?.length ? art(path.join(__dirname, 'templates/summary.art'), { summary: response.data.attributes.summary, }) : '') + response.data.attributes.content; item.updated = parseDate(response.data.attributes.lastModified); return item; }) ) ) : []; return { title: response.meta.page.title, description: response.meta.page.description, link: pageUrl, image: 'https://seekingalpha.com/samw/static/images/favicon.svg', item: items, allowEmpty: true, language: 'en-US', }; }