UNPKG

rsshub

Version:
99 lines (89 loc) 3.38 kB
import { Route, ViewType } from '@/types'; import { parseDate } from '@/utils/parse-date'; import sanitizeHtml from 'sanitize-html'; import puppeteer from '@/utils/puppeteer'; import logger from '@/utils/logger'; import cache from '@/utils/cache'; import { config } from '@/config'; export const route: Route = { path: '/user/:id', categories: ['social-media'], example: '/sotwe/user/_RSSHub', parameters: { id: 'Twitter username', }, features: { requireConfig: false, requirePuppeteer: true, antiCrawler: true, supportBT: false, supportPodcast: false, supportScihub: false, }, radar: [ { source: ['www.sotwe.com/:id'], target: '/user/:id', }, ], name: 'User timeline - Sotwe', maintainers: ['TonyRL'], handler, view: ViewType.Pictures, }; const renderMedia = (mediaEntities) => mediaEntities .map((e) => { switch (e.type) { case 'photo': return `<img src="${e.mediaURL}">`; case 'video': { const video = e.videoInfo.variants.filter((v) => v.type === 'video/mp4').sort((a, b) => b.bitrate - a.bitrate)[0]; return `<video controls preload="metadata" poster="${e.mediaURL}"><source src="${video.url}" type="video/mp4"></video>`; } default: return ''; } }) .join('<br>'); const renderDescription = (item) => `${renderMedia(item.mediaEntities)}<br>${item.text.replaceAll('\n', '<br>')}${item.quotedStatus ? `<br>${renderDescription(item.quotedStatus)}` : ''}${item.retweetedStatus ? `<br>${renderDescription(item.retweetedStatus)}` : ''}`; async function handler(ctx) { const baseUrl = 'https://www.sotwe.com'; const { id } = ctx.req.param(); const data = await cache.tryGet( `sotwe:user:${id}`, async () => { const browser = await puppeteer(); const page = await browser.newPage(); await page.setRequestInterception(true); page.on('request', (request) => { ['document', 'script', 'xhr', 'fetch'].includes(request.resourceType()) ? request.continue() : request.abort(); }); const apiUrl = `${baseUrl}/api/v3/user/${id}/`; logger.http(`Requesting ${apiUrl}`); await page.goto(apiUrl, { waitUntil: 'domcontentloaded', }); const response = await page.evaluate(() => document.documentElement.textContent); await page.close(); await browser.close(); return JSON.parse(response || '{}'); }, config.cache.routeExpire, false ); const items = data.data.map((item) => ({ title: sanitizeHtml(item.text.split('\n')[0], { allowedTags: [], allowedAttributes: {} }), description: renderDescription(item), link: `https://x.com/${id}/status/${item.id}`, pubDate: parseDate(item.createdAt, 'x'), })); return { title: `${data.info.name} @${data.info.screenName} - Twitter Profile | Sotwe`, description: data.info.description, link: `${baseUrl}/${id}`, image: data.info.profileImageThumbnail, item: items, }; }