@ikenxuan/amagi 6.1.3 → 6.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,22 +2,22 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_chunk = require("../chunk-CKQMccvm.cjs");
5
+ const require_rolldown_runtime = require("../rolldown-runtime-D6vf50IK.cjs");
6
6
  let node_url = require("node:url");
7
- node_url = require_chunk.__toESM(node_url, 1);
7
+ node_url = require_rolldown_runtime.__toESM(node_url, 1);
8
8
  let node_events = require("node:events");
9
9
  let zod = require("zod");
10
- zod = require_chunk.__toESM(zod, 1);
10
+ zod = require_rolldown_runtime.__toESM(zod, 1);
11
11
  let _ikenxuan_xhshow_ts = require("@ikenxuan/xhshow-ts");
12
12
  let node_crypto = require("node:crypto");
13
- node_crypto = require_chunk.__toESM(node_crypto, 1);
13
+ node_crypto = require_rolldown_runtime.__toESM(node_crypto, 1);
14
14
  let axios = require("axios");
15
- axios = require_chunk.__toESM(axios, 1);
15
+ axios = require_rolldown_runtime.__toESM(axios, 1);
16
16
  let chalk = require("chalk");
17
17
  let protobufjs = require("protobufjs");
18
- protobufjs = require_chunk.__toESM(protobufjs, 1);
18
+ protobufjs = require_rolldown_runtime.__toESM(protobufjs, 1);
19
19
  let express = require("express");
20
- express = require_chunk.__toESM(express, 1);
20
+ express = require_rolldown_runtime.__toESM(express, 1);
21
21
  //#region src/utils/deprecation.ts
