@ikenxuan/amagi 5.12.0 → 5.12.3
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.
- package/dist/default/index.cjs +271 -273
- package/dist/default/index.d.cts +83 -86
- package/dist/default/index.d.cts.map +1 -0
- package/dist/default/index.d.ts +83 -86
- package/dist/default/index.d.ts.map +1 -0
- package/dist/default/index.js +272 -273
- package/dist/default/index.js.map +1 -0
- package/package.json +16 -16
package/dist/default/index.js
CHANGED
|
@@ -2,14 +2,27 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import url from "node:url";
|
|
4
4
|
import { Chalk } from "chalk";
|
|
5
|
-
import log4js from "log4js";
|
|
6
5
|
import axios, { AxiosError } from "axios";
|
|
7
6
|
import zod from "zod";
|
|
8
|
-
import crypto from "node:crypto";
|
|
9
7
|
import { Xhshow } from "@ikenxuan/xhshow-ts";
|
|
8
|
+
import crypto from "node:crypto";
|
|
10
9
|
import express from "express";
|
|
11
10
|
|
|
12
11
|
//#region src/model/logger.ts
|
|
12
|
+
/**
|
|
13
|
+
* 动态获取 log4js 库
|
|
14
|
+
* 优先使用 node-karin/log4js (Karin 环境)
|
|
15
|
+
* 失败则回退到 log4js (独立环境,通过别名映射到 @karinjs/log4js)
|
|
16
|
+
*/
|
|
17
|
+
const getLog4js = async () => {
|
|
18
|
+
try {
|
|
19
|
+
return (await import("node-karin/log4js")).default;
|
|
20
|
+
} catch {
|
|
21
|
+
const lib = await import("log4js");
|
|
22
|
+
return lib.default || lib;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const log4jsPromise = getLog4js();
|
|
13
26
|
/** 获取包的绝对路径 */
|
|
14
27
|
const getPackageLogsPath = () => {
|
|
15
28
|
const currentFileUrl = import.meta.url;
|
|
@@ -19,7 +32,11 @@ const getPackageLogsPath = () => {
|
|
|
19
32
|
if (fs.existsSync(path.join(packageRoot, "package.json"))) break;
|
|
20
33
|
packageRoot = path.dirname(packageRoot);
|
|
21
34
|
}
|
|
22
|
-
|
|
35
|
+
const logsDir = path.join(packageRoot, "logs");
|
|
36
|
+
try {
|
|
37
|
+
if (!fs.existsSync(logsDir)) fs.mkdirSync(logsDir, { recursive: true });
|
|
38
|
+
} catch (error) {}
|
|
39
|
+
return logsDir;
|
|
23
40
|
};
|
|
24
41
|
const logsPath = getPackageLogsPath();
|
|
25
42
|
/** 获取日志级别,优先使用环境变量,默认为 info */
|
|
@@ -27,59 +44,65 @@ const getLogLevel = () => {
|
|
|
27
44
|
return process.env.LOG_LEVEL ?? "info";
|
|
28
45
|
};
|
|
29
46
|
const currentLogLevel = getLogLevel();
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
47
|
+
/** 初始化 logger 配置 */
|
|
48
|
+
const initLogger = () => {
|
|
49
|
+
log4jsPromise.then((log4js) => {
|
|
50
|
+
log4js.configure({
|
|
51
|
+
appenders: {
|
|
52
|
+
console: {
|
|
53
|
+
type: "stdout",
|
|
54
|
+
layout: {
|
|
55
|
+
type: "pattern",
|
|
56
|
+
pattern: "%[[amagi][%d{hh:mm:ss.SSS}][%4.4p]%] %m"
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
command: {
|
|
60
|
+
type: "dateFile",
|
|
61
|
+
filename: path.join(logsPath, "application", "command"),
|
|
62
|
+
pattern: "yyyy-MM-dd.log",
|
|
63
|
+
numBackups: 15,
|
|
64
|
+
alwaysIncludePattern: true,
|
|
65
|
+
layout: {
|
|
66
|
+
type: "pattern",
|
|
67
|
+
pattern: "[%d{hh:mm:ss.SSS}][%4.4p] %m"
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
httpConsole: {
|
|
71
|
+
type: "stdout",
|
|
72
|
+
layout: {
|
|
73
|
+
type: "pattern",
|
|
74
|
+
pattern: "%[[amagi][%d{hh:mm:ss.SSS}][HTTP]%] %m"
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
httpRequest: {
|
|
78
|
+
type: "dateFile",
|
|
79
|
+
filename: path.join(logsPath, "http", "requests"),
|
|
80
|
+
pattern: "yyyy-MM-dd.log",
|
|
81
|
+
numBackups: 30,
|
|
82
|
+
alwaysIncludePattern: true,
|
|
83
|
+
layout: {
|
|
84
|
+
type: "pattern",
|
|
85
|
+
pattern: "[%d{hh:mm:ss.SSS}][%4.4p] %m"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
categories: {
|
|
90
|
+
default: {
|
|
91
|
+
appenders: ["console", "command"],
|
|
92
|
+
level: currentLogLevel
|
|
93
|
+
},
|
|
94
|
+
http: {
|
|
95
|
+
appenders: ["httpConsole", "httpRequest"],
|
|
96
|
+
level: "debug"
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
pm2: true
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
};
|
|
81
103
|
var CustomLogger = class {
|
|
82
104
|
logger;
|
|
105
|
+
queue = [];
|
|
83
106
|
chalk;
|
|
84
107
|
red;
|
|
85
108
|
green;
|
|
@@ -90,7 +113,10 @@ var CustomLogger = class {
|
|
|
90
113
|
white;
|
|
91
114
|
gray;
|
|
92
115
|
constructor(name) {
|
|
93
|
-
|
|
116
|
+
log4jsPromise.then((log4js) => {
|
|
117
|
+
this.logger = log4js.getLogger(name);
|
|
118
|
+
this.flush();
|
|
119
|
+
});
|
|
94
120
|
this.chalk = new Chalk();
|
|
95
121
|
this.red = this.chalk.red;
|
|
96
122
|
this.green = this.chalk.green;
|
|
@@ -101,20 +127,31 @@ var CustomLogger = class {
|
|
|
101
127
|
this.white = this.chalk.white;
|
|
102
128
|
this.gray = this.chalk.gray;
|
|
103
129
|
}
|
|
130
|
+
flush() {
|
|
131
|
+
if (!this.logger) return;
|
|
132
|
+
while (this.queue.length) {
|
|
133
|
+
const [method, args] = this.queue.shift();
|
|
134
|
+
this.logger[method](...args);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
proxy(method, args) {
|
|
138
|
+
if (this.logger) this.logger[method](...args);
|
|
139
|
+
else this.queue.push([method, args]);
|
|
140
|
+
}
|
|
104
141
|
info(message, ...args) {
|
|
105
|
-
this.
|
|
142
|
+
this.proxy("info", [message, ...args]);
|
|
106
143
|
}
|
|
107
144
|
warn(message, ...args) {
|
|
108
|
-
this.
|
|
145
|
+
this.proxy("warn", [message, ...args]);
|
|
109
146
|
}
|
|
110
147
|
error(message, ...args) {
|
|
111
|
-
this.
|
|
148
|
+
this.proxy("error", [message, ...args]);
|
|
112
149
|
}
|
|
113
150
|
mark(message, ...args) {
|
|
114
|
-
this.
|
|
151
|
+
this.proxy("mark", [message, ...args]);
|
|
115
152
|
}
|
|
116
153
|
debug(message, ...args) {
|
|
117
|
-
this.
|
|
154
|
+
this.proxy("debug", [message, ...args]);
|
|
118
155
|
}
|
|
119
156
|
};
|
|
120
157
|
const logger = new CustomLogger("default");
|
|
@@ -713,51 +750,26 @@ var xiaohongshuSign = class {
|
|
|
713
750
|
return this.client.signXsPost(path$1, a1Cookie, clientType, body);
|
|
714
751
|
}
|
|
715
752
|
/**
|
|
716
|
-
* 生成X-S签名(兼容旧接口)
|
|
717
|
-
* @param url - 请求URL
|
|
718
|
-
* @param body - 请求体
|
|
719
|
-
* @param userAgent - User-Agent(暂未使用)
|
|
720
|
-
* @param method - 请求方法,默认为 'POST'
|
|
721
|
-
* @param a1Cookie - a1 cookie值
|
|
722
|
-
* @returns X-S签名
|
|
723
|
-
*/
|
|
724
|
-
static generateXS(url$1, body, userAgent, method = "POST", a1Cookie = "") {
|
|
725
|
-
try {
|
|
726
|
-
const urlObj = new URL(url$1);
|
|
727
|
-
const path$1 = urlObj.pathname + urlObj.search;
|
|
728
|
-
if (method.toUpperCase() === "GET") {
|
|
729
|
-
const params = typeof body === "object" ? body : {};
|
|
730
|
-
return this.generateXSGet(path$1, a1Cookie, "xhs-pc-web", params);
|
|
731
|
-
} else {
|
|
732
|
-
const requestBody = typeof body === "object" ? body : {};
|
|
733
|
-
return this.generateXSPost(path$1, a1Cookie, "xhs-pc-web", requestBody);
|
|
734
|
-
}
|
|
735
|
-
} catch (error) {
|
|
736
|
-
console.error("生成X-S签名失败:", error);
|
|
737
|
-
throw new Error(`签名生成失败: ${error}`);
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
/**
|
|
741
753
|
* 生成X-S-Common参数
|
|
742
|
-
* @param
|
|
754
|
+
* @param cookies - cookie字符串
|
|
743
755
|
* @returns Base64编码的随机字符串
|
|
744
756
|
*/
|
|
745
|
-
static generateXSCommon(
|
|
746
|
-
return
|
|
757
|
+
static generateXSCommon(cookies) {
|
|
758
|
+
return this.client.signXsCommon(cookies);
|
|
747
759
|
}
|
|
748
760
|
/**
|
|
749
761
|
* 生成X-T时间戳
|
|
750
762
|
* @returns 当前时间戳字符串
|
|
751
763
|
*/
|
|
752
764
|
static generateXT() {
|
|
753
|
-
return
|
|
765
|
+
return this.client.getXT();
|
|
754
766
|
}
|
|
755
767
|
/**
|
|
756
768
|
* 生成X-B3-Traceid
|
|
757
769
|
* @returns 16位随机字符串
|
|
758
770
|
*/
|
|
759
771
|
static generateXB3Traceid() {
|
|
760
|
-
return
|
|
772
|
+
return this.client.getB3TraceId();
|
|
761
773
|
}
|
|
762
774
|
/**
|
|
763
775
|
* 从cookie字符串中提取a1值
|
|
@@ -1243,7 +1255,8 @@ const fetchResponse = async (config, maxRetries = DEFAULT_MAX_RETRIES) => {
|
|
|
1243
1255
|
* 判断结果是否为网络错误响应
|
|
1244
1256
|
* @param result - 请求结果
|
|
1245
1257
|
* @returns 是否为ErrorResult
|
|
1246
|
-
*
|
|
1258
|
+
*
|
|
1259
|
+
* 通过检查 error 字段中的 amagiError 来区分网络错误和业务错误
|
|
1247
1260
|
*/
|
|
1248
1261
|
const isNetworkErrorResult = (result) => {
|
|
1249
1262
|
if (result === null || typeof result !== "object") return false;
|
|
@@ -3649,11 +3662,53 @@ const DouyinData = async (data$1, cookie, requestConfig) => {
|
|
|
3649
3662
|
}
|
|
3650
3663
|
case "评论数据": {
|
|
3651
3664
|
const urlGenerator = (params) => douyinApiUrls$1.评论(params);
|
|
3652
|
-
return await fetchPaginatedData(
|
|
3665
|
+
return await fetchPaginatedData({
|
|
3666
|
+
type: data$1.methodType,
|
|
3667
|
+
apiUrlGenerator: urlGenerator,
|
|
3668
|
+
params: {
|
|
3669
|
+
...data$1,
|
|
3670
|
+
cursor: data$1.cursor ?? 0
|
|
3671
|
+
},
|
|
3672
|
+
maxPageSize: 50,
|
|
3673
|
+
requestConfig: baseRequestConfig,
|
|
3674
|
+
signType,
|
|
3675
|
+
extractList: (resp) => resp.comments ?? [],
|
|
3676
|
+
updateParams: (params, resp) => ({
|
|
3677
|
+
...params,
|
|
3678
|
+
cursor: resp.cursor
|
|
3679
|
+
}),
|
|
3680
|
+
hasMore: (resp) => resp.has_more === 1,
|
|
3681
|
+
formatFinalResponse: (resp, list) => ({
|
|
3682
|
+
...resp,
|
|
3683
|
+
comments: list,
|
|
3684
|
+
cursor: resp.cursor ?? list.length
|
|
3685
|
+
})
|
|
3686
|
+
});
|
|
3653
3687
|
}
|
|
3654
3688
|
case "指定评论回复数据": {
|
|
3655
3689
|
const urlGenerator = (params) => douyinApiUrls$1.二级评论(params);
|
|
3656
|
-
return await fetchPaginatedData(
|
|
3690
|
+
return await fetchPaginatedData({
|
|
3691
|
+
type: data$1.methodType,
|
|
3692
|
+
apiUrlGenerator: urlGenerator,
|
|
3693
|
+
params: {
|
|
3694
|
+
...data$1,
|
|
3695
|
+
cursor: data$1.cursor ?? 0
|
|
3696
|
+
},
|
|
3697
|
+
maxPageSize: 3,
|
|
3698
|
+
requestConfig: baseRequestConfig,
|
|
3699
|
+
signType: "x_bogus",
|
|
3700
|
+
extractList: (resp) => resp.comments ?? [],
|
|
3701
|
+
updateParams: (params, resp) => ({
|
|
3702
|
+
...params,
|
|
3703
|
+
cursor: resp.cursor
|
|
3704
|
+
}),
|
|
3705
|
+
hasMore: (resp) => resp.has_more === 1,
|
|
3706
|
+
formatFinalResponse: (resp, list) => ({
|
|
3707
|
+
...resp,
|
|
3708
|
+
comments: list,
|
|
3709
|
+
cursor: resp.cursor ?? list.length
|
|
3710
|
+
})
|
|
3711
|
+
});
|
|
3657
3712
|
}
|
|
3658
3713
|
case "用户主页数据": {
|
|
3659
3714
|
const url$1 = douyinApiUrls$1.用户主页信息({ sec_uid: data$1.sec_uid });
|
|
@@ -3705,10 +3760,6 @@ const DouyinData = async (data$1, cookie, requestConfig) => {
|
|
|
3705
3760
|
});
|
|
3706
3761
|
}
|
|
3707
3762
|
case "搜索数据": {
|
|
3708
|
-
let search_id = "";
|
|
3709
|
-
const maxPageSize = 15;
|
|
3710
|
-
let fetchedSearchList = [];
|
|
3711
|
-
let tmpresp = null;
|
|
3712
3763
|
const searchType = data$1.type ?? "综合";
|
|
3713
3764
|
const refererUrl = searchType === "用户" ? `https://www.douyin.com/search/${encodeURIComponent(String(data$1.query))}?type=user` : searchType === "视频" ? `https://www.douyin.com/search/${encodeURIComponent(String(data$1.query))}?type=video` : `https://www.douyin.com/root/search/${encodeURIComponent(String(data$1.query))}`;
|
|
3714
3765
|
const customConfig = {
|
|
@@ -3720,146 +3771,82 @@ const DouyinData = async (data$1, cookie, requestConfig) => {
|
|
|
3720
3771
|
};
|
|
3721
3772
|
const isUserSearch = searchType === "用户";
|
|
3722
3773
|
const isVideoSearch = searchType === "视频";
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3774
|
+
return await fetchPaginatedData({
|
|
3775
|
+
type: data$1.methodType,
|
|
3776
|
+
apiUrlGenerator: (params) => douyinApiUrls$1.搜索(params),
|
|
3777
|
+
params: {
|
|
3727
3778
|
query: data$1.query,
|
|
3728
3779
|
type: data$1.type,
|
|
3729
|
-
number:
|
|
3730
|
-
search_id:
|
|
3731
|
-
}
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
请求类型:「${data$1.methodType}」
|
|
3746
|
-
搜索关键词:「${data$1.query}」
|
|
3747
|
-
请求URL:${url$1}
|
|
3748
|
-
`;
|
|
3749
|
-
!isFirstRequest && logger.warn(warningMessage);
|
|
3750
|
-
return {
|
|
3751
|
-
code: douoyinAPIErrorCode.COOKIE,
|
|
3752
|
-
data: raw,
|
|
3753
|
-
amagiError: Err,
|
|
3754
|
-
amagiMessage: warningMessage
|
|
3755
|
-
};
|
|
3756
|
-
}
|
|
3757
|
-
const userList = raw.user_list;
|
|
3758
|
-
if (isFirstRequest && (!userList || userList.length === 0)) {
|
|
3759
|
-
const Err = {
|
|
3760
|
-
errorDescription: "抖音用户搜索接口第一次请求就返回空数组,可能该关键词无搜索结果或触发风控限制,你的抖音Cookie可能已经失效!",
|
|
3761
|
-
requestType: data$1.methodType ?? "未知请求类型",
|
|
3762
|
-
requestUrl: url$1
|
|
3763
|
-
};
|
|
3764
|
-
const warningMessage = `
|
|
3765
|
-
获取响应数据失败!原因:${logger.yellow("用户搜索接口第一次请求就返回空数组,你的抖音Cookie可能已经失效!")}
|
|
3766
|
-
请求类型:「${data$1.methodType}」
|
|
3767
|
-
搜索关键词:「${data$1.query}」
|
|
3768
|
-
请求URL:${url$1}
|
|
3769
|
-
`;
|
|
3770
|
-
logger.warn(warningMessage);
|
|
3771
|
-
return {
|
|
3772
|
-
data: raw,
|
|
3773
|
-
amagiError: Err,
|
|
3774
|
-
amagiMessage: warningMessage
|
|
3775
|
-
};
|
|
3776
|
-
}
|
|
3777
|
-
if (Array.isArray(userList) && userList.length > 0) fetchedSearchList.push(...userList);
|
|
3778
|
-
tmpresp = raw;
|
|
3779
|
-
search_id = raw.rid ?? search_id;
|
|
3780
|
-
if (typeof raw.has_more === "number" && raw.has_more === 0) break;
|
|
3781
|
-
if (!userList || userList.length === 0) break;
|
|
3782
|
-
} else if (isVideoSearch) {
|
|
3783
|
-
if (!raw || typeof raw !== "object") {
|
|
3784
|
-
const Err = {
|
|
3785
|
-
errorDescription: "抖音视频搜索接口返回无有效数据,疑似触发反爬机制,你的抖音Cookie可能已经失效,你的抖音Cookie可能已经失效!",
|
|
3786
|
-
requestType: data$1.methodType ?? "未知请求类型",
|
|
3787
|
-
requestUrl: url$1
|
|
3788
|
-
};
|
|
3789
|
-
const warningMessage = `
|
|
3790
|
-
获取响应数据失败!原因:${logger.yellow("视频搜索接口返回无有效数据,疑似触发反爬机制,你的抖音Cookie可能已经失效!")}
|
|
3791
|
-
请求类型:「${data$1.methodType}」
|
|
3792
|
-
搜索关键词:「${data$1.query}」
|
|
3793
|
-
请求URL:${url$1}
|
|
3794
|
-
`;
|
|
3795
|
-
!isFirstRequest && logger.warn(warningMessage);
|
|
3796
|
-
return {
|
|
3797
|
-
code: douoyinAPIErrorCode.COOKIE,
|
|
3798
|
-
data: raw,
|
|
3799
|
-
amagiError: Err,
|
|
3800
|
-
amagiMessage: warningMessage
|
|
3801
|
-
};
|
|
3802
|
-
}
|
|
3803
|
-
const videoList = raw.data;
|
|
3804
|
-
if (isFirstRequest && (!videoList || videoList.length === 0)) {
|
|
3805
|
-
const Err = {
|
|
3806
|
-
errorDescription: "抖音视频搜索接口第一次请求就返回空数组,可能该关键词无搜索结果或触发风控限制,你的抖音Cookie可能已经失效!",
|
|
3807
|
-
requestType: data$1.methodType ?? "未知请求类型",
|
|
3808
|
-
requestUrl: url$1
|
|
3809
|
-
};
|
|
3810
|
-
const warningMessage = `
|
|
3811
|
-
获取响应数据失败!原因:${logger.yellow("视频搜索接口第一次请求就返回空数组,你的抖音Cookie可能已经失效!")}
|
|
3812
|
-
请求类型:「${data$1.methodType}」
|
|
3813
|
-
搜索关键词:「${data$1.query}」
|
|
3814
|
-
请求URL:${url$1}
|
|
3815
|
-
`;
|
|
3816
|
-
logger.warn(warningMessage);
|
|
3780
|
+
number: data$1.number ?? 10,
|
|
3781
|
+
search_id: ""
|
|
3782
|
+
},
|
|
3783
|
+
maxPageSize: 15,
|
|
3784
|
+
requestConfig: customConfig,
|
|
3785
|
+
signType: null,
|
|
3786
|
+
processRawResponse: (raw) => {
|
|
3787
|
+
if (!isUserSearch && !isVideoSearch) {
|
|
3788
|
+
const responses = filterSearchResponses(typeof raw === "string" ? parseDouyinMultiJson(raw) : [raw]);
|
|
3789
|
+
if (responses.length === 0) return raw;
|
|
3790
|
+
const mergedData = [];
|
|
3791
|
+
let lastValid = {};
|
|
3792
|
+
for (const resp of responses) {
|
|
3793
|
+
if (Array.isArray(resp.data) && resp.data.length > 0) mergedData.push(...resp.data);
|
|
3794
|
+
lastValid = resp;
|
|
3795
|
+
}
|
|
3817
3796
|
return {
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
amagiMessage: warningMessage
|
|
3797
|
+
...lastValid,
|
|
3798
|
+
data: mergedData
|
|
3821
3799
|
};
|
|
3822
3800
|
}
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
if (
|
|
3827
|
-
|
|
3828
|
-
}
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3801
|
+
return raw;
|
|
3802
|
+
},
|
|
3803
|
+
extractList: (resp) => {
|
|
3804
|
+
if (isUserSearch) return resp.user_list ?? [];
|
|
3805
|
+
return resp.data ?? [];
|
|
3806
|
+
},
|
|
3807
|
+
updateParams: (params, resp) => {
|
|
3808
|
+
let nextSearchId = params.search_id;
|
|
3809
|
+
if (isUserSearch) nextSearchId = resp.rid ?? nextSearchId;
|
|
3810
|
+
else nextSearchId = resp.log_pb?.impr_id ?? nextSearchId;
|
|
3811
|
+
return {
|
|
3812
|
+
...params,
|
|
3813
|
+
search_id: nextSearchId
|
|
3814
|
+
};
|
|
3815
|
+
},
|
|
3816
|
+
hasMore: (resp) => {
|
|
3817
|
+
return resp.has_more !== 0;
|
|
3818
|
+
},
|
|
3819
|
+
validateFirstPage: (list, raw, url$1) => {
|
|
3820
|
+
const typeStr = isUserSearch ? "用户" : isVideoSearch ? "视频" : "综合";
|
|
3821
|
+
let isInvalidResponse = false;
|
|
3822
|
+
const rawAny = raw;
|
|
3823
|
+
if (!rawAny || typeof rawAny !== "object") isInvalidResponse = true;
|
|
3824
|
+
else if (isUserSearch && !rawAny.user_list) isInvalidResponse = true;
|
|
3825
|
+
else if (isVideoSearch && !rawAny.data) isInvalidResponse = true;
|
|
3826
|
+
else if (!isUserSearch && !isVideoSearch && !rawAny.data) isInvalidResponse = true;
|
|
3827
|
+
if (isInvalidResponse) {
|
|
3828
|
+
const desc = `抖音${typeStr}搜索返回无有效数据,疑似触发反爬机制,你的抖音Cookie可能已经失效!`;
|
|
3836
3829
|
const warningMessage = `
|
|
3837
|
-
获取响应数据失败!原因:${logger.yellow(
|
|
3830
|
+
获取响应数据失败!原因:${logger.yellow(`${typeStr}搜索返回无有效数据,疑似触发反爬机制`)}
|
|
3838
3831
|
请求类型:「${data$1.methodType}」
|
|
3839
3832
|
搜索关键词:「${data$1.query}」
|
|
3840
3833
|
请求URL:${url$1}
|
|
3841
3834
|
`;
|
|
3842
|
-
!isFirstRequest && logger.warn(warningMessage);
|
|
3843
3835
|
return {
|
|
3844
3836
|
code: douoyinAPIErrorCode.COOKIE,
|
|
3845
3837
|
data: raw,
|
|
3846
|
-
amagiError:
|
|
3838
|
+
amagiError: {
|
|
3839
|
+
errorDescription: desc,
|
|
3840
|
+
requestType: data$1.methodType ?? "未知请求类型",
|
|
3841
|
+
requestUrl: url$1
|
|
3842
|
+
},
|
|
3847
3843
|
amagiMessage: warningMessage
|
|
3848
3844
|
};
|
|
3849
3845
|
}
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
tmpresp = resp;
|
|
3853
|
-
search_id = resp.log_pb?.impr_id ?? search_id;
|
|
3854
|
-
}
|
|
3855
|
-
if (isFirstRequest && fetchedSearchList.length === 0) {
|
|
3856
|
-
const Err = {
|
|
3857
|
-
errorDescription: "抖音综合搜索接口第一次请求就返回空数组,可能该关键词无搜索结果或触发风控限制,你的抖音Cookie可能已经失效!",
|
|
3858
|
-
requestType: data$1.methodType ?? "未知请求类型",
|
|
3859
|
-
requestUrl: url$1
|
|
3860
|
-
};
|
|
3846
|
+
if (!list || list.length === 0) {
|
|
3847
|
+
const desc = `抖音${typeStr}搜索接口第一次请求就返回空数组,可能该关键词无搜索结果或触发风控限制,你的抖音Cookie可能已经失效!`;
|
|
3861
3848
|
const warningMessage = `
|
|
3862
|
-
获取响应数据失败!原因:${logger.yellow(
|
|
3849
|
+
获取响应数据失败!原因:${logger.yellow(`${typeStr}搜索接口第一次请求就返回空数组,你的抖音Cookie可能已经失效!`)}
|
|
3863
3850
|
请求类型:「${data$1.methodType}」
|
|
3864
3851
|
搜索关键词:「${data$1.query}」
|
|
3865
3852
|
请求URL:${url$1}
|
|
@@ -3867,25 +3854,27 @@ const DouyinData = async (data$1, cookie, requestConfig) => {
|
|
|
3867
3854
|
logger.warn(warningMessage);
|
|
3868
3855
|
return {
|
|
3869
3856
|
data: raw,
|
|
3870
|
-
amagiError:
|
|
3857
|
+
amagiError: {
|
|
3858
|
+
errorDescription: desc,
|
|
3859
|
+
requestType: data$1.methodType ?? "未知请求类型",
|
|
3860
|
+
requestUrl: url$1
|
|
3861
|
+
},
|
|
3871
3862
|
amagiMessage: warningMessage
|
|
3872
3863
|
};
|
|
3873
3864
|
}
|
|
3874
|
-
|
|
3865
|
+
return null;
|
|
3866
|
+
},
|
|
3867
|
+
formatFinalResponse: (resp, list) => {
|
|
3868
|
+
if (isUserSearch) return {
|
|
3869
|
+
...resp,
|
|
3870
|
+
user_list: list
|
|
3871
|
+
};
|
|
3872
|
+
return {
|
|
3873
|
+
...resp,
|
|
3874
|
+
data: list
|
|
3875
|
+
};
|
|
3875
3876
|
}
|
|
3876
|
-
|
|
3877
|
-
}
|
|
3878
|
-
const slicedList = Number(data$1.number ?? 10) === 0 ? [] : fetchedSearchList.slice(0, Number(data$1.number ?? 10));
|
|
3879
|
-
return isUserSearch ? {
|
|
3880
|
-
...tmpresp ?? {},
|
|
3881
|
-
user_list: slicedList
|
|
3882
|
-
} : isVideoSearch ? {
|
|
3883
|
-
...tmpresp ?? {},
|
|
3884
|
-
data: slicedList
|
|
3885
|
-
} : {
|
|
3886
|
-
...tmpresp ?? {},
|
|
3887
|
-
data: slicedList
|
|
3888
|
-
};
|
|
3877
|
+
});
|
|
3889
3878
|
}
|
|
3890
3879
|
case "动态表情数据": {
|
|
3891
3880
|
const url$1 = douyinApiUrls$1.互动表情();
|
|
@@ -4021,40 +4010,48 @@ const DouyinData = async (data$1, cookie, requestConfig) => {
|
|
|
4021
4010
|
};
|
|
4022
4011
|
/**
|
|
4023
4012
|
* 通用的分页请求函数
|
|
4024
|
-
*
|
|
4025
|
-
*
|
|
4026
|
-
* @
|
|
4027
|
-
* @
|
|
4028
|
-
* @
|
|
4029
|
-
* @
|
|
4030
|
-
* @
|
|
4013
|
+
* 封装了循环分页请求、数据合并、错误处理和反爬检测逻辑
|
|
4014
|
+
*
|
|
4015
|
+
* @template T - 列表项类型
|
|
4016
|
+
* @template P - 参数类型
|
|
4017
|
+
* @template R - 返回值类型
|
|
4018
|
+
* @template RawResp - 原始响应类型
|
|
4019
|
+
* @param config - 分页请求配置对象
|
|
4020
|
+
* @returns Promise<R> 返回最终构造的数据对象
|
|
4031
4021
|
*/
|
|
4032
|
-
const fetchPaginatedData = async (
|
|
4033
|
-
|
|
4034
|
-
let
|
|
4035
|
-
|
|
4022
|
+
const fetchPaginatedData = async (config) => {
|
|
4023
|
+
const { type, apiUrlGenerator, params, maxPageSize, requestConfig, signType = "a_bogus", extractList, updateParams, hasMore, formatFinalResponse, processRawResponse, validateFirstPage } = config;
|
|
4024
|
+
let currentParams = { ...params };
|
|
4025
|
+
const fetchedData = [];
|
|
4026
|
+
let lastResponse = {};
|
|
4027
|
+
let isFirstRequest = true;
|
|
4036
4028
|
const userAgent = requestConfig.headers?.["User-Agent"];
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
const
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
const response = await GlobalGetData$2(type, {
|
|
4029
|
+
const targetNumber = Number(params.number ?? maxPageSize);
|
|
4030
|
+
while (fetchedData.length < targetNumber) {
|
|
4031
|
+
const remaining = targetNumber - fetchedData.length;
|
|
4032
|
+
currentParams.number = Math.min(remaining, maxPageSize);
|
|
4033
|
+
const url$1 = apiUrlGenerator(currentParams);
|
|
4034
|
+
const finalUrl = signType ? buildSignedUrl(url$1, signType, userAgent) : url$1;
|
|
4035
|
+
const raw = await GlobalGetData$2(type, {
|
|
4045
4036
|
...requestConfig,
|
|
4046
|
-
url:
|
|
4037
|
+
url: finalUrl
|
|
4047
4038
|
});
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4039
|
+
const response = processRawResponse ? processRawResponse(raw) : raw;
|
|
4040
|
+
if (response && response.amagiError) return response;
|
|
4041
|
+
const list = extractList(response);
|
|
4042
|
+
if (isFirstRequest && validateFirstPage) {
|
|
4043
|
+
const error = validateFirstPage(list, response, finalUrl);
|
|
4044
|
+
if (error) return error;
|
|
4045
|
+
}
|
|
4046
|
+
if (Array.isArray(list) && list.length > 0) fetchedData.push(...list);
|
|
4047
|
+
lastResponse = response;
|
|
4048
|
+
if (!hasMore(response)) break;
|
|
4049
|
+
if (!list || list.length === 0) break;
|
|
4050
|
+
currentParams = updateParams(currentParams, response);
|
|
4051
|
+
isFirstRequest = false;
|
|
4052
4052
|
}
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
comments: params.number === 0 ? [] : fetchedData.slice(0, Number(params.number ?? maxPageSize)),
|
|
4056
|
-
cursor: params.number === 0 ? 0 : fetchedData.length
|
|
4057
|
-
};
|
|
4053
|
+
const slicedData = targetNumber === 0 ? [] : fetchedData.slice(0, targetNumber);
|
|
4054
|
+
return formatFinalResponse(lastResponse, slicedData);
|
|
4058
4055
|
};
|
|
4059
4056
|
/**
|
|
4060
4057
|
* 全局数据获取函数
|
|
@@ -4363,7 +4360,7 @@ const XiaohongshuData = async (data$1, cookie, requestConfig) => {
|
|
|
4363
4360
|
headers: {
|
|
4364
4361
|
...baseRequestConfig.headers,
|
|
4365
4362
|
"x-s": xiaohongshuSign.generateXSPost(xiaohongshuApiUrls$1.首页推荐数据(data$1).apiPath, xiaohongshuSign.extractA1FromCookie(cookie ?? ""), "xhs-pc-web", xiaohongshuApiUrls$1.首页推荐数据(data$1).Body),
|
|
4366
|
-
"x-s-common": xiaohongshuSign.generateXSCommon(),
|
|
4363
|
+
"x-s-common": xiaohongshuSign.generateXSCommon(cookie ?? ""),
|
|
4367
4364
|
"x-t": xiaohongshuSign.generateXT()
|
|
4368
4365
|
}
|
|
4369
4366
|
});
|
|
@@ -4374,7 +4371,7 @@ const XiaohongshuData = async (data$1, cookie, requestConfig) => {
|
|
|
4374
4371
|
headers: {
|
|
4375
4372
|
...baseRequestConfig.headers,
|
|
4376
4373
|
"x-s": xiaohongshuSign.generateXSPost(xiaohongshuApiUrls$1.单个笔记数据(data$1).apiPath, xiaohongshuSign.extractA1FromCookie(cookie ?? ""), "xhs-pc-web", xiaohongshuApiUrls$1.单个笔记数据(data$1).Body),
|
|
4377
|
-
"x-s-common": xiaohongshuSign.generateXSCommon(),
|
|
4374
|
+
"x-s-common": xiaohongshuSign.generateXSCommon(cookie ?? ""),
|
|
4378
4375
|
"x-t": xiaohongshuSign.generateXT()
|
|
4379
4376
|
}
|
|
4380
4377
|
});
|
|
@@ -4394,7 +4391,7 @@ const XiaohongshuData = async (data$1, cookie, requestConfig) => {
|
|
|
4394
4391
|
headers: {
|
|
4395
4392
|
...baseRequestConfig$1.headers,
|
|
4396
4393
|
"x-s": xiaohongshuSign.generateXSGet(xiaohongshuApiUrls$1.评论数据(data$1).apiPath, xiaohongshuSign.extractA1FromCookie(cookie ?? ""), "xhs-pc-web"),
|
|
4397
|
-
"x-s-common": xiaohongshuSign.generateXSCommon(),
|
|
4394
|
+
"x-s-common": xiaohongshuSign.generateXSCommon(cookie ?? ""),
|
|
4398
4395
|
"x-t": xiaohongshuSign.generateXT()
|
|
4399
4396
|
}
|
|
4400
4397
|
});
|
|
@@ -4417,7 +4414,7 @@ const XiaohongshuData = async (data$1, cookie, requestConfig) => {
|
|
|
4417
4414
|
headers: {
|
|
4418
4415
|
...baseRequestConfig$1.headers,
|
|
4419
4416
|
"x-s": xiaohongshuSign.generateXSGet(xiaohongshuApiUrls$1.用户数据(data$1).apiPath, xiaohongshuSign.extractA1FromCookie(cookie ?? ""), "xhs-pc-web"),
|
|
4420
|
-
"x-s-common": xiaohongshuSign.generateXSCommon(),
|
|
4417
|
+
"x-s-common": xiaohongshuSign.generateXSCommon(cookie ?? ""),
|
|
4421
4418
|
"x-t": xiaohongshuSign.generateXT()
|
|
4422
4419
|
}
|
|
4423
4420
|
})),
|
|
@@ -4432,7 +4429,7 @@ const XiaohongshuData = async (data$1, cookie, requestConfig) => {
|
|
|
4432
4429
|
...baseRequestConfig.headers,
|
|
4433
4430
|
"x-b3-traceid": xiaohongshuSign.generateXB3Traceid(),
|
|
4434
4431
|
"x-s": xiaohongshuSign.generateXSGet(xiaohongshuApiUrls$1.用户笔记数据(data$1).apiPath, xiaohongshuSign.extractA1FromCookie(cookie ?? ""), "xhs-pc-web"),
|
|
4435
|
-
"x-s-common": xiaohongshuSign.generateXSCommon(),
|
|
4432
|
+
"x-s-common": xiaohongshuSign.generateXSCommon(cookie ?? ""),
|
|
4436
4433
|
"x-t": xiaohongshuSign.generateXT()
|
|
4437
4434
|
}
|
|
4438
4435
|
});
|
|
@@ -4452,7 +4449,7 @@ const XiaohongshuData = async (data$1, cookie, requestConfig) => {
|
|
|
4452
4449
|
headers: {
|
|
4453
4450
|
...baseRequestConfig$1.headers,
|
|
4454
4451
|
"x-s": xiaohongshuSign.generateXSGet(xiaohongshuApiUrls$1.表情列表(data$1).apiPath, xiaohongshuSign.extractA1FromCookie(cookie ?? ""), "xhs-pc-web"),
|
|
4455
|
-
"x-s-common": xiaohongshuSign.generateXSCommon(),
|
|
4452
|
+
"x-s-common": xiaohongshuSign.generateXSCommon(cookie ?? ""),
|
|
4456
4453
|
"x-t": xiaohongshuSign.generateXT()
|
|
4457
4454
|
}
|
|
4458
4455
|
});
|
|
@@ -4464,7 +4461,7 @@ const XiaohongshuData = async (data$1, cookie, requestConfig) => {
|
|
|
4464
4461
|
headers: {
|
|
4465
4462
|
...baseRequestConfig.headers,
|
|
4466
4463
|
"x-s": xiaohongshuSign.generateXSPost(xiaohongshuApiUrls$1.搜索笔记(data$1).apiPath, xiaohongshuSign.extractA1FromCookie(cookie ?? ""), "xhs-pc-web"),
|
|
4467
|
-
"x-s-common": xiaohongshuSign.generateXSCommon(),
|
|
4464
|
+
"x-s-common": xiaohongshuSign.generateXSCommon(cookie ?? ""),
|
|
4468
4465
|
"x-t": xiaohongshuSign.generateXT()
|
|
4469
4466
|
}
|
|
4470
4467
|
});
|
|
@@ -4956,6 +4953,7 @@ const createAmagiClient = (options) => {
|
|
|
4956
4953
|
* @returns Express应用实例
|
|
4957
4954
|
*/
|
|
4958
4955
|
const startServer = (port = 4567) => {
|
|
4956
|
+
initLogger();
|
|
4959
4957
|
const app = express();
|
|
4960
4958
|
app.use(express.json());
|
|
4961
4959
|
app.use(express.urlencoded({ extended: true }));
|
|
@@ -5173,7 +5171,7 @@ let DynamicType = /* @__PURE__ */ function(DynamicType$1) {
|
|
|
5173
5171
|
|
|
5174
5172
|
//#endregion
|
|
5175
5173
|
//#region src/index.ts
|
|
5176
|
-
const VERSION = "5.12.
|
|
5174
|
+
const VERSION = "5.12.3";
|
|
5177
5175
|
/**
|
|
5178
5176
|
* @deprecated 请使用 createAmagiClient 替代
|
|
5179
5177
|
*/
|
|
@@ -5209,4 +5207,5 @@ const Client = CreateApp;
|
|
|
5209
5207
|
const amagi = Client;
|
|
5210
5208
|
|
|
5211
5209
|
//#endregion
|
|
5212
|
-
export { AdditionalType, ApiError, BilibiliApplyCaptchaParamsSchema, BilibiliArticleCardParamsSchema, BilibiliArticleInfoParamsSchema, BilibiliArticleParamsSchema, BilibiliAv2BvParamsSchema, BilibiliBangumiInfoParamsSchema, BilibiliBangumiStreamParamsSchema, BilibiliBv2AvParamsSchema, BilibiliColumnInfoParamsSchema, BilibiliCommentParamsSchema, BilibiliCommentReplyParamsSchema, BilibiliDynamicParamsSchema, BilibiliEmojiParamsSchema, BilibiliLiveParamsSchema, BilibiliLoginParamsSchema, BilibiliMethodRoutes, BilibiliQrcodeParamsSchema, BilibiliQrcodeStatusParamsSchema, BilibiliUserParamsSchema, BilibiliValidateCaptchaParamsSchema, BilibiliValidationSchemas, BilibiliVideoDownloadParamsSchema, BilibiliVideoParamsSchema, CommentType, CreateApp, DouyinCommentParamsSchema, DouyinCommentReplyParamsSchema, DouyinDanmakuParamsSchema, DouyinEmojiListParamsSchema, DouyinEmojiProParamsSchema, DouyinHotWordsParamsSchema, DouyinLiveRoomParamsSchema, DouyinMethodRoutes, DouyinMusicParamsSchema, DouyinQrcodeParamsSchema, DouyinSearchParamsSchema, DouyinUserParamsSchema, DouyinValidationSchemas, DouyinWorkParamsSchema, DynamicType, KuaishouCommentParamsSchema, KuaishouEmojiParamsSchema, KuaishouMethodRoutes, KuaishouValidationSchemas, KuaishouVideoParamsSchema, MajorType, ValidationError, XiaohongshuMethodRoutes, XiaohongshuValidationSchemas, amagi, amagiClient, av2bv, bilibili, bilibiliApiUrls, bilibiliErrorCodeMap, bilibiliUtils, bv2av, createAmagiClient, createBilibiliRoutes, createBilibiliRoutes as registerBilibiliRoutes, createBoundBilibiliApi, createBoundDouyinApi, createBoundKuaishouApi, createBoundXiaohongshuApi, createDouyinRoutes, createDouyinRoutes as registerDouyinRoutes, createErrorResponse, createKuaishouRoutes, createKuaishouRoutes as registerKuaishouRoutes, createSuccessResponse, createXiaohongshuRoutes, createXiaohongshuRoutes as registerXiaohongshuRoutes, Client as default, douyin, douyinApiUrls, douyinSign, douyinUtils, fetchData, fetchResponse, getBilibiliData, getDouyinData, getHeadersAndData, getKuaishouData, handleError, httpLogger, isNetworkErrorResult, kuaishou, kuaishouApiUrls, kuaishouUtils, logMiddleware, logger, qtparam, validateBilibiliParams, validateDouyinParams, validateKuaishouParams, validateXiaohongshuParams, wbi_sign, xiaohongshu, xiaohongshuApiUrls, xiaohongshuSign, xiaohongshuUtils };
|
|
5210
|
+
export { AdditionalType, ApiError, BilibiliApplyCaptchaParamsSchema, BilibiliArticleCardParamsSchema, BilibiliArticleInfoParamsSchema, BilibiliArticleParamsSchema, BilibiliAv2BvParamsSchema, BilibiliBangumiInfoParamsSchema, BilibiliBangumiStreamParamsSchema, BilibiliBv2AvParamsSchema, BilibiliColumnInfoParamsSchema, BilibiliCommentParamsSchema, BilibiliCommentReplyParamsSchema, BilibiliDynamicParamsSchema, BilibiliEmojiParamsSchema, BilibiliLiveParamsSchema, BilibiliLoginParamsSchema, BilibiliMethodRoutes, BilibiliQrcodeParamsSchema, BilibiliQrcodeStatusParamsSchema, BilibiliUserParamsSchema, BilibiliValidateCaptchaParamsSchema, BilibiliValidationSchemas, BilibiliVideoDownloadParamsSchema, BilibiliVideoParamsSchema, CommentType, CreateApp, DouyinCommentParamsSchema, DouyinCommentReplyParamsSchema, DouyinDanmakuParamsSchema, DouyinEmojiListParamsSchema, DouyinEmojiProParamsSchema, DouyinHotWordsParamsSchema, DouyinLiveRoomParamsSchema, DouyinMethodRoutes, DouyinMusicParamsSchema, DouyinQrcodeParamsSchema, DouyinSearchParamsSchema, DouyinUserParamsSchema, DouyinValidationSchemas, DouyinWorkParamsSchema, DynamicType, KuaishouCommentParamsSchema, KuaishouEmojiParamsSchema, KuaishouMethodRoutes, KuaishouValidationSchemas, KuaishouVideoParamsSchema, MajorType, ValidationError, XiaohongshuMethodRoutes, XiaohongshuValidationSchemas, amagi, amagiClient, av2bv, bilibili, bilibiliApiUrls, bilibiliErrorCodeMap, bilibiliUtils, bv2av, createAmagiClient, createBilibiliRoutes, createBilibiliRoutes as registerBilibiliRoutes, createBoundBilibiliApi, createBoundDouyinApi, createBoundKuaishouApi, createBoundXiaohongshuApi, createDouyinRoutes, createDouyinRoutes as registerDouyinRoutes, createErrorResponse, createKuaishouRoutes, createKuaishouRoutes as registerKuaishouRoutes, createSuccessResponse, createXiaohongshuRoutes, createXiaohongshuRoutes as registerXiaohongshuRoutes, Client as default, douyin, douyinApiUrls, douyinSign, douyinUtils, fetchData, fetchResponse, getBilibiliData, getDouyinData, getHeadersAndData, getKuaishouData, handleError, httpLogger, initLogger, isNetworkErrorResult, kuaishou, kuaishouApiUrls, kuaishouUtils, logMiddleware, logger, qtparam, validateBilibiliParams, validateDouyinParams, validateKuaishouParams, validateXiaohongshuParams, wbi_sign, xiaohongshu, xiaohongshuApiUrls, xiaohongshuSign, xiaohongshuUtils };
|
|
5211
|
+
//# sourceMappingURL=index.js.map
|