mirror of
https://github.com/DIYgod/RSSHub.git
synced 2026-09-24 23:12:34 +08:00
feat(route/sotwe): add sotwe (#19649)
This commit is contained in:
@@ -0,0 +1 @@
|
||||
export { namespace } from '@/routes/twitter/namespace';
|
||||
@@ -0,0 +1,98 @@
|
||||
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) => {
|
||||
request.resourceType() === 'document' ? 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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user