22
22
  /**
23
23
  * 废弃 API 注册表
@@ -1349,12 +1349,194 @@ const KuaishouMethodRoutes = {
1349
1349
  emojiList: "/fetch_emoji_list"
1350
1350
  };
1351
1351
  //#endregion
1352
+ //#region src/platform/xiaohongshu/sign/config.ts
1353
+ /** 初始化签名配置。 */
1354
+ const createXiaohongshuCryptoConfig = () => new _ikenxuan_xhshow_ts.CryptoConfig().withOverrides({
1355
+ DATA_WEB_BUILD: "6.12.3",
1356
+ SIGNATURE_DATA_TEMPLATE: {
1357
+ x0: "4.3.5",
1358
+ x1: "xhs-pc-web",
1359
+ x2: "Windows",
1360
+ x3: "",
1361
+ x4: ""
1362
+ },
1363
+ SIGNATURE_XSCOMMON_TEMPLATE: {
1364
+ s0: 5,
1365
+ s1: "",
1366
+ x0: "1",
1367
+ x1: "4.3.5",
1368
+ x2: "Windows",
1369
+ x3: "xhs-pc-web",
1370
+ x4: "6.12.3",
1371
+ x5: "",
1372
+ x6: "",
1373
+ x7: "",
1374
+ x8: "",
1375
+ x9: -596800761,
1376
+ x10: 0,
1377
+ x11: "normal"
1378
+ }
1379
+ });
1380
+ //#endregion
1381
+ //#region src/platform/xiaohongshu/sign/guestCookie.ts
1382
+ /** @see https://github.com/Cialle/RedCrack */
1383
+ /** 小红书 Web 端游客会话初始化使用的浏览器请求头。 */
1384
+ const GUEST_HEADERS = {
1385
+ accept: "application/json, text/plain, */*",
1386
+ "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
1387
+ "content-type": "application/json;charset=UTF-8",
1388
+ origin: "https://www.xiaohongshu.com",
1389
+ priority: "u=1, i",
1390
+ referer: "https://www.xiaohongshu.com/",
1391
+ "sec-ch-ua": "\"Microsoft Edge\";v=\"141\", \"Not?A_Brand\";v=\"8\", \"Chromium\";v=\"141\"",
1392
+ "sec-ch-ua-mobile": "?0",
1393
+ "sec-ch-ua-platform": "\"Windows\"",
1394
+ "sec-fetch-dest": "empty",
1395
+ "sec-fetch-mode": "cors",
1396
+ "sec-fetch-site": "same-site",
1397
+ "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0"
1398
+ };
1399
+ /** 生成 a1 随机段时使用的 Web 端字符集。 */
1400
+ const COOKIE_RANDOM_CHARS = "abcdefghijklmnopqrstuvwxyz1234567890";
1401
+ /** 将 Cookie Jar 序列化为可直接写入 HTTP Cookie 请求头的字符串。 */
1402
+ const toCookieString = (cookies) => Object.entries(cookies).map(([key, value]) => `${key}=${value}`).join("; ");
1403
+ /** 将响应头中的 Set-Cookie 字段合并到当前游客会话的 Cookie Jar。 */
1404
+ const updateCookiesFromResponse = (cookies, setCookie) => {
1405
+ const headers = Array.isArray(setCookie) ? setCookie : setCookie ? [setCookie] : [];
1406
+ for (const header of headers) {
1407
+ const [nameValue] = header.split(";", 1);
1408
+ const separatorIndex = nameValue.indexOf("=");
1409
+ if (separatorIndex > 0) cookies[nameValue.slice(0, separatorIndex).trim()] = nameValue.slice(separatorIndex + 1).trim();
1410
+ }
1411
+ };
1412
+ /** 按小红书 Web 端规则生成 a1,以及由 a1 派生的 webId。 */
1413
+ const generateA1AndWebId = () => {
1414
+ const source = `${Date.now().toString(16)}${Array.from((0, node_crypto.randomBytes)(30), (byte) => COOKIE_RANDOM_CHARS[byte % 36]).join("")}5000`;
1415
+ const a1 = `${source}${crc32(source)}`.slice(0, 52);
1416
+ return {
1417
+ a1,
1418
+ webId: (0, node_crypto.createHash)("md5").update(a1).digest("hex")
1419
+ };
1420
+ };
1421
+ /** 计算与浏览器端实现兼容的无符号 CRC32 校验值。 */
1422
+ const crc32 = (input) => {
1423
+ let value = 4294967295;
1424
+ for (const byte of Buffer.from(input)) {
1425
+ value ^= byte;
1426
+ for (let bit = 0; bit < 8; bit += 1) value = value & 1 ? value >>> 1 ^ 3988292384 : value >>> 1;
1427
+ }
1428
+ return (value ^ 4294967295) >>> 0;
1429
+ };
1430
+ /** 从 scripting 接口返回的 VMP 数据中解码 websectiga Cookie。 */
1431
+ const generateWebsectiga = (payload) => {
1432
+ const bMatch = payload.match(/"b":"(.*?)",/);
1433
+ const dMatch = payload.match(/"d":(.*?)\}\)/);
1434
+ if (!bMatch || !dMatch) throw new Error("小红书 scripting 响应格式异常,无法生成 websectiga");
1435
+ const decoderData = JSON.parse(dMatch[1]);
1436
+ const encoded = Buffer.from(bMatch[1], "base64").toString("utf8");
1437
+ const logicList = [];
1438
+ for (let index = 0; index < encoded.length; index += 5) logicList.push(Array.from(encoded.slice(index, index + 5), (char) => char.charCodeAt(0) - 1));
1439
+ const start = decoderData[92];
1440
+ const end = decoderData[93];
1441
+ const target = logicList.slice(start, end + 1);
1442
+ const key = Array.from({ length: 64 }, (_, index) => {
1443
+ const item = target[675 + index * 2];
1444
+ if (!item) throw new Error("小红书 scripting 响应缺少 websectiga 解码数据");
1445
+ return decoderData[item[2]];
1446
+ });
1447
+ return Array.from({ length: 8 }, (_, group) => {
1448
+ const offset = 56 - group * 8;
1449
+ return String.fromCharCode(...key.slice(offset, offset + 8));
1450
+ }).join("");
1451
+ };
1452
+ /** 将浏览器指纹编码为 webprofile 接口要求的 DES-ECB profileData。 */
1453
+ const encryptProfileData = (fingerprint, desKey) => {
1454
+ const encoded = Buffer.from(JSON.stringify(fingerprint)).toString("base64");
1455
+ const blockSize = 8;
1456
+ const padding = blockSize - encoded.length % blockSize;
1457
+ const plaintext = Buffer.concat([Buffer.from(encoded), Buffer.alloc(padding)]);
1458
+ const cipher = (0, node_crypto.createCipheriv)("des-ede3", Buffer.from(desKey.repeat(3)), null);
1459
+ cipher.setAutoPadding(false);
1460
+ return Buffer.concat([cipher.update(plaintext), cipher.final()]).toString("hex");
1461
+ };
1462
+ /** 将 scripting 的 JSON 或 JSONP 响应统一还原为结构化数据。 */
1463
+ const unwrapScriptingResponse = (data) => {
1464
+ if (typeof data === "string") {
1465
+ const json = data.match(/^[^(]+\((.*)\)$/s)?.[1];
1466
+ return JSON.parse(json ?? data);
1467
+ }
1468
+ return data;
1469
+ };
1470
+ /**
1471
+ * 创建小红书 Web 端游客会话 Cookie。
1472
+ *
1473
+ * 该流程对应 Web 端首次访问时的 Cookie 初始化:生成 a1/webId,完成
1474
+ * scripting、webprofile 与 activate 三个会话请求,并返回最终 Cookie 字符串。
1475
+ */
1476
+ const createXiaohongshuGuestCookie = async (requestConfig) => {
1477
+ const cryptoConfig = createXiaohongshuCryptoConfig();
1478
+ const signer = new _ikenxuan_xhshow_ts.Xhshow(cryptoConfig);
1479
+ const cookies = {
1480
+ ...generateA1AndWebId(),
1481
+ webBuild: cryptoConfig.DATA_WEB_BUILD,
1482
+ xsecappid: "xhs-pc-web",
1483
+ loadts: String(Date.now()),
1484
+ abRequestId: (0, node_crypto.randomUUID)()
1485
+ };
1486
+ const { headers: requestHeaders, params: _params, ...transportConfig } = requestConfig ?? {};
1487
+ /** 发送会话初始化请求,并将响应中的 Set-Cookie 合并回当前 Cookie Jar。 */
1488
+ const request = async (url, data, signed = false) => {
1489
+ const signatureHeaders = signed ? signer.signHeadersPost(new URL(url).pathname, cookies, "xhs-pc-web", data) : {};
1490
+ const response = await (0, axios.default)({
1491
+ ...transportConfig,
1492
+ method: "POST",
1493
+ url,
1494
+ data,
1495
+ validateStatus: () => true,
1496
+ headers: {
1497
+ ...GUEST_HEADERS,
1498
+ ...requestHeaders,
1499
+ ...signatureHeaders,
1500
+ Cookie: toCookieString(cookies)
1501
+ }
1502
+ });
1503
+ updateCookiesFromResponse(cookies, response.headers["set-cookie"]);
1504
+ if (response.status < 200 || response.status >= 300) throw new Error(`小红书游客会话初始化失败:${url} 返回 HTTP ${response.status}`);
1505
+ return response.data;
1506
+ };
1507
+ const scriptingData = unwrapScriptingResponse(await request("https://as.xiaohongshu.com/api/sec/v1/scripting", {
1508
+ callFrom: "web",
1509
+ callback: "seccallback"
1510
+ })).data;
1511
+ if (!scriptingData?.data || !scriptingData.secPoisonId) throw new Error("小红书 scripting 响应缺少游客会话数据");
1512
+ cookies.websectiga = generateWebsectiga(scriptingData.data);
1513
+ cookies.sec_poison_id = scriptingData.secPoisonId;
1514
+ const fingerprint = new _ikenxuan_xhshow_ts.FingerprintGenerator(cryptoConfig).generate(cookies, GUEST_HEADERS["user-agent"]);
1515
+ await request(cryptoConfig.GID_URL, {
1516
+ platform: cryptoConfig.DATA_PLATFORM,
1517
+ profileData: encryptProfileData(fingerprint, cryptoConfig.DES_KEY),
1518
+ sdkVersion: cryptoConfig.DATA_SDK_VERSION,
1519
+ svn: cryptoConfig.DATA_SVN
1520
+ }, true);
1521
+ await request("https://edith.xiaohongshu.com/api/sns/web/v1/login/activate", {}, true);
1522
+ if (!cookies.web_session) throw new Error("小红书游客会话初始化失败:未获取到 web_session");
1523
+ return toCookieString(cookies);
1524
+ };
1525
+ //#endregion
1352
1526
  //#region src/platform/xiaohongshu/sign/index.ts
1353
1527
  /**
1354
1528
  * 小红书签名算法类
1355
1529
  */
1356
1530
  var xiaohongshuSign = class {
1357
- static client = new _ikenxuan_xhshow_ts.Xhshow();
1531
+ static client = new _ikenxuan_xhshow_ts.Xhshow(createXiaohongshuCryptoConfig());
1532
+ /**
1533
+ * 创建包含 web_session 的小红书 Web 端游客 Cookie。
1534
+ *
1535
+ * `requestConfig` 会透传到游客会话初始化请求,可用于配置代理、超时等传输参数。
1536
+ */
1537
+ static createGuestCookie(requestConfig) {
1538
+ return createXiaohongshuGuestCookie(requestConfig);
1539
+ }
1358
1540
  /**
1359
1541
  * 生成GET请求的X-S签名
1360
1542
  * @param path - API路径
@@ -1567,7 +1749,7 @@ const xiaohongshuApiUrls = {
1567
1749
  * @param data - 请求参数
1568
1750
  * @returns 完整的接口URL
1569
1751
  */
1570
- emojiList(data) {
1752
+ emojiList(_data) {
1571
1753
  return {
1572
1754
  apiPath: "/api/im/redmoji/detail",
1573
1755
  Url: "https://edith.xiaohongshu.com/api/im/redmoji/detail"
@@ -2473,7 +2655,7 @@ const generateSecChUa = (userAgent) => {
2473
2655
  */
2474
2656
  const getDouyinDefaultConfig = (cookie, requestConfig) => {
2475
2657
  let finalUserAgent = requestConfig?.headers?.["User-Agent"] ?? "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36";
2476
- finalUserAgent = finalUserAgent.replace(/\s+Edg\/[\d\.]+/g, "");
2658
+ finalUserAgent = finalUserAgent.replace(/\s+Edg\/[\d.]+/g, "");
2477
2659
  const defHeaders = {
2478
2660
  Accept: "application/json, text/plain, */*",
2479
2661
  "Accept-Encoding": "gzip, deflate, br, zstd",
@@ -2648,7 +2830,8 @@ var SM3 = class {
2648
2830
  this.chunk = this.chunk.concat(a.slice(0, f));
2649
2831
  while (this.chunk.length >= 64) {
2650
2832
  this._compress(this.chunk);
2651
- f < a.length ? this.chunk = a.slice(f, Math.min(f + 64, a.length)) : this.chunk = [];
2833
+ if (f < a.length) this.chunk = a.slice(f, Math.min(f + 64, a.length));
2834
+ else this.chunk = [];
2652
2835
  f += 64;
2653
2836
  }
2654
2837
  }
@@ -2684,10 +2867,17 @@ var SM3 = class {
2684
2867
  if (t.length < 64) console.error("compress error: not enough data");
2685
2868
  else {
2686
2869
  for (var f = ((e) => {
2687
- for (var r = new Array(132), t = 0; t < 16; t++) r[t] = e[4 * t] << 24, r[t] |= e[4 * t + 1] << 16, r[t] |= e[4 * t + 2] << 8, r[t] |= e[4 * t + 3], r[t] >>>= 0;
2870
+ for (var r = new Array(132), t = 0; t < 16; t++) {
2871
+ r[t] = e[4 * t] << 24;
2872
+ r[t] |= e[4 * t + 1] << 16;
2873
+ r[t] |= e[4 * t + 2] << 8;
2874
+ r[t] |= e[4 * t + 3];
2875
+ r[t] >>>= 0;
2876
+ }
2688
2877
  for (var n = 16; n < 68; n++) {
2689
2878
  let a = r[n - 16] ^ r[n - 9] ^ this.le(r[n - 3], 15);
2690
- a = a ^ this.le(a, 15) ^ this.le(a, 23), r[n] = (a ^ this.le(r[n - 13], 7) ^ r[n - 6]) >>> 0;
2879
+ a = a ^ this.le(a, 15) ^ this.le(a, 23);
2880
+ r[n] = (a ^ this.le(r[n - 13], 7) ^ r[n - 6]) >>> 0;
2691
2881
  }
2692
2882
  for (n = 0; n < 64; n++) r[n + 68] = (r[n] ^ r[n + 4]) >>> 0;
2693
2883
  return r;
@@ -2697,7 +2887,15 @@ var SM3 = class {
2697
2887
  let u = this.pe(c, i[0], i[1], i[2]);
2698
2888
  u = (4294967295 & (u = u + i[3] + s + f[c + 68])) >>> 0;
2699
2889
  let b = this.he(c, i[4], i[5], i[6]);
2700
- b = (4294967295 & (b = b + i[7] + o + f[c])) >>> 0, i[3] = i[2], i[2] = this.le(i[1], 9), i[1] = i[0], i[0] = u, i[7] = i[6], i[6] = this.le(i[5], 19), i[5] = i[4], i[4] = (b ^ this.le(b, 9) ^ this.le(b, 17)) >>> 0;
2890
+ b = (4294967295 & (b = b + i[7] + o + f[c])) >>> 0;
2891
+ i[3] = i[2];
2892
+ i[2] = this.le(i[1], 9);
2893
+ i[1] = i[0];
2894
+ i[0] = u;
2895
+ i[7] = i[6];
2896
+ i[6] = this.le(i[5], 19);
2897
+ i[5] = i[4];
2898
+ i[4] = (b ^ this.le(b, 9) ^ this.le(b, 17)) >>> 0;
2701
2899
  }
2702
2900
  for (let l = 0; l < 8; l++) this.reg[l] = (this.reg[l] ^ i[l]) >>> 0;
2703
2901
  }
@@ -3008,7 +3206,7 @@ function generate_random_str() {
3008
3206
  * @returns 清理后的User-Agent字符串
3009
3207
  */
3010
3208
  const cleanUserAgentForSigning = (userAgent) => {
3011
- return userAgent.replace(/\s+Edg\/[\d\.]+/g, "");
3209
+ return userAgent.replace(/\s+Edg\/[\d.]+/g, "");
3012
3210
  };
3013
3211
  /**
3014
3212
  * 抖音a_bogus签名算法
@@ -3130,7 +3328,6 @@ var XBogus = class {
3130
3328
  const array2 = this.md5StrToArray(this.md5(this.md5StrToArray("d41d8cd98f00b204e9800998ecf8427e")));
3131
3329
  const urlEncryptedArray = this.md5Encrypt(urlPath);
3132
3330
  const timestamp = Math.floor(Date.now() / 1e3);
3133
- const ct = 536919696;
3134
3331
  const newArray = [
3135
3332
  64,
3136
3333
  1,
@@ -3146,10 +3343,10 @@ var XBogus = class {
3146
3343
  timestamp >> 16 & 255,
3147
3344
  timestamp >> 8 & 255,
3148
3345
  timestamp & 255,
3149
- ct >> 24 & 255,
3150
- ct >> 16 & 255,
3151
- ct >> 8 & 255,
3152
- ct & 255
3346
+ 32,
3347
+ 0,
3348
+ 190,
3349
+ 144
3153
3350
  ];
3154
3351
  let xorResult = newArray[0];
3155
3352
  for (let i = 1; i < newArray.length; i++) xorResult ^= newArray[i];
@@ -4069,7 +4266,7 @@ const DouyinData = async (data, cookie, requestConfig) => {
4069
4266
  if (isInvalidResponse) {
4070
4267
  const desc = `抖音${typeStr}搜索返回无有效数据,疑似触发反爬机制,你的抖音Cookie可能已经失效!`;
4071
4268
  const warningMessage = `
4072
- 获取响应数据失败!原因:${logger.yellow(`${typeStr}搜索返回无有效数据,疑似触发反爬机制`)}
4269
+ 获取响应数据失败!原因:${typeStr}搜索返回无有效数据,疑似触发反爬机制
4073
4270
  请求类型:「${data.methodType}」
4074
4271
  搜索关键词:「${data.query}」
4075
4272
  请求URL:${url}
@@ -4088,12 +4285,12 @@ const DouyinData = async (data, cookie, requestConfig) => {
4088
4285
  if (!list || list.length === 0) {
4089
4286
  const desc = `抖音${typeStr}搜索接口第一次请求就返回空数组,可能该关键词无搜索结果或触发风控限制,你的抖音Cookie可能已经失效!`;
4090
4287
  const warningMessage = `
4091
- 获取响应数据失败!原因:${logger.yellow(`${typeStr}搜索接口第一次请求就返回空数组,你的抖音Cookie可能已经失效!`)}
4288
+ 获取响应数据失败!原因:${typeStr}搜索接口第一次请求就返回空数组,你的抖音Cookie可能已经失效!
4092
4289
  请求类型:「${data.methodType}」
4093
4290
  搜索关键词:「${data.query}」
4094
4291
  请求URL:${url}
4095
4292
  `;
4096
- logger.warn(warningMessage);
4293
+ emitLogWarn(warningMessage);
4097
4294
  return {
4098
4295
  data: raw,
4099
4296
  amagiError: {
@@ -4184,7 +4381,7 @@ const DouyinData = async (data, cookie, requestConfig) => {
4184
4381
  });
4185
4382
  currentStart = currentEnd;
4186
4383
  }
4187
- logger.debug(`弹幕数据需要分${segments.length}段获取,总时长:${totalDuration}ms`);
4384
+ emitLogDebug(`弹幕数据需要分${segments.length}段获取,总时长:${totalDuration}ms`);
4188
4385
  const segmentPromises = segments.map(async (segment, index) => {
4189
4386
  const url = douyinApiUrls.getDanmakuList({
4190
4387
  aweme_id: data.aweme_id,
@@ -4197,10 +4394,10 @@ const DouyinData = async (data, cookie, requestConfig) => {
4197
4394
  ...baseRequestConfig,
4198
4395
  url: buildSignedUrl(url, signType, userAgent)
4199
4396
  });
4200
- logger.debug(`弹幕第${index + 1}段获取成功 (${segment.start}ms-${segment.end}ms)`);
4397
+ emitLogDebug(`弹幕第${index + 1}段获取成功 (${segment.start}ms-${segment.end}ms)`);
4201
4398
  return segmentData;
4202
4399
  } catch (error) {
4203
- logger.debug(`弹幕第${index + 1}段获取失败 (${segment.start}ms-${segment.end}ms):`, error);
4400
+ emitLogDebug(`弹幕第${index + 1}段获取失败 (${segment.start}ms-${segment.end}ms):`, error);
4204
4401
  return null;
4205
4402
  }
4206
4403
  });
@@ -4229,7 +4426,7 @@ const DouyinData = async (data, cookie, requestConfig) => {
4229
4426
  extra: finalExtra,
4230
4427
  log_pb: finalLogPb
4231
4428
  };
4232
- logger.debug(`弹幕数据合并完成,共获取${mergedDanmakuList.length}条弹幕`);
4429
+ emitLogDebug(`弹幕数据合并完成,共获取${mergedDanmakuList.length}条弹幕`);
4233
4430
  return finalDanmakuData;
4234
4431
  }
4235
4432
  default: {
@@ -4241,7 +4438,7 @@ const DouyinData = async (data, cookie, requestConfig) => {
4241
4438
  url
4242
4439
  });
4243
4440
  }
4244
- logger.warn(`未知的抖音数据接口:「${logger.red(data.methodType)}」`);
4441
+ emitLogWarn(`未知的抖音数据接口:「${data.methodType}」`);
4245
4442
  return null;
4246
4443
  }
4247
4444
  }
@@ -4309,11 +4506,11 @@ const GlobalGetData$3 = async (type, config) => {
4309
4506
  requestUrl: config.url
4310
4507
  };
4311
4508
  warningMessage = `
4312
- 获取响应数据失败!原因:${logger.yellow("接口返回内容为空,你的抖音ck可能已经失效!")}
4509
+ 获取响应数据失败!原因:接口返回内容为空,你的抖音ck可能已经失效!
4313
4510
  请求类型:「${type}」
4314
4511
  请求URL:${config.url}
4315
4512
  `;
4316
- logger.warn(warningMessage);
4513
+ emitLogWarn(warningMessage);
4317
4514
  const cookieError = new Error(Err.errorDescription);
4318
4515
  Object.assign(cookieError, {
4319
4516
  code: "INVALID_COOKIE",
@@ -4330,11 +4527,11 @@ const GlobalGetData$3 = async (type, config) => {
4330
4527
  requestUrl: config.url
4331
4528
  };
4332
4529
  warningMessage = `
4333
- 获取响应数据失败!原因:${logger.yellow(filterReason)}
4530
+ 获取响应数据失败!原因:${filterReason}
4334
4531
  请求类型:「${type}」
4335
4532
  请求URL:${config.url}
4336
4533
  `;
4337
- logger.warn(warningMessage);
4534
+ emitLogWarn(warningMessage);
4338
4535
  const filterError = new Error(Err.errorDescription);
4339
4536
  Object.assign(filterError, {
4340
4537
  code: "CONTENT_FILTERED",
@@ -5312,6 +5509,8 @@ const encodeBase64Url = (bytes) => {
5312
5509
  * 数据流而最小化保留的局部算法。
5313
5510
  */
5314
5511
  var KuaishouChaChaCipher = class {
5512
+ key;
5513
+ nonce;
5315
5514
  wordIndex = 0;
5316
5515
  state = new Array(16).fill(0);
5317
5516
  constructor(key, nonce) {
@@ -6144,7 +6343,7 @@ const captureKuaishouEncodeStack = () => {
6144
6343
  * @returns 用于 `SECS.s` 的栈尾字符串
6145
6344
  */
6146
6345
  const deriveKuaishouSecsStackTail = (stack = captureKuaishouEncodeStack()) => {
6147
- return stack.length > KUAISHOU_SECS_STACK_LIMIT ? stack.slice(-KUAISHOU_SECS_STACK_LIMIT) : stack;
6346
+ return stack.length > KUAISHOU_SECS_STACK_LIMIT ? stack.slice(-100) : stack;
6148
6347
  };
6149
6348
  /**
6150
6349
  * 构造快手 `window.SECS` 的纯算法等价状态。
@@ -6997,7 +7196,7 @@ const KuaishouData = async (data, cookie, requestConfig) => {
6997
7196
  }
6998
7197
  case "emojiList": return fetchKuaishouGraphqlPayload(data.methodType, kuaishouApiUrls.emojiList());
6999
7198
  default:
7000
- logger.warn(`Unknown Kuaishou API method: "${logger.red(data.methodType)}"`);
7199
+ emitLogWarn(`Unknown Kuaishou API method: "${data.methodType}"`);
7001
7200
  return null;
7002
7201
  }
7003
7202
  };
@@ -7036,12 +7235,12 @@ const GlobalGetData$2 = async (type, options, config) => {
7036
7235
  requestBody: JSON.stringify(options.data)
7037
7236
  };
7038
7237
  warningMessage = `
7039
- 获取响应数据失败!原因:${logger.yellow("接口返回内容为空,你的快手ck可能已经失效!")}
7238
+ 获取响应数据失败!原因:接口返回内容为空,你的快手ck可能已经失效!
7040
7239
  请求类型:「${type}」
7041
7240
  请求URL:${options.url}
7042
7241
  请求参数:${JSON.stringify(options.data, null, 2)}
7043
7242
  `;
7044
- logger.warn(warningMessage);
7243
+ emitLogWarn(warningMessage);
7045
7244
  const cookieError = new Error(Err.errorDescription);
7046
7245
  Object.assign(cookieError, {
7047
7246
  code: "INVALID_COOKIE",
@@ -7276,132 +7475,136 @@ function createBoundKuaishouFetcher(cookie, requestConfig) {
7276
7475
  * @returns 返回小红书数据
7277
7476
  */
7278
7477
  const XiaohongshuData = async (data, cookie, requestConfig) => {
7279
- const defHeaders = getXiaohongshuDefaultConfig(cookie)["headers"];
7280
- const baseRequestConfig = {
7281
- method: "POST",
7282
- timeout: 1e4,
7283
- ...requestConfig,
7284
- headers: {
7285
- ...defHeaders,
7286
- ...requestConfig?.headers ?? {}
7287
- }
7288
- };
7289
- const xiaohongshuApiUrls = createXiaohongshuApiUrls();
7290
- switch (data.methodType) {
7291
- case "homeFeed": return await GlobalGetData$1(data.methodType, {
7292
- ...baseRequestConfig,
7293
- url: xiaohongshuApiUrls.homeFeed(data).Url,
7294
- data: JSON.stringify(xiaohongshuApiUrls.homeFeed(data).Body),
7295
- headers: {
7296
- ...baseRequestConfig.headers,
7297
- "x-s": xiaohongshuSign.generateXSPost(xiaohongshuApiUrls.homeFeed(data).apiPath, xiaohongshuSign.extractA1FromCookie(cookie ?? ""), "xhs-pc-web", xiaohongshuApiUrls.homeFeed(data).Body),
7298
- "x-s-common": xiaohongshuSign.generateXSCommon(cookie ?? ""),
7299
- "x-t": xiaohongshuSign.generateXT()
7300
- }
7301
- });
7302
- case "noteDetail": return await GlobalGetData$1(data.methodType, {
7303
- ...baseRequestConfig,
7304
- url: xiaohongshuApiUrls.noteDetail(data).Url,
7305
- data: xiaohongshuApiUrls.noteDetail(data).Body,
7478
+ /** 使用指定 Cookie 构建一次完整的小红书 API 请求。 */
7479
+ const requestWithCookie = async (requestCookie) => {
7480
+ const defHeaders = getXiaohongshuDefaultConfig(requestCookie)["headers"];
7481
+ const baseRequestConfig = {
7482
+ method: "POST",
7483
+ timeout: 1e4,
7484
+ ...requestConfig,
7306
7485
  headers: {
7307
- ...baseRequestConfig.headers,
7308
- "x-s": xiaohongshuSign.generateXSPost(xiaohongshuApiUrls.noteDetail(data).apiPath, xiaohongshuSign.extractA1FromCookie(cookie ?? ""), "xhs-pc-web", xiaohongshuApiUrls.noteDetail(data).Body),
7309
- "x-s-common": xiaohongshuSign.generateXSCommon(cookie ?? ""),
7310
- "x-t": xiaohongshuSign.generateXT()
7486
+ ...defHeaders,
7487
+ ...requestConfig?.headers ?? {}
7311
7488
  }
7312
- });
7313
- case "noteComments": {
7314
- const baseRequestConfig = {
7315
- method: "GET",
7316
- timeout: 1e4,
7317
- ...requestConfig,
7318
- headers: {
7319
- ...defHeaders,
7320
- ...requestConfig?.headers ?? {}
7321
- }
7322
- };
7323
- return await GlobalGetData$1(data.methodType, {
7489
+ };
7490
+ const xiaohongshuApiUrls = createXiaohongshuApiUrls();
7491
+ switch (data.methodType) {
7492
+ case "homeFeed": return await GlobalGetData$1(data.methodType, {
7324
7493
  ...baseRequestConfig,
7325
- url: xiaohongshuApiUrls.noteComments(data).Url,
7494
+ url: xiaohongshuApiUrls.homeFeed(data).Url,
7495
+ data: JSON.stringify(xiaohongshuApiUrls.homeFeed(data).Body),
7326
7496
  headers: {
7327
7497
  ...baseRequestConfig.headers,
7328
- "x-s": xiaohongshuSign.generateXSGet(xiaohongshuApiUrls.noteComments(data).apiPath, xiaohongshuSign.extractA1FromCookie(cookie ?? ""), "xhs-pc-web"),
7329
- "x-s-common": xiaohongshuSign.generateXSCommon(cookie ?? ""),
7498
+ "x-s": xiaohongshuSign.generateXSPost(xiaohongshuApiUrls.homeFeed(data).apiPath, xiaohongshuSign.extractA1FromCookie(requestCookie), "xhs-pc-web", xiaohongshuApiUrls.homeFeed(data).Body),
7499
+ "x-s-common": xiaohongshuSign.generateXSCommon(requestCookie),
7330
7500
  "x-t": xiaohongshuSign.generateXT()
7331
7501
  }
7332
7502
  });
7333
- }
7334
- case "userProfile": {
7335
- const baseRequestConfig = {
7336
- method: "GET",
7337
- timeout: 1e4,
7338
- ...requestConfig,
7503
+ case "noteDetail": return await GlobalGetData$1(data.methodType, {
7504
+ ...baseRequestConfig,
7505
+ url: xiaohongshuApiUrls.noteDetail(data).Url,
7506
+ data: xiaohongshuApiUrls.noteDetail(data).Body,
7339
7507
  headers: {
7340
- ...defHeaders,
7341
- ...requestConfig?.headers ?? {}
7508
+ ...baseRequestConfig.headers,
7509
+ "x-s": xiaohongshuSign.generateXSPost(xiaohongshuApiUrls.noteDetail(data).apiPath, xiaohongshuSign.extractA1FromCookie(requestCookie), "xhs-pc-web", xiaohongshuApiUrls.noteDetail(data).Body),
7510
+ "x-s-common": xiaohongshuSign.generateXSCommon(requestCookie),
7511
+ "x-t": xiaohongshuSign.generateXT()
7342
7512
  }
7343
- };
7344
- return {
7345
- code: 0,
7346
- data: extractCreatorInfoFromHtml(await GlobalGetData$1(data.methodType, {
7513
+ });
7514
+ case "noteComments": {
7515
+ const baseRequestConfig = {
7516
+ method: "GET",
7517
+ timeout: 1e4,
7518
+ ...requestConfig,
7519
+ headers: {
7520
+ ...defHeaders,
7521
+ ...requestConfig?.headers ?? {}
7522
+ }
7523
+ };
7524
+ return await GlobalGetData$1(data.methodType, {
7347
7525
  ...baseRequestConfig,
7348
- url: xiaohongshuApiUrls.userProfile(data).Url,
7526
+ url: xiaohongshuApiUrls.noteComments(data).Url,
7349
7527
  headers: {
7350
7528
  ...baseRequestConfig.headers,
7351
- "x-s": xiaohongshuSign.generateXSGet(xiaohongshuApiUrls.userProfile(data).apiPath, xiaohongshuSign.extractA1FromCookie(cookie ?? ""), "xhs-pc-web"),
7352
- "x-s-common": xiaohongshuSign.generateXSCommon(cookie ?? ""),
7529
+ "x-s": xiaohongshuSign.generateXSGet(xiaohongshuApiUrls.noteComments(data).apiPath, xiaohongshuSign.extractA1FromCookie(requestCookie), "xhs-pc-web"),
7530
+ "x-s-common": xiaohongshuSign.generateXSCommon(requestCookie),
7353
7531
  "x-t": xiaohongshuSign.generateXT()
7354
7532
  }
7355
- })),
7356
- msg: "success"
7357
- };
7358
- }
7359
- case "userNoteList": return await GlobalGetData$1(data.methodType, {
7360
- ...baseRequestConfig,
7361
- method: "GET",
7362
- url: xiaohongshuApiUrls.userNoteList(data).Url,
7363
- headers: {
7364
- ...baseRequestConfig.headers,
7365
- "x-b3-traceid": xiaohongshuSign.generateXB3Traceid(),
7366
- "x-s": xiaohongshuSign.generateXSGet(xiaohongshuApiUrls.userNoteList(data).apiPath, xiaohongshuSign.extractA1FromCookie(cookie ?? ""), "xhs-pc-web"),
7367
- "x-s-common": xiaohongshuSign.generateXSCommon(cookie ?? ""),
7368
- "x-t": xiaohongshuSign.generateXT()
7533
+ });
7369
7534
  }
7370
- });
7371
- case "emojiList": {
7372
- const baseRequestConfig = {
7535
+ case "userProfile": {
7536
+ const baseRequestConfig = {
7537
+ method: "GET",
7538
+ timeout: 1e4,
7539
+ ...requestConfig,
7540
+ headers: {
7541
+ ...defHeaders,
7542
+ ...requestConfig?.headers ?? {}
7543
+ }
7544
+ };
7545
+ return {
7546
+ code: 0,
7547
+ data: extractCreatorInfoFromHtml(await GlobalGetData$1(data.methodType, {
7548
+ ...baseRequestConfig,
7549
+ url: xiaohongshuApiUrls.userProfile(data).Url,
7550
+ headers: {
7551
+ ...baseRequestConfig.headers,
7552
+ "x-s": xiaohongshuSign.generateXSGet(xiaohongshuApiUrls.userProfile(data).apiPath, xiaohongshuSign.extractA1FromCookie(requestCookie), "xhs-pc-web"),
7553
+ "x-s-common": xiaohongshuSign.generateXSCommon(requestCookie),
7554
+ "x-t": xiaohongshuSign.generateXT()
7555
+ }
7556
+ })),
7557
+ msg: "success"
7558
+ };
7559
+ }
7560
+ case "userNoteList": return await GlobalGetData$1(data.methodType, {
7561
+ ...baseRequestConfig,
7373
7562
  method: "GET",
7374
- timeout: 1e4,
7375
- ...requestConfig,
7563
+ url: xiaohongshuApiUrls.userNoteList(data).Url,
7376
7564
  headers: {
7377
- ...defHeaders,
7378
- ...requestConfig?.headers ?? {}
7565
+ ...baseRequestConfig.headers,
7566
+ "x-b3-traceid": xiaohongshuSign.generateXB3Traceid(),
7567
+ "x-s": xiaohongshuSign.generateXSGet(xiaohongshuApiUrls.userNoteList(data).apiPath, xiaohongshuSign.extractA1FromCookie(requestCookie), "xhs-pc-web"),
7568
+ "x-s-common": xiaohongshuSign.generateXSCommon(requestCookie),
7569
+ "x-t": xiaohongshuSign.generateXT()
7379
7570
  }
7380
- };
7381
- return await GlobalGetData$1(data.methodType, {
7571
+ });
7572
+ case "emojiList": {
7573
+ const baseRequestConfig = {
7574
+ method: "GET",
7575
+ timeout: 1e4,
7576
+ ...requestConfig,
7577
+ headers: {
7578
+ ...defHeaders,
7579
+ ...requestConfig?.headers ?? {}
7580
+ }
7581
+ };
7582
+ return await GlobalGetData$1(data.methodType, {
7583
+ ...baseRequestConfig,
7584
+ url: xiaohongshuApiUrls.emojiList(data).Url,
7585
+ headers: {
7586
+ ...baseRequestConfig.headers,
7587
+ "x-s": xiaohongshuSign.generateXSGet(xiaohongshuApiUrls.emojiList(data).apiPath, xiaohongshuSign.extractA1FromCookie(requestCookie), "xhs-pc-web"),
7588
+ "x-s-common": xiaohongshuSign.generateXSCommon(requestCookie),
7589
+ "x-t": xiaohongshuSign.generateXT()
7590
+ }
7591
+ });
7592
+ }
7593
+ case "searchNotes": return await GlobalGetData$1(data.methodType, {
7382
7594
  ...baseRequestConfig,
7383
- url: xiaohongshuApiUrls.emojiList(data).Url,
7595
+ url: xiaohongshuApiUrls.searchNotes(data).Url,
7596
+ data: xiaohongshuApiUrls.searchNotes(data).Body,
7384
7597
  headers: {
7385
7598
  ...baseRequestConfig.headers,
7386
- "x-s": xiaohongshuSign.generateXSGet(xiaohongshuApiUrls.emojiList(data).apiPath, xiaohongshuSign.extractA1FromCookie(cookie ?? ""), "xhs-pc-web"),
7387
- "x-s-common": xiaohongshuSign.generateXSCommon(cookie ?? ""),
7599
+ "x-s": xiaohongshuSign.generateXSPost(xiaohongshuApiUrls.searchNotes(data).apiPath, xiaohongshuSign.extractA1FromCookie(requestCookie), "xhs-pc-web"),
7600
+ "x-s-common": xiaohongshuSign.generateXSCommon(requestCookie),
7388
7601
  "x-t": xiaohongshuSign.generateXT()
7389
7602
  }
7390
7603
  });
7604
+ default: throw new Error(`Unknown Xiaohongshu API method: "${data.methodType}"`);
7391
7605
  }
7392
- case "searchNotes": return await GlobalGetData$1(data.methodType, {
7393
- ...baseRequestConfig,
7394
- url: xiaohongshuApiUrls.searchNotes(data).Url,
7395
- data: xiaohongshuApiUrls.searchNotes(data).Body,
7396
- headers: {
7397
- ...baseRequestConfig.headers,
7398
- "x-s": xiaohongshuSign.generateXSPost(xiaohongshuApiUrls.searchNotes(data).apiPath, xiaohongshuSign.extractA1FromCookie(cookie ?? ""), "xhs-pc-web"),
7399
- "x-s-common": xiaohongshuSign.generateXSCommon(cookie ?? ""),
7400
- "x-t": xiaohongshuSign.generateXT()
7401
- }
7402
- });
7403
- default: throw new Error(`Unknown Xiaohongshu API method: "${logger.red(data.methodType)}"`);
7404
- }
7606
+ };
7607
+ return requestWithCookie(cookie?.trim() ?? "");
7405
7608
  };
7406
7609
  /**
7407
7610
  * 全局数据获取函数
@@ -7425,7 +7628,7 @@ const GlobalGetData$1 = async (methodType, config) => {
7425
7628
  if (response.code !== 0) throw new Error(`API request failed: ${response.data?.msg ?? response.msg ?? "Unknown error"}, code: ${response.code}`);
7426
7629
  return response;
7427
7630
  } catch (error) {
7428
- logger.error(`Xiaohongshu API request failed [${methodType}]:`, error.message);
7631
+ emitLogError(`Xiaohongshu API request failed [${methodType}]:`, error.message);
7429
7632
  return {
7430
7633
  code: 500,
7431
7634
  message: "error",
@@ -7766,7 +7969,7 @@ const createNetworkErrorResult = (error, retries) => {
7766
7969
  * @returns 清理后的User-Agent字符串
7767
7970
  */
7768
7971
  const cleanUserAgent = (userAgent) => {
7769
- return userAgent.replace(/\s+Edg\/[\d\.]+/g, "");
7972
+ return userAgent.replace(/\s+Edg\/[\d.]+/g, "");
7770
7973
  };
7771
7974
  /**
7772
7975
  * 执行网络请求并返回数据(带自动重试)
@@ -8018,8 +8221,7 @@ const qtparam = async (BASEURL, cookie) => {
8018
8221
  126,
8019
8222
  127
8020
8223
  ];
8021
- let isvip;
8022
- logininfo.data.vipStatus === 1 ? isvip = true : isvip = false;
8224
+ const isvip = logininfo.data.vipStatus === 1;
8023
8225
  if (isvip) return {
8024
8226
  QUERY: `&fnval=4048&fourk=1&${sign}`,
8025
8227
  STATUS: "isLogin",
@@ -8354,7 +8556,7 @@ const fetchBilibili = async (data, cookie, requestConfig) => {
8354
8556
  ...baseRequestConfig,
8355
8557
  url: checkStatusUrl
8356
8558
  })).data === null) {
8357
- logger.error("评论区未开放");
8559
+ emitLogError("评论区未开放");
8358
8560
  return {
8359
8561
  code: 404,
8360
8562
  message: "评论区未开放",
@@ -8385,7 +8587,7 @@ const fetchBilibili = async (data, cookie, requestConfig) => {
8385
8587
  } else isEnd = true;
8386
8588
  requestCount++;
8387
8589
  if (isEnd || currentComments.length === 0 || !nextPaginationStr) {
8388
- logger.info("已到达评论末尾或无更多评论");
8590
+ emitLogInfo("已到达评论末尾或无更多评论");
8389
8591
  break;
8390
8592
  }
8391
8593
  }
@@ -8624,7 +8826,7 @@ const fetchBilibili = async (data, cookie, requestConfig) => {
8624
8826
  }
8625
8827
  }
8626
8828
  default:
8627
- logger.warn(`未知的B站数据接口:「${logger.red(data.methodType)}」`);
8829
+ emitLogWarn(`未知的B站数据接口:「${data.methodType}」`);
8628
8830
  return null;
8629
8831
  }
8630
8832
  };
@@ -8660,11 +8862,11 @@ const GlobalGetData = async (type, options, retryCount = 0) => {
8660
8862
  requestUrl: options.url
8661
8863
  };
8662
8864
  warningMessage = `
8663
- 获取响应数据失败!原因:${logger.yellow("接口返回内容为空,你的B站ck可能已经失效!")}
8865
+ 获取响应数据失败!原因:接口返回内容为空,你的B站ck可能已经失效!
8664
8866
  请求类型:「${type}」
8665
8867
  请求URL:${options.url}
8666
8868
  `;
8667
- logger.warn(warningMessage);
8869
+ emitLogWarn(warningMessage);
8668
8870
  const riskError = new Error(Err.errorDescription);
8669
8871
  Object.assign(riskError, {
8670
8872
  code: "-352",
@@ -8686,12 +8888,12 @@ const GlobalGetData = async (type, options, retryCount = 0) => {
8686
8888
  responseCode: result.code
8687
8889
  };
8688
8890
  warningMessage = `
8689
- 获取响应数据失败!原因:${logger.yellow(errorMessage)}
8891
+ 获取响应数据失败!原因:${errorMessage}
8690
8892
  错误代码:${result.code}
8691
8893
  请求类型:「${type}」
8692
8894
  请求URL:${options.url}
8693
8895
  `;
8694
- logger.warn(warningMessage);
8896
+ emitLogWarn(warningMessage);
8695
8897
  const apiError = new Error(Err.errorDescription);
8696
8898
  Object.assign(apiError, {
8697
8899
  code: result.code,
@@ -9175,15 +9377,6 @@ const kuaishouUtils = {
9175
9377
  //#endregion
9176
9378
  //#region src/platform/xiaohongshu/XiaohongshuApi.ts
9177
9379
  /**
9178
- * 小红书 API 模块 (已废弃)
9179
- *
9180
- * 此模块中的 API 已在 v6 版本废弃
9181
- * 请使用 xiaohongshuFetcher 或 client.xiaohongshu.fetcher 替代
9182
- *
9183
- * @module platform/xiaohongshu/XiaohongshuApi
9184
- * @deprecated v6 已废弃,请使用 fetcher API 替代
9185
- */
9186
- /**
9187
9380
  * 创建废弃的 API 存根函数
9188
9381
  */
9189
9382
  const createDeprecatedStub = (methodName) => {
@@ -9986,7 +10179,7 @@ function getApiRoute(platform, methodType) {
9986
10179
  * 构建后使用 __VERSION__,开发环境从 package.json 读取
9987
10180
  */
9988
10181
  const getVersion = () => {
9989
- return "6.1.3";
10182
+ return "6.2.0";
9990
10183
  };
9991
10184
  const VERSION = getVersion();
9992
10185
  /**
@@ -10033,6 +10226,11 @@ const CreateApp = CreateAmagiApp;
10033
10226
  /** After instantiation, it can interact with the specified platform API to quickly obtain data. */
10034
10227
  const Client = CreateApp;
10035
10228
  const amagi = Client;
10229
+ /*!
10230
+ * @ikenxuan/amagi
10231
+ * Copyright(c) 2023 ikenxuan
10232
+ * GPL-3.0 Licensed
10233
+ */
10036
10234
  //#endregion
10037
10235
  exports.AdditionalType = AdditionalType;
10038
10236
  exports.ApiError = ApiError;