rsshub
Version:
Make RSS Great Again!
120 lines (119 loc) • 3.51 kB
JavaScript
import { t as parseDate } from "./parse-date-CjhZE69i.mjs";
import { t as md5 } from "./md5-CpIHIxCO.mjs";
import { randomBytes, randomInt, randomUUID } from "node:crypto";
import sanitizeHtml from "sanitize-html";
//#region lib/routes/soulapp/utils.ts
const apiRoot = "https://api-h5.soulapp.cn";
const shareRoot = "https://w3.soulsmile.cn/activity/#/web/topic/detail";
const device = {
ua: "Soul_New/6.32.0 (iPhone; iOS 27.0; Scale/3.00; CFNetwork; iPhone18,2) SoulBegin-iOS-6.32.0-WIFI-SoulEnd",
aid: "10000001",
av: "6.32.0",
avc: "260813221000",
di: randomUUID().toUpperCase(),
sdi: randomBytes(16).toString("hex")
};
const sortByCodeUnit = (a, b) => {
if (a < b) return -1;
if (a > b) return 1;
return 0;
};
const encodeTimestamp = (hex8, table) => {
const permuted = table.map((t) => hex8[t - 1]).join("");
const buffer = Buffer.alloc(4);
buffer.writeUInt32BE(Number.parseInt(permuted, 16));
return buffer;
};
const signCs = (timestampSec, str1, str2) => {
const checkStatus = 128;
const s = Buffer.alloc(36);
s[0] = 2;
s[1] = checkStatus;
const th = timestampSec.toString(16).padStart(8, "0");
const e1 = encodeTimestamp(th, [
4,
2,
7,
6,
5,
1,
8,
3
]);
const e2 = encodeTimestamp(th, [
2,
5,
3,
8,
7,
1,
6,
4
]);
const m1 = Buffer.from(md5(str2 + "SoulPowerful"), "hex");
for (let i = 0; i < 4; i++) {
s[2 + 2 * i] = m1[i];
s[3 + 2 * i] = e1[i];
}
const m2 = Buffer.from(md5(str1 + e2.toString("hex") + "kG@yGB9"), "hex");
for (let i = 0; i < 4; i++) s[12 + i] = m2[i];
Buffer.from(s.subarray(0, 16).toString("hex"), "latin1").copy(s, 0);
s[20] = 48 + randomInt(0, 10);
s[21] = 48 + randomInt(0, 10);
s[22] = 48 + randomInt(0, 10);
s[23] = s[22];
Buffer.from(randomBytes(2).toString("hex"), "latin1").copy(s, 32);
return s.toString("latin1");
};
const signedHeaders = (path, query) => {
const ms = Date.now();
const str1 = `${path}?${Object.keys(query).toSorted(sortByCodeUnit).map((k) => `${k}=${query[k]}`).join("&")}`;
const headers = {
"User-Agent": device.ua,
aid: device.aid,
at: ms.toString(16),
av: device.av,
di: device.di,
sdi: device.sdi
};
const str2 = Object.entries(headers).toSorted(([a], [b]) => sortByCodeUnit(a, b)).map(([, v]) => v).join("");
return {
...headers,
avc: device.avc,
platform: "iOS",
cs: signCs(Math.floor(ms / 1e3), str1, str2)
};
};
const parsePost = (item) => {
const pureContent = item.content;
let content = pureContent.replaceAll("\n", "<br />");
if (item.attachments) for (const attachment of item.attachments) switch (attachment.type) {
case "IMAGE":
content += "<br />";
content += `<img src="${attachment.fileUrl}" />`;
break;
case "VIDEO":
content += "<br />";
content += `<video controls src="${attachment.fileUrl}" width="640" height="360"></video>`;
break;
case "AUDIO":
content += "<br />";
content += `<audio controls src="${attachment.fileUrl}"></audio>`;
break;
default: throw new Error(`Unknown attachment type: ${attachment.type}`);
}
return {
title: `「${item.signature}」的瞬间:${sanitizeHtml(pureContent.split("\n", 1)[0], {
allowedTags: [],
allowedAttributes: {}
})}`,
author: item.signature,
description: content,
category: item.innerTags?.map((tag) => `${tag.name} (${tag.tagIdEcpt})`),
pubDate: parseDate(item.createTime),
guid: item.postIdEcpt,
link: `${shareRoot}?postIdEcpt=${item.postIdEcpt}`
};
};
//#endregion
export { parsePost as n, signedHeaders as r, apiRoot as t };