UNPKG

rsshub

Version:
227 lines (205 loc) 8.17 kB
import { Route } from '@/types'; import cache from '@/utils/cache'; import ofetch from '@/utils/ofetch'; import * as cheerio from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import path from 'node:path'; import { art } from '@/utils/render'; import CryptoJS from 'crypto-js'; export const route: Route = { path: '/mp/:xpt', categories: ['new-media'], example: '/sohu/mp/c29odXptdGhnbjZ3NEBzb2h1LmNvbQ==', parameters: { xpt: '搜狐号 xpt ,可在URL中找到或搜狐号 ID' }, radar: [ { source: ['mp.sohu.com/profile'], target: (_params, url) => `/sohu/mp/${new URL(url).searchParams.get('xpt')}`, }, ], name: '最新', maintainers: ['HenryQW'], handler, description: `搜狐号 ID 可以通过以下方式获取: 1. 通过浏览器搜索相关搜狐号 \`果壳 site: mp.sohu.com\`。 2. 通过浏览器控制台执行 \`window.globalConst.mkeyConst_mkey\`,返回的即为搜狐号 ID。`, }; function randomString(length = 32) { let r = ''; const e = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'; const n = e.length; for (let i = 0; i < length; i++) { r += e.charAt(Math.floor(Math.random() * n)); } return r; } const defaultSUV = '1612268936507kas0gk'; function decryptImageUrl(cipherText) { const key = CryptoJS.enc.Utf8.parse('www.sohu.com6666'); const cipher = CryptoJS.AES.decrypt(cipherText, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7, }); return cipher.toString(CryptoJS.enc.Utf8); } function fetchArticle(item) { return cache.tryGet(item.link, async () => { const response = await ofetch(item.link); const $ = cheerio.load(response); $('.original-title, .lookall-box').remove(); item.author = item.author || $('span[data-role="original-link"] a').text(); if (/window\.sohu_mp\.article_video/.test($('script').text())) { const videoSrc = $('script') .text() .match(/\s*url: "(.*?)",/)?.[1]; item.description = art(path.join(__dirname, 'templates/video.art'), { poster: $('script') .text() .match(/cover: "(.*?)",/)?.[1], src: videoSrc, type: videoSrc?.split('.').pop()?.toLowerCase(), }); } else { const article = $('#mp-editor'); article.find('#backsohucom, p[data-role="editor-name"]').each((i, e) => { $(e).remove(); }); article.find('img').each((_, e) => { const $e = $(e); if ($e.attr('data-src') && !$e.attr('src')) { $e.attr('src', decryptImageUrl($e.attr('data-src'))); $e.removeAttr('data-src'); } }); item.description = article.html(); } return item; }); } async function handler(ctx) { const xpt = ctx.req.param('xpt'); const isPureNumber = /^\d+$/.test(xpt); if (isPureNumber) { return legacyIdHandler(ctx); } const pageResponse = await ofetch.raw('https://mp.sohu.com/profile', { query: { xpt, }, }); const suv = pageResponse.headers ?.getSetCookie() .find((e) => e.startsWith('SUV')) ?.split(';')[0]; const $ = cheerio.load(pageResponse._data); const CBDRenderConst = JSON.parse( $('script:contains("CBDRenderConst")') .text() .trim() .match(/CBDRenderConst\s=\s(.*)/)?.[1] || '{}' ); const contentData = JSON.parse( $('script:contains("contentData")') .toArray() .map( (e) => $(e) .text() .match(/contentData = (.*)/)?.[1] ) .sort((a: any, b: any) => b.length - a.length)[0] || '{}' ); const blockRenderData = JSON.parse( $('script:contains("column_2_text")') .text() .match(/({.*})/)?.[1] ); const renderData = blockRenderData[Object.keys(blockRenderData).find((e) => e.startsWith('FeedSlideloadAuthor'))]; const globalConst = JSON.parse( $('script:contains("globalConst")') .text() .match(/globalConst\s=\s(.*)/)?.[1] || '{}' ); const originalRequest = JSON.parse( $('script:contains("originalRequest")') .text() .match(/originalRequest\s=\s(.*)/)?.[1] || '{}' ); const blockData = await ofetch('https://odin.sohu.com/odin/api/blockdata', { method: 'POST', headers: { Cookie: Object.entries({ SUV: suv, itssohu: 'true', reqtype: 'pc', t: Date.now(), }) .map(([key, value]) => `${key}=${value}`) .join('; '), }, body: { pvId: CBDRenderConst.COMMONCONFIG.pvId || `${Date.now()}_${randomString(7)}`, pageId: `${Date.now()}_${defaultSUV.slice(0, -5)}_${randomString(3)}`, mainContent: { productType: contentData.businessType || '13', productId: contentData.id || '324', secureScore: contentData.secureScore || '5', categoryId: contentData.categoryId || '47', adTags: contentData.adTags || '11111111', authorId: contentData.account.id || 121_135_924, }, resourceList: [ { tplCompKey: renderData.param.data2.reqParam.tplCompKey || 'FeedSlideloadAuthor_2_0_pc_1655965929143_data2', isServerRender: renderData.param.data2.reqParam.isServerRender || false, isSingleAd: renderData.param.data2.reqParam.isSingleAd || false, configSource: renderData.param.data2.reqParam.configSource || 'mp', content: { productId: renderData.param.data2.reqParam.content.productId || '325', productType: renderData.param.data2.reqParam.content.productType || '13', size: 20, pro: renderData.param.pro || '0,1,3,4,5', feedType: renderData.param.feedType || 'XTOPIC_SYNTHETICAL', view: '', innerTag: renderData.param.data2.reqParam.content.innerTag || 'work', spm: renderData.param.data2.reqParam.content.spm || 'smpc.channel_248.block3_308_hHsK47_2_fd', page: 1, requestId: `${Date.now()}_${randomString(13)}_${contentData.id}`, }, adInfo: {}, context: { mkey: globalConst.mkeyConst_mkey, // legacy ID }, }, ], }, }); const list = blockData.data[renderData.param.data2.reqParam.tplCompKey].list.map((item) => ({ title: item.title, description: item.brief, link: `https://www.sohu.com/a/${item.id}_${item.authorId}`, author: item.authorName, pubDate: parseDate(item.postTime, 'x'), })); const items = await Promise.all(list.map((e) => fetchArticle(e))); return { title: `搜狐号 - ${globalConst.title}`, link: originalRequest.url, item: items, }; } async function legacyIdHandler(ctx) { const id = ctx.req.param('xpt'); const authorArticleAPI = `https://v2.sohu.com/author-page-api/author-articles/pc/${id}`; const response = await ofetch(authorArticleAPI); const list = response.data.pcArticleVOS.map((item) => ({ title: item.title, link: item.link.startsWith('http') ? item.link : `https://${item.link}`, pubDate: parseDate(item.publicTime), })); const items = await Promise.all(list.map((e) => fetchArticle(e))); return { title: `搜狐号 - ${id}`, item: items, }; }