@skrillex1224/playwright-toolkit 2.1.224 → 2.1.226
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/README.md +10 -6
- package/dist/browser.js +43 -3
- package/dist/browser.js.map +2 -2
- package/dist/index.cjs +1040 -147
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +1040 -147
- package/dist/index.js.map +4 -4
- package/index.d.ts +29 -5
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -39,8 +39,10 @@ var constants_exports = {};
|
|
|
39
39
|
__export(constants_exports, {
|
|
40
40
|
ActorInfo: () => ActorInfo,
|
|
41
41
|
Code: () => Code,
|
|
42
|
+
Device: () => Device,
|
|
42
43
|
PresetOfLiveViewKey: () => PresetOfLiveViewKey,
|
|
43
|
-
Status: () => Status
|
|
44
|
+
Status: () => Status,
|
|
45
|
+
normalizeDevice: () => normalizeDevice
|
|
44
46
|
});
|
|
45
47
|
var Code = {
|
|
46
48
|
Success: 0,
|
|
@@ -56,6 +58,17 @@ var Status = {
|
|
|
56
58
|
Failed: "FAILED"
|
|
57
59
|
};
|
|
58
60
|
var PresetOfLiveViewKey = "LIVE_VIEW_SCREENSHOT";
|
|
61
|
+
var Device = Object.freeze({
|
|
62
|
+
Desktop: "desktop",
|
|
63
|
+
Mobile: "mobile"
|
|
64
|
+
});
|
|
65
|
+
var normalizeDevice = (value, fallback = Device.Desktop) => {
|
|
66
|
+
const normalizedFallback = String(fallback || "").trim().toLowerCase() === Device.Mobile ? Device.Mobile : Device.Desktop;
|
|
67
|
+
const raw = String(value || "").trim().toLowerCase();
|
|
68
|
+
if (raw === Device.Mobile) return Device.Mobile;
|
|
69
|
+
if (raw === Device.Desktop) return Device.Desktop;
|
|
70
|
+
return normalizedFallback;
|
|
71
|
+
};
|
|
59
72
|
var createActorInfo = (info) => {
|
|
60
73
|
const normalizeDomain = (value) => {
|
|
61
74
|
if (!value) return "";
|
|
@@ -133,12 +146,14 @@ var createActorInfo = (info) => {
|
|
|
133
146
|
const domain = normalizeDomain(info.domain);
|
|
134
147
|
const path2 = normalizePath(info.path);
|
|
135
148
|
const share = normalizeShare2(info.share);
|
|
149
|
+
const device = normalizeDevice(info.device);
|
|
136
150
|
return {
|
|
137
151
|
...info,
|
|
138
152
|
protocol,
|
|
139
153
|
domain,
|
|
140
154
|
path: path2,
|
|
141
155
|
share,
|
|
156
|
+
device,
|
|
142
157
|
get icon() {
|
|
143
158
|
if (info.icon) return info.icon;
|
|
144
159
|
return buildIcon(this);
|
|
@@ -190,6 +205,7 @@ var ActorInfo = {
|
|
|
190
205
|
name: "\u8C46\u5305",
|
|
191
206
|
domain: "www.doubao.com",
|
|
192
207
|
path: "/",
|
|
208
|
+
device: Device.Mobile,
|
|
193
209
|
share: {
|
|
194
210
|
mode: "response",
|
|
195
211
|
prefix: "https://www.doubao.com/thread/",
|
|
@@ -205,6 +221,7 @@ var ActorInfo = {
|
|
|
205
221
|
name: "DeepSeek",
|
|
206
222
|
domain: "chat.deepseek.com",
|
|
207
223
|
path: "/",
|
|
224
|
+
device: Device.Mobile,
|
|
208
225
|
share: {
|
|
209
226
|
mode: "response",
|
|
210
227
|
prefix: "https://chat.deepseek.com/share/",
|
|
@@ -361,18 +378,18 @@ var fallbackLog = {
|
|
|
361
378
|
error: (...args) => console.error(...args),
|
|
362
379
|
debug: (...args) => console.debug ? console.debug(...args) : console.log(...args)
|
|
363
380
|
};
|
|
364
|
-
var resolveLogMethod = (
|
|
365
|
-
if (
|
|
366
|
-
return
|
|
381
|
+
var resolveLogMethod = (logger16, name) => {
|
|
382
|
+
if (logger16 && typeof logger16[name] === "function") {
|
|
383
|
+
return logger16[name].bind(logger16);
|
|
367
384
|
}
|
|
368
|
-
if (name === "warning" &&
|
|
369
|
-
return
|
|
385
|
+
if (name === "warning" && logger16 && typeof logger16.warn === "function") {
|
|
386
|
+
return logger16.warn.bind(logger16);
|
|
370
387
|
}
|
|
371
388
|
return fallbackLog[name];
|
|
372
389
|
};
|
|
373
390
|
var defaultLogger = null;
|
|
374
|
-
var setDefaultLogger = (
|
|
375
|
-
defaultLogger =
|
|
391
|
+
var setDefaultLogger = (logger16) => {
|
|
392
|
+
defaultLogger = logger16;
|
|
376
393
|
};
|
|
377
394
|
var resolveLogger = (explicitLogger) => {
|
|
378
395
|
if (explicitLogger && typeof explicitLogger.info === "function") {
|
|
@@ -399,8 +416,8 @@ var colorize = (text, color) => {
|
|
|
399
416
|
var createBaseLogger = (prefix = "", explicitLogger) => {
|
|
400
417
|
const name = prefix ? String(prefix) : "";
|
|
401
418
|
const dispatch = (methodName, icon, message, color) => {
|
|
402
|
-
const
|
|
403
|
-
const logFn = resolveLogMethod(
|
|
419
|
+
const logger16 = resolveLogger(explicitLogger);
|
|
420
|
+
const logFn = resolveLogMethod(logger16, methodName);
|
|
404
421
|
const timestamp = colorize(`[${formatTimestamp()}]`, ANSI.gray);
|
|
405
422
|
const line = formatLine(name, icon, message);
|
|
406
423
|
const coloredLine = colorize(line, color);
|
|
@@ -1000,6 +1017,12 @@ var PageRuntimeStateKey = "__playwright_toolkit_runtime_state__";
|
|
|
1000
1017
|
var BROWSER_PROFILE_SCHEMA_VERSION = 1;
|
|
1001
1018
|
var rememberedRuntimeState = null;
|
|
1002
1019
|
var isPlainObject = (value) => value && typeof value === "object" && !Array.isArray(value);
|
|
1020
|
+
var normalizeKnownDevice = (value) => {
|
|
1021
|
+
const raw = String(value || "").trim().toLowerCase();
|
|
1022
|
+
if (raw === Device.Mobile) return Device.Mobile;
|
|
1023
|
+
if (raw === Device.Desktop) return Device.Desktop;
|
|
1024
|
+
return "";
|
|
1025
|
+
};
|
|
1003
1026
|
var deepClone = (value) => {
|
|
1004
1027
|
if (value == null) return value;
|
|
1005
1028
|
try {
|
|
@@ -1351,6 +1374,10 @@ var normalizeObservedBrowserProfile = (value) => {
|
|
|
1351
1374
|
var normalizeBrowserProfileCore = (value) => {
|
|
1352
1375
|
const source = isPlainObject(value) ? value : {};
|
|
1353
1376
|
const profile = {};
|
|
1377
|
+
const device = normalizeKnownDevice(source.device);
|
|
1378
|
+
if (device) {
|
|
1379
|
+
profile.device = device;
|
|
1380
|
+
}
|
|
1354
1381
|
if (isPlainObject(source.fingerprint) && Object.keys(source.fingerprint).length > 0) {
|
|
1355
1382
|
profile.fingerprint = deepClone(source.fingerprint);
|
|
1356
1383
|
}
|
|
@@ -1408,9 +1435,11 @@ var mergeBrowserProfilePayload = (target = {}, source = {}) => {
|
|
|
1408
1435
|
if (Object.keys(mergedCore).length === 0 && Object.keys(incomingCore).length > 0) {
|
|
1409
1436
|
mergedCore = incomingCore;
|
|
1410
1437
|
} else if (Object.keys(incomingCore).length > 0) {
|
|
1438
|
+
const currentDevice = normalizeKnownDevice(currentCore.device);
|
|
1439
|
+
const incomingDevice = normalizeKnownDevice(incomingCore.device);
|
|
1411
1440
|
const currentVersion = Number(currentCore.browser_major_version || 0);
|
|
1412
1441
|
const incomingVersion = Number(incomingCore.browser_major_version || 0);
|
|
1413
|
-
if (currentVersion > 0 && incomingVersion > 0 && currentVersion !== incomingVersion) {
|
|
1442
|
+
if (incomingDevice && currentDevice !== incomingDevice || currentVersion > 0 && incomingVersion > 0 && currentVersion !== incomingVersion) {
|
|
1414
1443
|
mergedCore = incomingCore;
|
|
1415
1444
|
}
|
|
1416
1445
|
}
|
|
@@ -1461,8 +1490,13 @@ var normalizeRuntimeState = (source = {}, actor = "") => {
|
|
|
1461
1490
|
}
|
|
1462
1491
|
return RuntimeEnv.parseInput(source, actor);
|
|
1463
1492
|
};
|
|
1493
|
+
var resolveInputDevice = (input = {}, runtime2 = {}, actor = "") => {
|
|
1494
|
+
const actorDevice = ActorInfo?.[actor]?.device;
|
|
1495
|
+
return normalizeDevice(actorDevice ?? input?.device ?? runtime2?.device);
|
|
1496
|
+
};
|
|
1464
1497
|
var RuntimeEnv = {
|
|
1465
1498
|
tryParseJSON,
|
|
1499
|
+
normalizeDevice,
|
|
1466
1500
|
normalizeCookies,
|
|
1467
1501
|
normalizeLocalStorage,
|
|
1468
1502
|
normalizeSessionStorage,
|
|
@@ -1488,6 +1522,7 @@ var RuntimeEnv = {
|
|
|
1488
1522
|
parseInput(input = {}, actor = "") {
|
|
1489
1523
|
const runtime2 = tryParseJSON(input?.runtime) || {};
|
|
1490
1524
|
const resolvedActor = String(actor || input?.actor || "").trim();
|
|
1525
|
+
const device = resolveInputDevice(input, runtime2, resolvedActor);
|
|
1491
1526
|
const query = String(input?.query || "").trim();
|
|
1492
1527
|
const cookies = normalizeCookies(runtime2?.cookies);
|
|
1493
1528
|
const cookieMap = buildCookieMap(cookies);
|
|
@@ -1507,6 +1542,7 @@ var RuntimeEnv = {
|
|
|
1507
1542
|
}
|
|
1508
1543
|
const state = {
|
|
1509
1544
|
actor: resolvedActor,
|
|
1545
|
+
device,
|
|
1510
1546
|
runtime: normalizedRuntime,
|
|
1511
1547
|
envId,
|
|
1512
1548
|
query,
|
|
@@ -1552,7 +1588,10 @@ var RuntimeEnv = {
|
|
|
1552
1588
|
},
|
|
1553
1589
|
setBrowserProfileCore(source = {}, core = {}, actor = "") {
|
|
1554
1590
|
const state = normalizeRuntimeState(source, actor);
|
|
1555
|
-
const normalizedCore = normalizeBrowserProfileCore(
|
|
1591
|
+
const normalizedCore = normalizeBrowserProfileCore({
|
|
1592
|
+
...core,
|
|
1593
|
+
device: normalizeKnownDevice(core?.device) || state.device
|
|
1594
|
+
});
|
|
1556
1595
|
state.browserProfileCore = normalizedCore;
|
|
1557
1596
|
state.browserProfile = buildBrowserProfilePayload(normalizedCore, state.browserProfileObserved);
|
|
1558
1597
|
if (Object.keys(state.browserProfile).length > 0) {
|
|
@@ -2027,15 +2066,25 @@ var DEFAULT_LAUNCH_ARGS = [
|
|
|
2027
2066
|
"--disable-setuid-sandbox",
|
|
2028
2067
|
"--window-position=0,0"
|
|
2029
2068
|
];
|
|
2030
|
-
function buildFingerprintOptions({ locale = BASE_CONFIG.locale, browserMajorVersion = 0 } = {}) {
|
|
2069
|
+
function buildFingerprintOptions({ locale = BASE_CONFIG.locale, browserMajorVersion = 0, device = Device.Desktop } = {}) {
|
|
2070
|
+
const resolvedDevice = normalizeDevice(device);
|
|
2031
2071
|
const normalizedBrowserMajorVersion = Number(browserMajorVersion || 0);
|
|
2032
2072
|
const browserDescriptor = normalizedBrowserMajorVersion > 0 ? [{ name: "chrome", minVersion: normalizedBrowserMajorVersion, maxVersion: normalizedBrowserMajorVersion }] : [{ name: "chrome", minVersion: 110 }];
|
|
2033
|
-
|
|
2073
|
+
const options = {
|
|
2034
2074
|
browsers: browserDescriptor,
|
|
2035
|
-
devices: [
|
|
2036
|
-
operatingSystems: ["windows", "macos", "linux"],
|
|
2075
|
+
devices: [resolvedDevice],
|
|
2076
|
+
operatingSystems: resolvedDevice === Device.Mobile ? ["android"] : ["windows", "macos", "linux"],
|
|
2037
2077
|
locales: [locale]
|
|
2038
2078
|
};
|
|
2079
|
+
if (resolvedDevice === Device.Mobile) {
|
|
2080
|
+
options.screen = {
|
|
2081
|
+
minWidth: 320,
|
|
2082
|
+
maxWidth: 480,
|
|
2083
|
+
minHeight: 640,
|
|
2084
|
+
maxHeight: 1100
|
|
2085
|
+
};
|
|
2086
|
+
}
|
|
2087
|
+
return options;
|
|
2039
2088
|
}
|
|
2040
2089
|
var AntiCheat = {
|
|
2041
2090
|
/**
|
|
@@ -2045,7 +2094,7 @@ var AntiCheat = {
|
|
|
2045
2094
|
return { ...BASE_CONFIG };
|
|
2046
2095
|
},
|
|
2047
2096
|
/**
|
|
2048
|
-
* 用于 Crawlee fingerprint generator
|
|
2097
|
+
* 用于 Crawlee fingerprint generator 的统一配置。
|
|
2049
2098
|
*/
|
|
2050
2099
|
getFingerprintGeneratorOptions(options = {}) {
|
|
2051
2100
|
return buildFingerprintOptions(options);
|
|
@@ -2058,10 +2107,10 @@ var AntiCheat = {
|
|
|
2058
2107
|
return [...DEFAULT_LAUNCH_ARGS, `--lang=${locale}`];
|
|
2059
2108
|
},
|
|
2060
2109
|
/**
|
|
2061
|
-
* 为 got-scraping 生成与浏览器一致的 TLS
|
|
2110
|
+
* 为 got-scraping 生成与浏览器一致的 TLS 指纹配置。
|
|
2062
2111
|
*/
|
|
2063
|
-
getTlsFingerprintOptions(userAgent = "", acceptLanguage = "") {
|
|
2064
|
-
return buildFingerprintOptions();
|
|
2112
|
+
getTlsFingerprintOptions(userAgent = "", acceptLanguage = "", options = {}) {
|
|
2113
|
+
return buildFingerprintOptions(options);
|
|
2065
2114
|
},
|
|
2066
2115
|
/**
|
|
2067
2116
|
* 规范化请求头
|
|
@@ -2074,7 +2123,7 @@ var AntiCheat = {
|
|
|
2074
2123
|
}
|
|
2075
2124
|
};
|
|
2076
2125
|
|
|
2077
|
-
// src/humanize.js
|
|
2126
|
+
// src/internals/humanize/desktop.js
|
|
2078
2127
|
var import_delay = __toESM(require("delay"), 1);
|
|
2079
2128
|
var import_ghost_cursor_playwright = require("ghost-cursor-playwright");
|
|
2080
2129
|
var logger6 = createInternalLogger("Humanize");
|
|
@@ -2099,7 +2148,7 @@ var Humanize = {
|
|
|
2099
2148
|
},
|
|
2100
2149
|
/**
|
|
2101
2150
|
* 初始化页面的 Ghost Cursor(必须在使用其他 cursor 相关方法前调用)
|
|
2102
|
-
*
|
|
2151
|
+
*
|
|
2103
2152
|
* @param {import('playwright').Page} page
|
|
2104
2153
|
* @returns {Promise<void>}
|
|
2105
2154
|
*/
|
|
@@ -2115,7 +2164,7 @@ var Humanize = {
|
|
|
2115
2164
|
},
|
|
2116
2165
|
/**
|
|
2117
2166
|
* 人类化鼠标移动 - 使用 ghost-cursor 移动到指定位置或元素
|
|
2118
|
-
*
|
|
2167
|
+
*
|
|
2119
2168
|
* @param {import('playwright').Page} page
|
|
2120
2169
|
* @param {string|{x: number, y: number}|import('playwright').ElementHandle} target - CSS选择器、坐标对象或元素句柄
|
|
2121
2170
|
*/
|
|
@@ -2157,7 +2206,7 @@ var Humanize = {
|
|
|
2157
2206
|
/**
|
|
2158
2207
|
* 渐进式滚动到元素可见(仅处理 Y 轴滚动)
|
|
2159
2208
|
* 返回 restore 方法,用于将滚动容器恢复到原位置
|
|
2160
|
-
*
|
|
2209
|
+
*
|
|
2161
2210
|
* @param {import('playwright').Page} page
|
|
2162
2211
|
* @param {string|import('playwright').ElementHandle} target - CSS 选择器或元素句柄
|
|
2163
2212
|
* @param {Object} [options]
|
|
@@ -2228,7 +2277,7 @@ var Humanize = {
|
|
|
2228
2277
|
return { code: "VISIBLE", isFixed };
|
|
2229
2278
|
});
|
|
2230
2279
|
};
|
|
2231
|
-
const
|
|
2280
|
+
const getScrollableRect2 = async () => {
|
|
2232
2281
|
return await element.evaluate((el) => {
|
|
2233
2282
|
const isScrollable = (node) => {
|
|
2234
2283
|
const style = window.getComputedStyle(node);
|
|
@@ -2271,7 +2320,7 @@ var Humanize = {
|
|
|
2271
2320
|
if (status.code === "OBSTRUCTED" && status.obstruction) {
|
|
2272
2321
|
logger6.debug(`humanScroll | \u88AB\u4EE5\u4E0B\u5143\u7D20\u906E\u6321 <${status.obstruction.tag} id="${status.obstruction.id}">`);
|
|
2273
2322
|
}
|
|
2274
|
-
const scrollRect = await
|
|
2323
|
+
const scrollRect = await getScrollableRect2();
|
|
2275
2324
|
if (!scrollRect && status.isFixed) {
|
|
2276
2325
|
logger6.warn("humanScroll | fixed \u5BB9\u5668\u5185\u4E14\u65E0\u53EF\u6EDA\u52A8\u7956\u5148\uFF0C\u8DF3\u8FC7\u6EDA\u52A8");
|
|
2277
2326
|
return { element, didScroll };
|
|
@@ -2318,7 +2367,7 @@ var Humanize = {
|
|
|
2318
2367
|
},
|
|
2319
2368
|
/**
|
|
2320
2369
|
* 人类化点击 - 使用 ghost-cursor 模拟人类鼠标移动轨迹并点击
|
|
2321
|
-
*
|
|
2370
|
+
*
|
|
2322
2371
|
* @param {import('playwright').Page} page
|
|
2323
2372
|
* @param {string|import('playwright').ElementHandle} [target] - CSS 选择器或元素句柄。如果为空,则点击当前鼠标位置
|
|
2324
2373
|
* @param {Object} [options]
|
|
@@ -2424,7 +2473,7 @@ var Humanize = {
|
|
|
2424
2473
|
* @param {import('playwright').Page} page
|
|
2425
2474
|
* @param {string} selector - 输入框选择器
|
|
2426
2475
|
* @param {string} text - 要输入的文本
|
|
2427
|
-
* @param {Object} [options]
|
|
2476
|
+
* @param {Object} [options]
|
|
2428
2477
|
* @param {number} [options.baseDelay=180] - 基础按键延迟 (ms),实际 ±40% 抖动
|
|
2429
2478
|
* @param {number} [options.pauseProbability=0.08] - 停顿概率 (0-1)
|
|
2430
2479
|
* @param {number} [options.pauseBase=800] - 停顿时长基础值 (ms),实际 ±50% 抖动
|
|
@@ -2552,6 +2601,787 @@ var Humanize = {
|
|
|
2552
2601
|
}
|
|
2553
2602
|
};
|
|
2554
2603
|
|
|
2604
|
+
// src/internals/humanize/shared.js
|
|
2605
|
+
var import_delay2 = __toESM(require("delay"), 1);
|
|
2606
|
+
var jitterMs = (base, jitterPercent = 0.3) => {
|
|
2607
|
+
const jitter = Number(base || 0) * Number(jitterPercent || 0) * (Math.random() * 2 - 1);
|
|
2608
|
+
return Math.max(10, Math.round(Number(base || 0) + jitter));
|
|
2609
|
+
};
|
|
2610
|
+
var randomPointInBox = (box, paddingRatio = 0.24) => {
|
|
2611
|
+
const safePaddingX = Math.max(2, Math.min(box.width * paddingRatio, box.width / 2));
|
|
2612
|
+
const safePaddingY = Math.max(2, Math.min(box.height * paddingRatio, box.height / 2));
|
|
2613
|
+
const x = box.x + safePaddingX + Math.random() * Math.max(1, box.width - safePaddingX * 2);
|
|
2614
|
+
const y = box.y + safePaddingY + Math.random() * Math.max(1, box.height - safePaddingY * 2);
|
|
2615
|
+
return { x, y };
|
|
2616
|
+
};
|
|
2617
|
+
var resolveElement = async (page, target, { throwOnMissing = true } = {}) => {
|
|
2618
|
+
if (target == null) return null;
|
|
2619
|
+
let element = target;
|
|
2620
|
+
if (typeof target === "string") {
|
|
2621
|
+
element = await page.$(target);
|
|
2622
|
+
}
|
|
2623
|
+
if (!element) {
|
|
2624
|
+
if (throwOnMissing) {
|
|
2625
|
+
throw new Error(`\u627E\u4E0D\u5230\u5143\u7D20 ${String(target)}`);
|
|
2626
|
+
}
|
|
2627
|
+
return null;
|
|
2628
|
+
}
|
|
2629
|
+
return element;
|
|
2630
|
+
};
|
|
2631
|
+
var waitJitter = (base, jitterPercent = 0.3) => (0, import_delay2.default)(jitterMs(base, jitterPercent));
|
|
2632
|
+
|
|
2633
|
+
// src/internals/humanize/mobile.js
|
|
2634
|
+
var logger7 = createInternalLogger("Humanize.Mobile");
|
|
2635
|
+
var initializedPages = /* @__PURE__ */ new WeakSet();
|
|
2636
|
+
var clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
2637
|
+
var resolveViewport = (page) => page?.viewportSize?.() || { width: 390, height: 844 };
|
|
2638
|
+
var describeTarget = (target) => {
|
|
2639
|
+
if (target == null) return "Current Position";
|
|
2640
|
+
return typeof target === "string" ? target : "ElementHandle";
|
|
2641
|
+
};
|
|
2642
|
+
var clipBoxToViewport = (box, viewport) => {
|
|
2643
|
+
if (!box || !viewport) return box;
|
|
2644
|
+
const left = clamp(box.x, 0, viewport.width);
|
|
2645
|
+
const top = clamp(box.y, 0, viewport.height);
|
|
2646
|
+
const right = clamp(box.x + box.width, 0, viewport.width);
|
|
2647
|
+
const bottom = clamp(box.y + box.height, 0, viewport.height);
|
|
2648
|
+
if (right - left > 1 && bottom - top > 1) {
|
|
2649
|
+
return {
|
|
2650
|
+
x: left,
|
|
2651
|
+
y: top,
|
|
2652
|
+
width: right - left,
|
|
2653
|
+
height: bottom - top
|
|
2654
|
+
};
|
|
2655
|
+
}
|
|
2656
|
+
return box;
|
|
2657
|
+
};
|
|
2658
|
+
var checkElementVisibility = async (element) => {
|
|
2659
|
+
return element.evaluate((el) => {
|
|
2660
|
+
const targetStyle = window.getComputedStyle(el);
|
|
2661
|
+
if (!targetStyle || targetStyle.display === "none" || targetStyle.visibility === "hidden" || targetStyle.visibility === "collapse") {
|
|
2662
|
+
return { code: "NOT_INTERACTABLE", reason: "\u5143\u7D20\u4E0D\u53EF\u89C1", direction: "down" };
|
|
2663
|
+
}
|
|
2664
|
+
const rect = el.getBoundingClientRect();
|
|
2665
|
+
if (!rect || rect.width <= 0 || rect.height <= 0) {
|
|
2666
|
+
return { code: "ZERO_DIMENSIONS", reason: "\u5C3A\u5BF8\u4E3A\u96F6", direction: "down" };
|
|
2667
|
+
}
|
|
2668
|
+
const viewW = window.innerWidth;
|
|
2669
|
+
const viewH = window.innerHeight;
|
|
2670
|
+
const centerY = rect.top + rect.height / 2;
|
|
2671
|
+
let clipLeft = 0;
|
|
2672
|
+
let clipRight = viewW;
|
|
2673
|
+
let clipTop = 0;
|
|
2674
|
+
let clipBottom = viewH;
|
|
2675
|
+
let isFixed = false;
|
|
2676
|
+
for (let node = el; node && node !== document.body; node = node.parentElement) {
|
|
2677
|
+
const style = window.getComputedStyle(node);
|
|
2678
|
+
if (style && (style.position === "fixed" || style.position === "sticky")) {
|
|
2679
|
+
isFixed = true;
|
|
2680
|
+
break;
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
for (let node = el.parentElement; node && node !== document.body; node = node.parentElement) {
|
|
2684
|
+
const style = window.getComputedStyle(node);
|
|
2685
|
+
if (!style) continue;
|
|
2686
|
+
const clipsY = ["auto", "scroll", "overlay", "hidden", "clip"].includes(style.overflowY);
|
|
2687
|
+
const clipsX = ["auto", "scroll", "overlay", "hidden", "clip"].includes(style.overflowX);
|
|
2688
|
+
if (!clipsX && !clipsY) continue;
|
|
2689
|
+
const nodeRect = node.getBoundingClientRect();
|
|
2690
|
+
if (!nodeRect || nodeRect.width <= 0 || nodeRect.height <= 0) continue;
|
|
2691
|
+
if (clipsX) {
|
|
2692
|
+
clipLeft = Math.max(clipLeft, nodeRect.left);
|
|
2693
|
+
clipRight = Math.min(clipRight, nodeRect.right);
|
|
2694
|
+
}
|
|
2695
|
+
if (clipsY) {
|
|
2696
|
+
clipTop = Math.max(clipTop, nodeRect.top);
|
|
2697
|
+
clipBottom = Math.min(clipBottom, nodeRect.bottom);
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
const visibleLeft = Math.max(clipLeft, Math.min(clipRight, rect.left));
|
|
2701
|
+
const visibleRight = Math.max(clipLeft, Math.min(clipRight, rect.right));
|
|
2702
|
+
const visibleTop = Math.max(clipTop, Math.min(clipBottom, rect.top));
|
|
2703
|
+
const visibleBottom = Math.max(clipTop, Math.min(clipBottom, rect.bottom));
|
|
2704
|
+
const visibleWidth = visibleRight - visibleLeft;
|
|
2705
|
+
const visibleHeight = visibleBottom - visibleTop;
|
|
2706
|
+
const cx = visibleLeft + visibleWidth / 2;
|
|
2707
|
+
const cy = visibleTop + visibleHeight / 2;
|
|
2708
|
+
if (visibleWidth <= 1 || visibleHeight <= 1) {
|
|
2709
|
+
return {
|
|
2710
|
+
code: "OUT_OF_VIEWPORT",
|
|
2711
|
+
reason: "\u4E0D\u5728\u89C6\u53E3\u5185",
|
|
2712
|
+
direction: centerY < clipTop ? "up" : "down",
|
|
2713
|
+
cy: centerY,
|
|
2714
|
+
viewH,
|
|
2715
|
+
isFixed
|
|
2716
|
+
};
|
|
2717
|
+
}
|
|
2718
|
+
const interactiveSelector = [
|
|
2719
|
+
"button",
|
|
2720
|
+
'[role="button"]',
|
|
2721
|
+
"a[href]",
|
|
2722
|
+
"label",
|
|
2723
|
+
"input",
|
|
2724
|
+
"textarea",
|
|
2725
|
+
"select",
|
|
2726
|
+
"summary",
|
|
2727
|
+
'[contenteditable="true"]',
|
|
2728
|
+
'[tabindex]:not([tabindex="-1"])'
|
|
2729
|
+
].join(",");
|
|
2730
|
+
const targetInteractive = typeof el.closest === "function" ? el.closest(interactiveSelector) : null;
|
|
2731
|
+
const sameInteractiveTarget = (pointElement) => {
|
|
2732
|
+
if (!pointElement) return false;
|
|
2733
|
+
if (pointElement === el || el.contains(pointElement) || pointElement.contains(el)) {
|
|
2734
|
+
return true;
|
|
2735
|
+
}
|
|
2736
|
+
if (!targetInteractive || typeof pointElement.closest !== "function") {
|
|
2737
|
+
return false;
|
|
2738
|
+
}
|
|
2739
|
+
return pointElement.closest(interactiveSelector) === targetInteractive;
|
|
2740
|
+
};
|
|
2741
|
+
const describeElement = (node) => {
|
|
2742
|
+
if (!node) return null;
|
|
2743
|
+
const className = typeof node.className === "string" ? node.className : node.className && typeof node.className.baseVal === "string" ? node.className.baseVal : "";
|
|
2744
|
+
const rect2 = node.getBoundingClientRect?.();
|
|
2745
|
+
const style = window.getComputedStyle(node);
|
|
2746
|
+
return {
|
|
2747
|
+
tag: node.tagName,
|
|
2748
|
+
id: node.id || "",
|
|
2749
|
+
className,
|
|
2750
|
+
isFixed: Boolean(style && (style.position === "fixed" || style.position === "sticky")),
|
|
2751
|
+
top: rect2 ? rect2.top : null,
|
|
2752
|
+
bottom: rect2 ? rect2.bottom : null,
|
|
2753
|
+
left: rect2 ? rect2.left : null,
|
|
2754
|
+
right: rect2 ? rect2.right : null
|
|
2755
|
+
};
|
|
2756
|
+
};
|
|
2757
|
+
const samplePoints = [
|
|
2758
|
+
{ x: cx, y: cy },
|
|
2759
|
+
{ x: visibleLeft + Math.min(8, Math.max(1, visibleWidth * 0.25)), y: cy },
|
|
2760
|
+
{ x: visibleRight - Math.min(8, Math.max(1, visibleWidth * 0.25)), y: cy },
|
|
2761
|
+
{ x: cx, y: visibleTop + Math.min(8, Math.max(1, visibleHeight * 0.25)) },
|
|
2762
|
+
{ x: cx, y: visibleBottom - Math.min(8, Math.max(1, visibleHeight * 0.25)) }
|
|
2763
|
+
];
|
|
2764
|
+
let obstruction = null;
|
|
2765
|
+
for (const point of samplePoints) {
|
|
2766
|
+
const pointElement = document.elementFromPoint(point.x, point.y);
|
|
2767
|
+
if (sameInteractiveTarget(pointElement)) {
|
|
2768
|
+
return { code: "VISIBLE", isFixed };
|
|
2769
|
+
}
|
|
2770
|
+
obstruction = obstruction || describeElement(pointElement);
|
|
2771
|
+
}
|
|
2772
|
+
if (obstruction) {
|
|
2773
|
+
return {
|
|
2774
|
+
code: "OBSTRUCTED",
|
|
2775
|
+
reason: "\u88AB\u906E\u6321",
|
|
2776
|
+
direction: cy > viewH / 2 ? "down" : "up",
|
|
2777
|
+
obstruction,
|
|
2778
|
+
cy,
|
|
2779
|
+
viewH,
|
|
2780
|
+
isFixed
|
|
2781
|
+
};
|
|
2782
|
+
}
|
|
2783
|
+
return { code: "VISIBLE", isFixed };
|
|
2784
|
+
});
|
|
2785
|
+
};
|
|
2786
|
+
var resolveSafeTapPoint = async (element) => {
|
|
2787
|
+
return element.evaluate((el) => {
|
|
2788
|
+
const rect = el.getBoundingClientRect();
|
|
2789
|
+
if (!rect || rect.width <= 0 || rect.height <= 0) {
|
|
2790
|
+
return null;
|
|
2791
|
+
}
|
|
2792
|
+
const viewW = window.innerWidth;
|
|
2793
|
+
const viewH = window.innerHeight;
|
|
2794
|
+
let clipLeft = 0;
|
|
2795
|
+
let clipRight = viewW;
|
|
2796
|
+
let clipTop = 0;
|
|
2797
|
+
let clipBottom = viewH;
|
|
2798
|
+
for (let node = el.parentElement; node && node !== document.body; node = node.parentElement) {
|
|
2799
|
+
const style = window.getComputedStyle(node);
|
|
2800
|
+
if (!style) continue;
|
|
2801
|
+
const clipsX = ["auto", "scroll", "overlay", "hidden", "clip"].includes(style.overflowX);
|
|
2802
|
+
const clipsY = ["auto", "scroll", "overlay", "hidden", "clip"].includes(style.overflowY);
|
|
2803
|
+
if (!clipsX && !clipsY) continue;
|
|
2804
|
+
const nodeRect = node.getBoundingClientRect();
|
|
2805
|
+
if (!nodeRect || nodeRect.width <= 0 || nodeRect.height <= 0) continue;
|
|
2806
|
+
if (clipsX) {
|
|
2807
|
+
clipLeft = Math.max(clipLeft, nodeRect.left);
|
|
2808
|
+
clipRight = Math.min(clipRight, nodeRect.right);
|
|
2809
|
+
}
|
|
2810
|
+
if (clipsY) {
|
|
2811
|
+
clipTop = Math.max(clipTop, nodeRect.top);
|
|
2812
|
+
clipBottom = Math.min(clipBottom, nodeRect.bottom);
|
|
2813
|
+
}
|
|
2814
|
+
}
|
|
2815
|
+
const visibleLeft = Math.max(clipLeft, Math.min(clipRight, rect.left));
|
|
2816
|
+
const visibleRight = Math.max(clipLeft, Math.min(clipRight, rect.right));
|
|
2817
|
+
const visibleTop = Math.max(clipTop, Math.min(clipBottom, rect.top));
|
|
2818
|
+
const visibleBottom = Math.max(clipTop, Math.min(clipBottom, rect.bottom));
|
|
2819
|
+
const visibleWidth = visibleRight - visibleLeft;
|
|
2820
|
+
const visibleHeight = visibleBottom - visibleTop;
|
|
2821
|
+
if (visibleWidth <= 1 || visibleHeight <= 1) {
|
|
2822
|
+
return null;
|
|
2823
|
+
}
|
|
2824
|
+
const interactiveSelector = [
|
|
2825
|
+
"button",
|
|
2826
|
+
'[role="button"]',
|
|
2827
|
+
"a[href]",
|
|
2828
|
+
"label",
|
|
2829
|
+
"input",
|
|
2830
|
+
"textarea",
|
|
2831
|
+
"select",
|
|
2832
|
+
"summary",
|
|
2833
|
+
'[contenteditable="true"]',
|
|
2834
|
+
'[tabindex]:not([tabindex="-1"])'
|
|
2835
|
+
].join(",");
|
|
2836
|
+
const targetInteractive = typeof el.closest === "function" ? el.closest(interactiveSelector) : null;
|
|
2837
|
+
const sameInteractiveTarget = (pointElement) => {
|
|
2838
|
+
if (!pointElement) return false;
|
|
2839
|
+
if (pointElement === el || el.contains(pointElement) || pointElement.contains(el)) {
|
|
2840
|
+
return true;
|
|
2841
|
+
}
|
|
2842
|
+
if (!targetInteractive || typeof pointElement.closest !== "function") {
|
|
2843
|
+
return false;
|
|
2844
|
+
}
|
|
2845
|
+
return pointElement.closest(interactiveSelector) === targetInteractive;
|
|
2846
|
+
};
|
|
2847
|
+
const cx = visibleLeft + visibleWidth / 2;
|
|
2848
|
+
const cy = visibleTop + visibleHeight / 2;
|
|
2849
|
+
const smallX = Math.min(12, Math.max(2, visibleWidth * 0.18));
|
|
2850
|
+
const smallY = Math.min(12, Math.max(2, visibleHeight * 0.18));
|
|
2851
|
+
const points = [
|
|
2852
|
+
{ x: cx, y: cy },
|
|
2853
|
+
{ x: visibleLeft + smallX, y: visibleTop + smallY },
|
|
2854
|
+
{ x: visibleRight - smallX, y: visibleTop + smallY },
|
|
2855
|
+
{ x: visibleLeft + smallX, y: visibleBottom - smallY },
|
|
2856
|
+
{ x: visibleRight - smallX, y: visibleBottom - smallY },
|
|
2857
|
+
{ x: cx, y: visibleTop + Math.min(10, Math.max(2, visibleHeight * 0.2)) },
|
|
2858
|
+
{ x: cx, y: visibleBottom - Math.min(10, Math.max(2, visibleHeight * 0.2)) },
|
|
2859
|
+
{ x: visibleLeft + Math.min(10, Math.max(2, visibleWidth * 0.2)), y: cy },
|
|
2860
|
+
{ x: visibleRight - Math.min(10, Math.max(2, visibleWidth * 0.2)), y: cy }
|
|
2861
|
+
];
|
|
2862
|
+
const safePoints = points.filter((point) => {
|
|
2863
|
+
const pointElement = document.elementFromPoint(point.x, point.y);
|
|
2864
|
+
return sameInteractiveTarget(pointElement);
|
|
2865
|
+
});
|
|
2866
|
+
const candidates = safePoints.length ? safePoints : points;
|
|
2867
|
+
const chosen = candidates[Math.floor(Math.random() * candidates.length)];
|
|
2868
|
+
if (!chosen) return null;
|
|
2869
|
+
return {
|
|
2870
|
+
x: chosen.x,
|
|
2871
|
+
y: chosen.y
|
|
2872
|
+
};
|
|
2873
|
+
});
|
|
2874
|
+
};
|
|
2875
|
+
var getScrollableRect = async (element) => {
|
|
2876
|
+
return element.evaluate((el) => {
|
|
2877
|
+
const isScrollable = (node) => {
|
|
2878
|
+
const style = window.getComputedStyle(node);
|
|
2879
|
+
if (!style) return false;
|
|
2880
|
+
const overflowY = style.overflowY;
|
|
2881
|
+
if (!["auto", "scroll", "overlay"].includes(overflowY)) return false;
|
|
2882
|
+
return node.scrollHeight > node.clientHeight + 1;
|
|
2883
|
+
};
|
|
2884
|
+
let current = el;
|
|
2885
|
+
while (current && current !== document.body) {
|
|
2886
|
+
if (isScrollable(current)) {
|
|
2887
|
+
const rect = current.getBoundingClientRect();
|
|
2888
|
+
if (rect && rect.width > 0 && rect.height > 0) {
|
|
2889
|
+
return {
|
|
2890
|
+
x: rect.x,
|
|
2891
|
+
y: rect.y,
|
|
2892
|
+
width: rect.width,
|
|
2893
|
+
height: rect.height
|
|
2894
|
+
};
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
current = current.parentElement;
|
|
2898
|
+
}
|
|
2899
|
+
return null;
|
|
2900
|
+
});
|
|
2901
|
+
};
|
|
2902
|
+
var scrollScrollableAncestor = async (element, deltaY) => {
|
|
2903
|
+
return element.evaluate((el, amount) => {
|
|
2904
|
+
const isScrollable = (node) => {
|
|
2905
|
+
const style = window.getComputedStyle(node);
|
|
2906
|
+
if (!style) return false;
|
|
2907
|
+
const overflowY = style.overflowY;
|
|
2908
|
+
if (!["auto", "scroll", "overlay"].includes(overflowY)) return false;
|
|
2909
|
+
return node.scrollHeight > node.clientHeight + 1;
|
|
2910
|
+
};
|
|
2911
|
+
let current = el;
|
|
2912
|
+
while (current && current !== document.body) {
|
|
2913
|
+
if (isScrollable(current)) {
|
|
2914
|
+
const beforeTop2 = current.scrollTop;
|
|
2915
|
+
current.scrollTop = beforeTop2 + amount;
|
|
2916
|
+
return {
|
|
2917
|
+
scroller: true,
|
|
2918
|
+
moved: current.scrollTop !== beforeTop2,
|
|
2919
|
+
scrollTop: current.scrollTop
|
|
2920
|
+
};
|
|
2921
|
+
}
|
|
2922
|
+
current = current.parentElement;
|
|
2923
|
+
}
|
|
2924
|
+
const beforeTop = window.scrollY;
|
|
2925
|
+
window.scrollBy(0, amount);
|
|
2926
|
+
return {
|
|
2927
|
+
scroller: null,
|
|
2928
|
+
moved: window.scrollY !== beforeTop,
|
|
2929
|
+
scrollTop: window.scrollY
|
|
2930
|
+
};
|
|
2931
|
+
}, deltaY);
|
|
2932
|
+
};
|
|
2933
|
+
var dispatchTouchSwipe = async (page, deltaY, options = {}) => {
|
|
2934
|
+
const viewport = resolveViewport(page);
|
|
2935
|
+
const rawRect = options.rect || null;
|
|
2936
|
+
const rect = rawRect ? {
|
|
2937
|
+
x: clamp(rawRect.x, 0, viewport.width),
|
|
2938
|
+
y: clamp(rawRect.y, 0, viewport.height),
|
|
2939
|
+
width: clamp(rawRect.width, 0, viewport.width),
|
|
2940
|
+
height: clamp(rawRect.height, 0, viewport.height)
|
|
2941
|
+
} : null;
|
|
2942
|
+
const area = rect && rect.width > 24 && rect.height > 48 ? {
|
|
2943
|
+
left: rect.x,
|
|
2944
|
+
right: Math.min(viewport.width, rect.x + rect.width),
|
|
2945
|
+
top: rect.y,
|
|
2946
|
+
bottom: Math.min(viewport.height, rect.y + rect.height)
|
|
2947
|
+
} : {
|
|
2948
|
+
left: 0,
|
|
2949
|
+
right: viewport.width,
|
|
2950
|
+
top: 0,
|
|
2951
|
+
bottom: viewport.height
|
|
2952
|
+
};
|
|
2953
|
+
const areaWidth = Math.max(1, area.right - area.left);
|
|
2954
|
+
const areaHeight = Math.max(1, area.bottom - area.top);
|
|
2955
|
+
const maxDistance = rect ? Math.max(60, areaHeight * 0.72) : Math.max(120, viewport.height * 0.72);
|
|
2956
|
+
const distance = clamp(Math.abs(deltaY), Math.min(80, maxDistance), maxDistance);
|
|
2957
|
+
const direction = deltaY >= 0 ? 1 : -1;
|
|
2958
|
+
const startX = clamp(
|
|
2959
|
+
area.left + areaWidth * (0.45 + Math.random() * 0.1),
|
|
2960
|
+
24,
|
|
2961
|
+
viewport.width - 24
|
|
2962
|
+
);
|
|
2963
|
+
const startY = direction > 0 ? clamp(area.top + areaHeight * (0.72 + Math.random() * 0.1), area.top + 24, area.bottom - 16) : clamp(area.top + areaHeight * (0.28 + Math.random() * 0.1), area.top + 16, area.bottom - 24);
|
|
2964
|
+
const endY = clamp(startY - direction * distance, Math.max(16, area.top + 12), Math.min(viewport.height - 16, area.bottom - 12));
|
|
2965
|
+
const steps = Math.max(4, Math.round(Number(options.steps || 6)));
|
|
2966
|
+
const durationMs = jitterMs(options.durationMs || 320, 0.35);
|
|
2967
|
+
let client = null;
|
|
2968
|
+
const scrollBefore = await page.evaluate(() => ({ x: window.scrollX, y: window.scrollY })).catch(() => null);
|
|
2969
|
+
try {
|
|
2970
|
+
if (page.context && typeof page.context().newCDPSession === "function") {
|
|
2971
|
+
client = await page.context().newCDPSession(page);
|
|
2972
|
+
}
|
|
2973
|
+
if (!client) throw new Error("CDP session unavailable");
|
|
2974
|
+
await client.send("Input.synthesizeScrollGesture", {
|
|
2975
|
+
x: startX,
|
|
2976
|
+
y: startY,
|
|
2977
|
+
yDistance: -direction * distance,
|
|
2978
|
+
xOverscroll: (Math.random() - 0.5) * 4,
|
|
2979
|
+
yOverscroll: (Math.random() - 0.5) * 8,
|
|
2980
|
+
speed: 700 + Math.round(Math.random() * 350),
|
|
2981
|
+
gestureSourceType: "touch"
|
|
2982
|
+
});
|
|
2983
|
+
await waitJitter(160, 0.35);
|
|
2984
|
+
const scrollAfterSynth = await page.evaluate(() => ({ x: window.scrollX, y: window.scrollY })).catch(() => null);
|
|
2985
|
+
if (scrollBefore && scrollAfterSynth && (Math.abs(scrollAfterSynth.y - scrollBefore.y) > 2 || Math.abs(scrollAfterSynth.x - scrollBefore.x) > 2)) {
|
|
2986
|
+
return true;
|
|
2987
|
+
}
|
|
2988
|
+
await client.send("Input.dispatchTouchEvent", {
|
|
2989
|
+
type: "touchStart",
|
|
2990
|
+
touchPoints: [{ x: startX, y: startY, id: 1 }]
|
|
2991
|
+
});
|
|
2992
|
+
for (let i = 1; i <= steps; i += 1) {
|
|
2993
|
+
const progress = i / steps;
|
|
2994
|
+
const eased = 1 - Math.pow(1 - progress, 2);
|
|
2995
|
+
const x = startX + (Math.random() - 0.5) * 3;
|
|
2996
|
+
const y = startY + (endY - startY) * eased + (Math.random() - 0.5) * 5;
|
|
2997
|
+
await waitJitter(durationMs / steps, 0.25);
|
|
2998
|
+
await client.send("Input.dispatchTouchEvent", {
|
|
2999
|
+
type: "touchMove",
|
|
3000
|
+
touchPoints: [{ x, y, id: 1 }]
|
|
3001
|
+
});
|
|
3002
|
+
}
|
|
3003
|
+
await client.send("Input.dispatchTouchEvent", {
|
|
3004
|
+
type: "touchEnd",
|
|
3005
|
+
touchPoints: []
|
|
3006
|
+
});
|
|
3007
|
+
await waitJitter(120, 0.35);
|
|
3008
|
+
const scrollAfterTouch = await page.evaluate(() => ({ x: window.scrollX, y: window.scrollY })).catch(() => null);
|
|
3009
|
+
if (scrollBefore && scrollAfterTouch && Math.abs(scrollAfterTouch.y - scrollBefore.y) <= 2 && Math.abs(scrollAfterTouch.x - scrollBefore.x) <= 2) {
|
|
3010
|
+
await page.evaluate((amount) => window.scrollBy(0, amount), deltaY);
|
|
3011
|
+
await waitJitter(120, 0.35);
|
|
3012
|
+
}
|
|
3013
|
+
return true;
|
|
3014
|
+
} catch (error) {
|
|
3015
|
+
logger7.debug(`touch swipe fallback: ${error?.message || error}`);
|
|
3016
|
+
try {
|
|
3017
|
+
await page.evaluate((amount) => window.scrollBy(0, amount), deltaY);
|
|
3018
|
+
await waitJitter(120, 0.35);
|
|
3019
|
+
return true;
|
|
3020
|
+
} catch {
|
|
3021
|
+
await page.mouse.wheel(0, deltaY);
|
|
3022
|
+
await waitJitter(120, 0.35);
|
|
3023
|
+
return true;
|
|
3024
|
+
}
|
|
3025
|
+
} finally {
|
|
3026
|
+
if (client && typeof client.detach === "function") {
|
|
3027
|
+
try {
|
|
3028
|
+
await client.detach();
|
|
3029
|
+
} catch {
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
}
|
|
3033
|
+
};
|
|
3034
|
+
var tapPoint = async (page, point) => {
|
|
3035
|
+
if (page.touchscreen && typeof page.touchscreen.tap === "function") {
|
|
3036
|
+
await page.touchscreen.tap(point.x, point.y);
|
|
3037
|
+
return;
|
|
3038
|
+
}
|
|
3039
|
+
await page.mouse.click(point.x, point.y);
|
|
3040
|
+
};
|
|
3041
|
+
var MobileHumanize = {
|
|
3042
|
+
jitterMs,
|
|
3043
|
+
async initializeCursor(page) {
|
|
3044
|
+
if (initializedPages.has(page)) return;
|
|
3045
|
+
initializedPages.add(page);
|
|
3046
|
+
logger7.debug("initializeCursor: mobile mode uses touch gestures, cursor init skipped");
|
|
3047
|
+
},
|
|
3048
|
+
async humanMove(page, target) {
|
|
3049
|
+
logger7.debug(`humanMove: mobile no-op target=${typeof target === "string" ? target : "element/coords"}`);
|
|
3050
|
+
if (typeof target === "string" || target && typeof target.boundingBox === "function") {
|
|
3051
|
+
const element = await resolveElement(page, target, { throwOnMissing: false });
|
|
3052
|
+
if (!element) {
|
|
3053
|
+
return false;
|
|
3054
|
+
}
|
|
3055
|
+
}
|
|
3056
|
+
await waitJitter(80, 0.4);
|
|
3057
|
+
return true;
|
|
3058
|
+
},
|
|
3059
|
+
async humanScroll(page, target, options = {}) {
|
|
3060
|
+
const {
|
|
3061
|
+
maxSteps = 12,
|
|
3062
|
+
minStep = 180,
|
|
3063
|
+
maxStep = 520,
|
|
3064
|
+
maxDurationMs = maxSteps * 280 + 1200,
|
|
3065
|
+
throwOnMissing = false
|
|
3066
|
+
} = options;
|
|
3067
|
+
const targetDesc = describeTarget(target);
|
|
3068
|
+
logger7.start("humanScroll", `target=${targetDesc}`);
|
|
3069
|
+
const element = await resolveElement(page, target, { throwOnMissing });
|
|
3070
|
+
if (!element) {
|
|
3071
|
+
logger7.warn(`humanScroll | \u5143\u7D20\u672A\u627E\u5230: ${targetDesc}`);
|
|
3072
|
+
return { element: null, didScroll: false, restore: null };
|
|
3073
|
+
}
|
|
3074
|
+
const startTime = Date.now();
|
|
3075
|
+
let didScroll = false;
|
|
3076
|
+
for (let i = 0; i < maxSteps; i += 1) {
|
|
3077
|
+
const status = await checkElementVisibility(element);
|
|
3078
|
+
if (status.code === "VISIBLE") {
|
|
3079
|
+
if (status.isFixed) {
|
|
3080
|
+
logger7.info("humanScroll | fixed/sticky \u5BB9\u5668\u5185\uFF0C\u8DF3\u8FC7\u6EDA\u52A8");
|
|
3081
|
+
} else {
|
|
3082
|
+
logger7.debug("humanScroll | \u5143\u7D20\u53EF\u89C1\u4E14\u65E0\u906E\u6321");
|
|
3083
|
+
}
|
|
3084
|
+
logger7.success("humanScroll", didScroll ? "\u5DF2\u6EDA\u52A8" : "\u65E0\u9700\u6EDA\u52A8");
|
|
3085
|
+
return { element, didScroll, restore: null };
|
|
3086
|
+
}
|
|
3087
|
+
if (status.code === "ZERO_DIMENSIONS" || status.code === "NOT_INTERACTABLE") {
|
|
3088
|
+
logger7.warn(`humanScroll | \u5143\u7D20\u4E0D\u53EF\u6EDA\u52A8\u81F3\u53EF\u70B9\u51FB\u72B6\u6001: ${status.reason || status.code}`);
|
|
3089
|
+
return { element, didScroll, restore: null };
|
|
3090
|
+
}
|
|
3091
|
+
const scrollRect = await getScrollableRect(element);
|
|
3092
|
+
if (!scrollRect && status.isFixed && status.code === "OBSTRUCTED") {
|
|
3093
|
+
logger7.warn(`humanScroll | fixed/sticky \u76EE\u6807\u88AB\u906E\u6321\uFF0C\u6EDA\u52A8\u65E0\u6CD5\u89E3\u9664 (${status.obstruction?.tag || "unknown"})`);
|
|
3094
|
+
return { element, didScroll, restore: null };
|
|
3095
|
+
}
|
|
3096
|
+
if (Date.now() - startTime > maxDurationMs) {
|
|
3097
|
+
logger7.warn(`humanScroll | mobile timeout (${maxDurationMs}ms, status=${status.code}, direction=${status.direction || "unknown"}, fixed=${Boolean(status.isFixed)})`);
|
|
3098
|
+
return { element, didScroll, restore: null };
|
|
3099
|
+
}
|
|
3100
|
+
const stepMin = scrollRect ? Math.min(minStep, Math.max(60, scrollRect.height * 0.4)) : minStep;
|
|
3101
|
+
const stepMax = scrollRect ? Math.min(maxStep, Math.max(stepMin + 40, scrollRect.height * 0.8)) : maxStep;
|
|
3102
|
+
logger7.debug(`humanScroll | \u6B65\u9AA4 ${i + 1}/${maxSteps}: ${status.reason || status.code} ${status.direction ? `(${status.direction})` : ""}`);
|
|
3103
|
+
const distance = stepMin + Math.random() * Math.max(1, stepMax - stepMin);
|
|
3104
|
+
let deltaY = status.direction === "up" ? -distance : distance;
|
|
3105
|
+
if (status.code === "OBSTRUCTED") {
|
|
3106
|
+
if (status.obstruction?.isFixed && status.obstruction.top != null) {
|
|
3107
|
+
const obstructionMiddle = (Number(status.obstruction.top || 0) + Number(status.obstruction.bottom || status.obstruction.top || 0)) / 2;
|
|
3108
|
+
const visibleMiddle = scrollRect ? scrollRect.y + scrollRect.height / 2 : status.viewH / 2;
|
|
3109
|
+
deltaY = obstructionMiddle < visibleMiddle ? -distance : distance;
|
|
3110
|
+
} else {
|
|
3111
|
+
const halfY = scrollRect ? scrollRect.y + scrollRect.height / 2 : status.viewH / 2;
|
|
3112
|
+
deltaY = status.cy > halfY ? distance : -distance;
|
|
3113
|
+
}
|
|
3114
|
+
}
|
|
3115
|
+
const beforeWindowState = scrollRect ? await page.evaluate(() => ({ x: window.scrollX, y: window.scrollY })) : null;
|
|
3116
|
+
const beforeState = scrollRect ? await element.evaluate((el) => {
|
|
3117
|
+
const isScrollable = (node) => {
|
|
3118
|
+
const style = window.getComputedStyle(node);
|
|
3119
|
+
if (!style) return false;
|
|
3120
|
+
const overflowY = style.overflowY;
|
|
3121
|
+
if (!["auto", "scroll", "overlay"].includes(overflowY)) return false;
|
|
3122
|
+
return node.scrollHeight > node.clientHeight + 1;
|
|
3123
|
+
};
|
|
3124
|
+
let current = el;
|
|
3125
|
+
while (current && current !== document.body) {
|
|
3126
|
+
if (isScrollable(current)) {
|
|
3127
|
+
return {
|
|
3128
|
+
kind: "element",
|
|
3129
|
+
top: current.scrollTop,
|
|
3130
|
+
left: current.scrollLeft
|
|
3131
|
+
};
|
|
3132
|
+
}
|
|
3133
|
+
current = current.parentElement;
|
|
3134
|
+
}
|
|
3135
|
+
return { kind: "window", top: window.scrollY, left: window.scrollX };
|
|
3136
|
+
}) : null;
|
|
3137
|
+
await dispatchTouchSwipe(page, deltaY, { rect: scrollRect });
|
|
3138
|
+
if (scrollRect && beforeWindowState) {
|
|
3139
|
+
const afterWindowState = await page.evaluate(() => ({ x: window.scrollX, y: window.scrollY }));
|
|
3140
|
+
if (Math.abs(afterWindowState.x - beforeWindowState.x) > 2 || Math.abs(afterWindowState.y - beforeWindowState.y) > 2) {
|
|
3141
|
+
await page.evaluate((state) => window.scrollTo(state.x, state.y), beforeWindowState);
|
|
3142
|
+
logger7.debug(`humanScroll | \u7A97\u53E3\u6EDA\u52A8\u56DE\u6536 from=${Math.round(afterWindowState.y)} to=${Math.round(beforeWindowState.y)}`);
|
|
3143
|
+
}
|
|
3144
|
+
}
|
|
3145
|
+
if (scrollRect && beforeState) {
|
|
3146
|
+
const afterState = await element.evaluate((el) => {
|
|
3147
|
+
const isScrollable = (node) => {
|
|
3148
|
+
const style = window.getComputedStyle(node);
|
|
3149
|
+
if (!style) return false;
|
|
3150
|
+
const overflowY = style.overflowY;
|
|
3151
|
+
if (!["auto", "scroll", "overlay"].includes(overflowY)) return false;
|
|
3152
|
+
return node.scrollHeight > node.clientHeight + 1;
|
|
3153
|
+
};
|
|
3154
|
+
let current = el;
|
|
3155
|
+
while (current && current !== document.body) {
|
|
3156
|
+
if (isScrollable(current)) {
|
|
3157
|
+
return {
|
|
3158
|
+
kind: "element",
|
|
3159
|
+
top: current.scrollTop,
|
|
3160
|
+
left: current.scrollLeft
|
|
3161
|
+
};
|
|
3162
|
+
}
|
|
3163
|
+
current = current.parentElement;
|
|
3164
|
+
}
|
|
3165
|
+
return { kind: "window", top: window.scrollY, left: window.scrollX };
|
|
3166
|
+
});
|
|
3167
|
+
const topDelta = Number(afterState.top || 0) - Number(beforeState.top || 0);
|
|
3168
|
+
const leftDelta = Number(afterState.left || 0) - Number(beforeState.left || 0);
|
|
3169
|
+
const expectedDelta = Number(deltaY || 0);
|
|
3170
|
+
const moved = beforeState.kind !== afterState.kind || Math.abs(topDelta) > 2 || Math.abs(leftDelta) > 2;
|
|
3171
|
+
if (!moved) {
|
|
3172
|
+
const fallback = await scrollScrollableAncestor(element, deltaY);
|
|
3173
|
+
logger7.debug(`humanScroll | \u5BB9\u5668\u89E6\u6478\u65E0\u6548\uFF0C\u76F4\u63A5\u6EDA\u52A8 fallback=${fallback.scroller ? "ancestor" : "window"} top=${Math.round(fallback.scrollTop || 0)}`);
|
|
3174
|
+
} else if (beforeState.kind === afterState.kind && Math.abs(expectedDelta) > 24 && Math.sign(topDelta || expectedDelta) === Math.sign(expectedDelta) && Math.abs(topDelta) < Math.min(Math.abs(expectedDelta) * 0.45, 96)) {
|
|
3175
|
+
const residualDelta = expectedDelta - topDelta;
|
|
3176
|
+
if (Math.sign(residualDelta) === Math.sign(expectedDelta) && Math.abs(residualDelta) > 24) {
|
|
3177
|
+
const fallback = await scrollScrollableAncestor(element, residualDelta);
|
|
3178
|
+
logger7.debug(`humanScroll | \u5BB9\u5668\u89E6\u6478\u8DDD\u79BB\u4E0D\u8DB3\uFF0C\u8865\u507F\u6EDA\u52A8 fallback=${fallback.scroller ? "ancestor" : "window"} top=${Math.round(fallback.scrollTop || 0)}`);
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
}
|
|
3182
|
+
didScroll = true;
|
|
3183
|
+
}
|
|
3184
|
+
try {
|
|
3185
|
+
await element.scrollIntoViewIfNeeded?.();
|
|
3186
|
+
await waitJitter(80, 0.3);
|
|
3187
|
+
const finalStatus = await checkElementVisibility(element);
|
|
3188
|
+
if (finalStatus.code === "VISIBLE") {
|
|
3189
|
+
logger7.info("humanScroll | \u539F\u751F scrollIntoViewIfNeeded \u515C\u5E95\u6210\u529F");
|
|
3190
|
+
logger7.success("humanScroll", didScroll ? "\u5DF2\u6EDA\u52A8" : "\u65E0\u9700\u6EDA\u52A8");
|
|
3191
|
+
return { element, didScroll: true, restore: null };
|
|
3192
|
+
}
|
|
3193
|
+
} catch (fallbackError) {
|
|
3194
|
+
logger7.debug(`humanScroll | native fallback failed: ${fallbackError?.message || fallbackError}`);
|
|
3195
|
+
}
|
|
3196
|
+
logger7.warn(`humanScroll | \u5728 ${maxSteps} \u6B65\u540E\u65E0\u6CD5\u786E\u4FDD\u53EF\u89C1\u6027`);
|
|
3197
|
+
return { element, didScroll, restore: null };
|
|
3198
|
+
},
|
|
3199
|
+
async humanClick(page, target, options = {}) {
|
|
3200
|
+
const {
|
|
3201
|
+
reactionDelay = 220,
|
|
3202
|
+
throwOnMissing = true,
|
|
3203
|
+
scrollIfNeeded = true
|
|
3204
|
+
} = options;
|
|
3205
|
+
const targetDesc = describeTarget(target);
|
|
3206
|
+
logger7.start("humanClick", `target=${targetDesc}`);
|
|
3207
|
+
try {
|
|
3208
|
+
if (target == null) {
|
|
3209
|
+
const viewport = resolveViewport(page);
|
|
3210
|
+
await waitJitter(reactionDelay, 0.45);
|
|
3211
|
+
await tapPoint(page, {
|
|
3212
|
+
x: viewport.width * (0.45 + Math.random() * 0.1),
|
|
3213
|
+
y: viewport.height * (0.48 + Math.random() * 0.12)
|
|
3214
|
+
});
|
|
3215
|
+
logger7.success("humanClick", "Tapped current position");
|
|
3216
|
+
return true;
|
|
3217
|
+
}
|
|
3218
|
+
const element = await resolveElement(page, target, { throwOnMissing });
|
|
3219
|
+
if (!element) {
|
|
3220
|
+
logger7.warn(`humanClick: \u5143\u7D20\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7\u70B9\u51FB ${targetDesc}`);
|
|
3221
|
+
return false;
|
|
3222
|
+
}
|
|
3223
|
+
if (scrollIfNeeded) {
|
|
3224
|
+
await MobileHumanize.humanScroll(page, element, { throwOnMissing });
|
|
3225
|
+
}
|
|
3226
|
+
const status = await checkElementVisibility(element).catch(() => null);
|
|
3227
|
+
if (status && (status.code === "ZERO_DIMENSIONS" || status.code === "NOT_INTERACTABLE")) {
|
|
3228
|
+
const message = `\u5143\u7D20\u4E0D\u53EF\u70B9\u51FB: ${status.reason || status.code}`;
|
|
3229
|
+
if (throwOnMissing) throw new Error(message);
|
|
3230
|
+
logger7.warn(`humanClick: ${message}\uFF0C\u8DF3\u8FC7\u70B9\u51FB`);
|
|
3231
|
+
return false;
|
|
3232
|
+
}
|
|
3233
|
+
const box = await element.boundingBox();
|
|
3234
|
+
if (!box) {
|
|
3235
|
+
if (throwOnMissing) throw new Error("\u65E0\u6CD5\u83B7\u53D6\u5143\u7D20\u4F4D\u7F6E");
|
|
3236
|
+
logger7.warn("humanClick: \u65E0\u6CD5\u83B7\u53D6\u4F4D\u7F6E\uFF0C\u8DF3\u8FC7\u70B9\u51FB");
|
|
3237
|
+
return false;
|
|
3238
|
+
}
|
|
3239
|
+
await waitJitter(reactionDelay, 0.45);
|
|
3240
|
+
const safePoint = await resolveSafeTapPoint(element).catch(() => null);
|
|
3241
|
+
const tapTarget = safePoint || randomPointInBox(clipBoxToViewport(box, resolveViewport(page)), 0.2);
|
|
3242
|
+
await tapPoint(page, tapTarget);
|
|
3243
|
+
await waitJitter(120, 0.35);
|
|
3244
|
+
logger7.success("humanClick");
|
|
3245
|
+
return true;
|
|
3246
|
+
} catch (error) {
|
|
3247
|
+
logger7.fail("humanClick", error);
|
|
3248
|
+
throw error;
|
|
3249
|
+
}
|
|
3250
|
+
},
|
|
3251
|
+
async randomSleep(baseMs, jitterPercent = 0.3) {
|
|
3252
|
+
await waitJitter(baseMs, jitterPercent);
|
|
3253
|
+
},
|
|
3254
|
+
async simulateGaze(page, baseDurationMs = 2500) {
|
|
3255
|
+
const durationMs = jitterMs(baseDurationMs, 0.4);
|
|
3256
|
+
const startTime = Date.now();
|
|
3257
|
+
while (Date.now() - startTime < durationMs) {
|
|
3258
|
+
if (Math.random() < 0.28) {
|
|
3259
|
+
const distance = 70 + Math.random() * 120;
|
|
3260
|
+
await dispatchTouchSwipe(page, Math.random() < 0.7 ? distance : -distance, {
|
|
3261
|
+
durationMs: 180,
|
|
3262
|
+
steps: 4
|
|
3263
|
+
});
|
|
3264
|
+
} else {
|
|
3265
|
+
await waitJitter(420, 0.55);
|
|
3266
|
+
}
|
|
3267
|
+
}
|
|
3268
|
+
},
|
|
3269
|
+
async humanType(page, selector, text, options = {}) {
|
|
3270
|
+
const {
|
|
3271
|
+
baseDelay = 160,
|
|
3272
|
+
pauseProbability = 0.08,
|
|
3273
|
+
pauseBase = 700
|
|
3274
|
+
} = options;
|
|
3275
|
+
await MobileHumanize.humanClick(page, selector, { scrollIfNeeded: true });
|
|
3276
|
+
await waitJitter(220, 0.45);
|
|
3277
|
+
for (let i = 0; i < String(text).length; i += 1) {
|
|
3278
|
+
const char = String(text)[i];
|
|
3279
|
+
const charDelay = /[,.!?;:,。!?;:]/.test(char) ? jitterMs(baseDelay * 1.45, 0.4) : jitterMs(char === " " ? baseDelay * 0.65 : baseDelay, 0.4);
|
|
3280
|
+
await page.keyboard.type(char);
|
|
3281
|
+
await waitJitter(charDelay, 0.15);
|
|
3282
|
+
if (Math.random() < pauseProbability && i < String(text).length - 1) {
|
|
3283
|
+
await waitJitter(pauseBase, 0.5);
|
|
3284
|
+
}
|
|
3285
|
+
}
|
|
3286
|
+
},
|
|
3287
|
+
async humanClear(page, selector) {
|
|
3288
|
+
const locator = page.locator(selector);
|
|
3289
|
+
await MobileHumanize.humanClick(page, locator, { scrollIfNeeded: true });
|
|
3290
|
+
await waitJitter(160, 0.4);
|
|
3291
|
+
await locator.evaluate((el) => {
|
|
3292
|
+
if ("value" in el) {
|
|
3293
|
+
el.value = "";
|
|
3294
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3295
|
+
el.dispatchEvent(new Event("change", { bubbles: true }));
|
|
3296
|
+
return;
|
|
3297
|
+
}
|
|
3298
|
+
el.textContent = "";
|
|
3299
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3300
|
+
});
|
|
3301
|
+
},
|
|
3302
|
+
async warmUpBrowsing(page, baseDuration = 3500) {
|
|
3303
|
+
const durationMs = jitterMs(baseDuration, 0.4);
|
|
3304
|
+
const startTime = Date.now();
|
|
3305
|
+
while (Date.now() - startTime < durationMs) {
|
|
3306
|
+
const action = Math.random();
|
|
3307
|
+
if (action < 0.5) {
|
|
3308
|
+
await dispatchTouchSwipe(page, 120 + Math.random() * 220, {
|
|
3309
|
+
durationMs: 240,
|
|
3310
|
+
steps: 5
|
|
3311
|
+
});
|
|
3312
|
+
} else if (action < 0.7) {
|
|
3313
|
+
await dispatchTouchSwipe(page, -(80 + Math.random() * 160), {
|
|
3314
|
+
durationMs: 220,
|
|
3315
|
+
steps: 4
|
|
3316
|
+
});
|
|
3317
|
+
} else {
|
|
3318
|
+
await waitJitter(560, 0.55);
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3321
|
+
},
|
|
3322
|
+
async naturalScroll(page, direction = "down", distance = 300, baseSteps = 5) {
|
|
3323
|
+
const steps = Math.max(3, baseSteps + Math.floor(Math.random() * 3) - 1);
|
|
3324
|
+
const actualDistance = jitterMs(distance, 0.15);
|
|
3325
|
+
const sign = direction === "down" ? 1 : -1;
|
|
3326
|
+
for (let i = 0; i < steps; i += 1) {
|
|
3327
|
+
const factor = 1 - i / steps * 0.45;
|
|
3328
|
+
await dispatchTouchSwipe(page, actualDistance / steps * factor * sign, {
|
|
3329
|
+
durationMs: 150 + i * 20,
|
|
3330
|
+
steps: 4
|
|
3331
|
+
});
|
|
3332
|
+
}
|
|
3333
|
+
}
|
|
3334
|
+
};
|
|
3335
|
+
|
|
3336
|
+
// src/humanize.js
|
|
3337
|
+
var resolveDeviceFromPage = (page) => normalizeDevice(page?.[PageRuntimeStateKey]?.device);
|
|
3338
|
+
var resolveDelegate = (page) => {
|
|
3339
|
+
return resolveDeviceFromPage(page) === Device.Mobile ? MobileHumanize : Humanize;
|
|
3340
|
+
};
|
|
3341
|
+
var callDelegate = (method, page, args) => {
|
|
3342
|
+
const delegate = resolveDelegate(page);
|
|
3343
|
+
return delegate[method](page, ...args);
|
|
3344
|
+
};
|
|
3345
|
+
var Humanize2 = {
|
|
3346
|
+
jitterMs(base, jitterPercent = 0.3) {
|
|
3347
|
+
return Humanize.jitterMs(base, jitterPercent);
|
|
3348
|
+
},
|
|
3349
|
+
initializeCursor(page) {
|
|
3350
|
+
return callDelegate("initializeCursor", page, []);
|
|
3351
|
+
},
|
|
3352
|
+
humanMove(page, target) {
|
|
3353
|
+
return callDelegate("humanMove", page, [target]);
|
|
3354
|
+
},
|
|
3355
|
+
humanScroll(page, target, options = {}) {
|
|
3356
|
+
return callDelegate("humanScroll", page, [target, options]);
|
|
3357
|
+
},
|
|
3358
|
+
humanClick(page, target, options = {}) {
|
|
3359
|
+
return callDelegate("humanClick", page, [target, options]);
|
|
3360
|
+
},
|
|
3361
|
+
randomSleep(pageOrBaseMs, maybeBaseMs, maybeJitterPercent) {
|
|
3362
|
+
if (pageOrBaseMs && typeof pageOrBaseMs === "object" && typeof pageOrBaseMs.evaluate === "function") {
|
|
3363
|
+
const delegate = resolveDelegate(pageOrBaseMs);
|
|
3364
|
+
return delegate.randomSleep(maybeBaseMs, maybeJitterPercent);
|
|
3365
|
+
}
|
|
3366
|
+
return Humanize.randomSleep(pageOrBaseMs, maybeBaseMs);
|
|
3367
|
+
},
|
|
3368
|
+
simulateGaze(page, baseDurationMs = 2500) {
|
|
3369
|
+
return callDelegate("simulateGaze", page, [baseDurationMs]);
|
|
3370
|
+
},
|
|
3371
|
+
humanType(page, selector, text, options = {}) {
|
|
3372
|
+
return callDelegate("humanType", page, [selector, text, options]);
|
|
3373
|
+
},
|
|
3374
|
+
humanClear(page, selector) {
|
|
3375
|
+
return callDelegate("humanClear", page, [selector]);
|
|
3376
|
+
},
|
|
3377
|
+
warmUpBrowsing(page, baseDuration = 3500) {
|
|
3378
|
+
return callDelegate("warmUpBrowsing", page, [baseDuration]);
|
|
3379
|
+
},
|
|
3380
|
+
naturalScroll(page, direction = "down", distance = 300, baseSteps = 5) {
|
|
3381
|
+
return callDelegate("naturalScroll", page, [direction, distance, baseSteps]);
|
|
3382
|
+
}
|
|
3383
|
+
};
|
|
3384
|
+
|
|
2555
3385
|
// src/launch.js
|
|
2556
3386
|
var import_node_child_process = require("node:child_process");
|
|
2557
3387
|
var import_fingerprint_generator = require("fingerprint-generator");
|
|
@@ -2638,7 +3468,7 @@ var ByPass = {
|
|
|
2638
3468
|
};
|
|
2639
3469
|
|
|
2640
3470
|
// src/launch.js
|
|
2641
|
-
var
|
|
3471
|
+
var logger8 = createInternalLogger("Launch");
|
|
2642
3472
|
var REQUEST_HOOK_FLAG = Symbol("playwright-toolkit-request-hook");
|
|
2643
3473
|
var injectedContexts = /* @__PURE__ */ new WeakSet();
|
|
2644
3474
|
var browserMajorVersionCache = /* @__PURE__ */ new Map();
|
|
@@ -2690,20 +3520,29 @@ var detectBrowserMajorVersion = (launcher) => {
|
|
|
2690
3520
|
});
|
|
2691
3521
|
detectedVersion = parseChromeMajorVersion(rawVersion);
|
|
2692
3522
|
} catch (error) {
|
|
2693
|
-
|
|
3523
|
+
logger8.warn(`\u8BFB\u53D6\u6D4F\u89C8\u5668\u7248\u672C\u5931\u8D25: ${error?.message || error}`);
|
|
2694
3524
|
}
|
|
2695
3525
|
browserMajorVersionCache.set(executablePath, detectedVersion);
|
|
2696
3526
|
return detectedVersion;
|
|
2697
3527
|
};
|
|
2698
|
-
var
|
|
3528
|
+
var resolveRuntimeDevice = (runtimeState = {}) => normalizeDevice(runtimeState?.device);
|
|
3529
|
+
var resolveCoreDevice = (core = {}) => {
|
|
3530
|
+
const raw = String(core?.device || "").trim().toLowerCase();
|
|
3531
|
+
if (raw === Device.Mobile) return Device.Mobile;
|
|
3532
|
+
if (raw === Device.Desktop) return Device.Desktop;
|
|
3533
|
+
return "";
|
|
3534
|
+
};
|
|
3535
|
+
var buildFingerprintGenerator = ({ locale, browserMajorVersion, device }) => {
|
|
2699
3536
|
return new import_fingerprint_generator.FingerprintGenerator(
|
|
2700
3537
|
AntiCheat.getFingerprintGeneratorOptions({
|
|
2701
3538
|
locale,
|
|
2702
|
-
browserMajorVersion
|
|
3539
|
+
browserMajorVersion,
|
|
3540
|
+
device
|
|
2703
3541
|
})
|
|
2704
3542
|
);
|
|
2705
3543
|
};
|
|
2706
3544
|
var buildReplayableBrowserProfile = (runtimeState, launcher) => {
|
|
3545
|
+
const device = resolveRuntimeDevice(runtimeState);
|
|
2707
3546
|
if (!runtimeState || !RuntimeEnv.hasLoginState(runtimeState)) {
|
|
2708
3547
|
return { runtimeState, browserProfileCore: null };
|
|
2709
3548
|
}
|
|
@@ -2713,29 +3552,34 @@ var buildReplayableBrowserProfile = (runtimeState, launcher) => {
|
|
|
2713
3552
|
const locale = DEFAULT_LOCALE;
|
|
2714
3553
|
const currentBrowserMajorVersion = detectBrowserMajorVersion(launcher);
|
|
2715
3554
|
const storedBrowserMajorVersion = Number(browserProfileCore?.browser_major_version || 0);
|
|
2716
|
-
const
|
|
3555
|
+
const storedDevice = resolveCoreDevice(browserProfileCore);
|
|
3556
|
+
const needsDeviceRebuild = storedDevice ? storedDevice !== device : device === Device.Mobile;
|
|
3557
|
+
const needsRebuild = !browserProfileCore?.fingerprint || Object.keys(browserProfileCore.fingerprint || {}).length === 0 || needsDeviceRebuild || currentBrowserMajorVersion > 0 && storedBrowserMajorVersion > 0 && storedBrowserMajorVersion !== currentBrowserMajorVersion;
|
|
2717
3558
|
if (needsRebuild) {
|
|
2718
3559
|
const generator = buildFingerprintGenerator({
|
|
2719
3560
|
locale,
|
|
2720
|
-
browserMajorVersion: currentBrowserMajorVersion
|
|
3561
|
+
browserMajorVersion: currentBrowserMajorVersion,
|
|
3562
|
+
device
|
|
2721
3563
|
});
|
|
2722
3564
|
const fingerprint = generator.getFingerprint();
|
|
2723
3565
|
const fingerprintBrowserMajorVersion = parseChromeMajorVersion(fingerprint?.fingerprint?.navigator?.userAgent || "");
|
|
2724
3566
|
browserProfileCore = {
|
|
2725
3567
|
fingerprint,
|
|
3568
|
+
device,
|
|
2726
3569
|
timezone_id: timezoneId,
|
|
2727
3570
|
locale,
|
|
2728
3571
|
browser_major_version: currentBrowserMajorVersion > 0 ? currentBrowserMajorVersion : fingerprintBrowserMajorVersion,
|
|
2729
3572
|
schema_version: DEFAULT_BROWSER_PROFILE_SCHEMA_VERSION
|
|
2730
3573
|
};
|
|
2731
3574
|
nextState = RuntimeEnv.setBrowserProfileCore(nextState, browserProfileCore);
|
|
2732
|
-
|
|
2733
|
-
`\u5DF2\u751F\u6210\u6D4F\u89C8\u5668\u6307\u7EB9\u771F\u6E90 | env=${String(nextState.envId || "-")} | version=${browserProfileCore.browser_major_version || "-"} | timezone=${timezoneId}`
|
|
3575
|
+
logger8.info(
|
|
3576
|
+
`\u5DF2\u751F\u6210\u6D4F\u89C8\u5668\u6307\u7EB9\u771F\u6E90 | env=${String(nextState.envId || "-")} | device=${device} | version=${browserProfileCore.browser_major_version || "-"} | timezone=${timezoneId}`
|
|
2734
3577
|
);
|
|
2735
3578
|
return { runtimeState: nextState, browserProfileCore };
|
|
2736
3579
|
}
|
|
2737
3580
|
const normalizedBrowserProfileCore = {
|
|
2738
3581
|
...browserProfileCore,
|
|
3582
|
+
device,
|
|
2739
3583
|
timezone_id: timezoneId,
|
|
2740
3584
|
locale,
|
|
2741
3585
|
schema_version: Number(browserProfileCore.schema_version || 0) > 0 ? Number(browserProfileCore.schema_version || 0) : DEFAULT_BROWSER_PROFILE_SCHEMA_VERSION,
|
|
@@ -2751,6 +3595,35 @@ var buildReplayableBrowserProfile = (runtimeState, launcher) => {
|
|
|
2751
3595
|
browserProfileCore: normalizedBrowserProfileCore
|
|
2752
3596
|
};
|
|
2753
3597
|
};
|
|
3598
|
+
var applyDevicePageOptions = (pageOptions = {}, { device = Device.Desktop, fingerprint = null } = {}) => {
|
|
3599
|
+
if (!pageOptions || typeof pageOptions !== "object") return;
|
|
3600
|
+
const resolvedDevice = normalizeDevice(device);
|
|
3601
|
+
const screen = fingerprint?.screen || {};
|
|
3602
|
+
const dpr = Number(screen.devicePixelRatio || 0);
|
|
3603
|
+
if (resolvedDevice === Device.Mobile) {
|
|
3604
|
+
pageOptions.isMobile = true;
|
|
3605
|
+
pageOptions.hasTouch = true;
|
|
3606
|
+
pageOptions.deviceScaleFactor = dpr > 0 ? dpr : 2;
|
|
3607
|
+
if (!pageOptions.viewport) {
|
|
3608
|
+
pageOptions.viewport = {
|
|
3609
|
+
width: Number(screen.width || 390),
|
|
3610
|
+
height: Number(screen.height || 844)
|
|
3611
|
+
};
|
|
3612
|
+
}
|
|
3613
|
+
if (!pageOptions.screen) {
|
|
3614
|
+
pageOptions.screen = {
|
|
3615
|
+
width: Number(screen.width || pageOptions.viewport.width || 390),
|
|
3616
|
+
height: Number(screen.height || pageOptions.viewport.height || 844)
|
|
3617
|
+
};
|
|
3618
|
+
}
|
|
3619
|
+
return;
|
|
3620
|
+
}
|
|
3621
|
+
pageOptions.isMobile = false;
|
|
3622
|
+
pageOptions.hasTouch = false;
|
|
3623
|
+
if (!pageOptions.deviceScaleFactor) {
|
|
3624
|
+
pageOptions.deviceScaleFactor = dpr > 0 ? dpr : 1;
|
|
3625
|
+
}
|
|
3626
|
+
};
|
|
2754
3627
|
var buildReplayBrowserPoolOptions = (browserProfileCore) => {
|
|
2755
3628
|
const fingerprintWithHeaders = browserProfileCore?.fingerprint;
|
|
2756
3629
|
const fingerprint = fingerprintWithHeaders?.fingerprint;
|
|
@@ -2783,6 +3656,10 @@ var buildReplayBrowserPoolOptions = (browserProfileCore) => {
|
|
|
2783
3656
|
if (browserProfileCore.timezone_id) {
|
|
2784
3657
|
pageOptions.timezoneId = browserProfileCore.timezone_id;
|
|
2785
3658
|
}
|
|
3659
|
+
applyDevicePageOptions(pageOptions, {
|
|
3660
|
+
device: browserProfileCore.device,
|
|
3661
|
+
fingerprint
|
|
3662
|
+
});
|
|
2786
3663
|
}
|
|
2787
3664
|
],
|
|
2788
3665
|
postPageCreateHooks: [
|
|
@@ -2812,6 +3689,7 @@ var Launch = {
|
|
|
2812
3689
|
postNavigationHooks = [],
|
|
2813
3690
|
runtimeState = null
|
|
2814
3691
|
} = normalizedOptions;
|
|
3692
|
+
const device = resolveRuntimeDevice(runtimeState);
|
|
2815
3693
|
const { byPassDomains, enableProxy, proxyUrl } = resolveProxyLaunchOptions(proxyConfiguration);
|
|
2816
3694
|
const byPassRules = ByPass.buildByPassDomainRules(byPassDomains);
|
|
2817
3695
|
const proxyMeter = enableProxy && proxyUrl ? ProxyMeterRuntime.startProxyMeter({ proxyUrl, debugMode }) : null;
|
|
@@ -2840,18 +3718,18 @@ var Launch = {
|
|
|
2840
3718
|
upstreamLabel = `${parsedProxyUrl.protocol}//${parsedProxyUrl.host}`;
|
|
2841
3719
|
} catch {
|
|
2842
3720
|
}
|
|
2843
|
-
|
|
3721
|
+
logger8.info(
|
|
2844
3722
|
`[\u4EE3\u7406\u5DF2\u542F\u7528] \u672C\u5730=${launchProxy.server} \u4E0A\u6E38=${upstreamLabel || "-"} \u76F4\u8FDE\u57DF\u540D=${(byPassDomains || []).join(",")}`
|
|
2845
3723
|
);
|
|
2846
|
-
|
|
3724
|
+
logger8.info(`[\u6D41\u91CF\u89C2\u6D4B] \u9010\u8BF7\u6C42\u8C03\u8BD5=${Boolean(debugMode) ? "\u5F00\u542F" : "\u5173\u95ED"}\uFF08\u6C47\u603B\u59CB\u7EC8\u5F00\u542F\uFF09`);
|
|
2847
3725
|
} else if (enableByPassLogger && enableProxy && !launchProxy) {
|
|
2848
|
-
|
|
2849
|
-
|
|
3726
|
+
logger8.info("[\u4EE3\u7406\u672A\u542F\u7528] enable_proxy=true \u4F46 proxy_url \u4E3A\u7A7A");
|
|
3727
|
+
logger8.info(`[\u6D41\u91CF\u89C2\u6D4B] \u9010\u8BF7\u6C42\u8C03\u8BD5=${Boolean(debugMode) ? "\u5F00\u542F" : "\u5173\u95ED"}\uFF08\u6C47\u603B\u59CB\u7EC8\u5F00\u542F\uFF09`);
|
|
2850
3728
|
} else if (enableByPassLogger && !enableProxy && proxyUrl) {
|
|
2851
|
-
|
|
2852
|
-
|
|
3729
|
+
logger8.info("[\u4EE3\u7406\u672A\u542F\u7528] enable_proxy=false \u4E14 proxy_url \u5DF2\u914D\u7F6E");
|
|
3730
|
+
logger8.info(`[\u6D41\u91CF\u89C2\u6D4B] \u9010\u8BF7\u6C42\u8C03\u8BD5=${Boolean(debugMode) ? "\u5F00\u542F" : "\u5173\u95ED"}\uFF08\u6C47\u603B\u59CB\u7EC8\u5F00\u542F\uFF09`);
|
|
2853
3731
|
} else if (enableByPassLogger) {
|
|
2854
|
-
|
|
3732
|
+
logger8.info(`[\u6D41\u91CF\u89C2\u6D4B] \u9010\u8BF7\u6C42\u8C03\u8BD5=${Boolean(debugMode) ? "\u5F00\u542F" : "\u5173\u95ED"}\uFF08\u6C47\u603B\u59CB\u7EC8\u5F00\u542F\uFF09`);
|
|
2855
3733
|
}
|
|
2856
3734
|
const onPageCreated = (page) => {
|
|
2857
3735
|
const recommendedGotoOptions = {
|
|
@@ -2873,7 +3751,7 @@ var Launch = {
|
|
|
2873
3751
|
}
|
|
2874
3752
|
if (!enableByPassLogger || byPassDomains.length === 0) return;
|
|
2875
3753
|
if (!matched || !matched.rule) return;
|
|
2876
|
-
|
|
3754
|
+
logger8.info(`[\u76F4\u8FDE\u547D\u4E2D] \u89C4\u5219=${matched.rule.pattern} \u57DF\u540D=${matched.hostname} \u8D44\u6E90\u7C7B\u578B=${resourceType} \u65B9\u6CD5=${req.method()} \u5730\u5740=${requestUrl}`);
|
|
2877
3755
|
};
|
|
2878
3756
|
page.on("request", requestHandler);
|
|
2879
3757
|
return recommendedGotoOptions;
|
|
@@ -2892,8 +3770,23 @@ var Launch = {
|
|
|
2892
3770
|
browserPoolOptions: replayBrowserPoolOptions || {
|
|
2893
3771
|
useFingerprints: true,
|
|
2894
3772
|
fingerprintOptions: {
|
|
2895
|
-
fingerprintGeneratorOptions: AntiCheat.getFingerprintGeneratorOptions(
|
|
2896
|
-
|
|
3773
|
+
fingerprintGeneratorOptions: AntiCheat.getFingerprintGeneratorOptions({
|
|
3774
|
+
locale: launchLocale,
|
|
3775
|
+
device
|
|
3776
|
+
})
|
|
3777
|
+
},
|
|
3778
|
+
prePageCreateHooks: [
|
|
3779
|
+
(_pageId, _browserController, pageOptions = {}) => {
|
|
3780
|
+
applyDevicePageOptions(pageOptions, { device });
|
|
3781
|
+
if (launchLocale) {
|
|
3782
|
+
pageOptions.locale = launchLocale;
|
|
3783
|
+
}
|
|
3784
|
+
const timezoneId = AntiCheat.getBaseConfig().timezoneId;
|
|
3785
|
+
if (timezoneId) {
|
|
3786
|
+
pageOptions.timezoneId = timezoneId;
|
|
3787
|
+
}
|
|
3788
|
+
}
|
|
3789
|
+
]
|
|
2897
3790
|
},
|
|
2898
3791
|
launchContext
|
|
2899
3792
|
};
|
|
@@ -2916,7 +3809,7 @@ var Launch = {
|
|
|
2916
3809
|
// src/live-view.js
|
|
2917
3810
|
var import_express = __toESM(require("express"), 1);
|
|
2918
3811
|
var import_apify = require("apify");
|
|
2919
|
-
var
|
|
3812
|
+
var logger9 = createInternalLogger("LiveView");
|
|
2920
3813
|
async function startLiveViewServer(liveViewKey) {
|
|
2921
3814
|
const app = (0, import_express.default)();
|
|
2922
3815
|
app.get("/", async (req, res) => {
|
|
@@ -2941,13 +3834,13 @@ async function startLiveViewServer(liveViewKey) {
|
|
|
2941
3834
|
</html>
|
|
2942
3835
|
`);
|
|
2943
3836
|
} catch (error) {
|
|
2944
|
-
|
|
3837
|
+
logger9.fail("Live View Server", error);
|
|
2945
3838
|
res.status(500).send(`\u65E0\u6CD5\u52A0\u8F7D\u5C4F\u5E55\u622A\u56FE: ${error.message}`);
|
|
2946
3839
|
}
|
|
2947
3840
|
});
|
|
2948
3841
|
const port = process.env.APIFY_CONTAINER_PORT || 4321;
|
|
2949
3842
|
app.listen(port, () => {
|
|
2950
|
-
|
|
3843
|
+
logger9.success("startLiveViewServer", `\u76D1\u542C\u7AEF\u53E3 ${port}`);
|
|
2951
3844
|
});
|
|
2952
3845
|
}
|
|
2953
3846
|
async function takeLiveScreenshot(liveViewKey, page, logMessage) {
|
|
@@ -2955,10 +3848,10 @@ async function takeLiveScreenshot(liveViewKey, page, logMessage) {
|
|
|
2955
3848
|
const buffer = await capturePageScreenshot(page, { type: "png" });
|
|
2956
3849
|
await import_apify.Actor.setValue(liveViewKey, buffer, { contentType: "image/png" });
|
|
2957
3850
|
if (logMessage) {
|
|
2958
|
-
|
|
3851
|
+
logger9.info(`(\u622A\u56FE): ${logMessage}`);
|
|
2959
3852
|
}
|
|
2960
3853
|
} catch (e) {
|
|
2961
|
-
|
|
3854
|
+
logger9.warn(`\u65E0\u6CD5\u6355\u83B7 Live View \u5C4F\u5E55\u622A\u56FE: ${e.message}`);
|
|
2962
3855
|
}
|
|
2963
3856
|
}
|
|
2964
3857
|
var useLiveView = (liveViewKey = PresetOfLiveViewKey) => {
|
|
@@ -3058,7 +3951,7 @@ var dragCaptchaWithMouse = async (page, sourceLocator, targetLocator) => {
|
|
|
3058
3951
|
};
|
|
3059
3952
|
|
|
3060
3953
|
// src/internals/captcha/bytedance.js
|
|
3061
|
-
var
|
|
3954
|
+
var logger10 = createInternalLogger("Captcha");
|
|
3062
3955
|
var DEFAULT_BYTEDANCE_CAPTCHA_OPTIONS = Object.freeze({
|
|
3063
3956
|
apiType: "31234",
|
|
3064
3957
|
maxRetries: 3,
|
|
@@ -3125,14 +4018,14 @@ var getVerifycenterCaptchaContext = async (page, options) => {
|
|
|
3125
4018
|
if (!isContainerVisible) {
|
|
3126
4019
|
return null;
|
|
3127
4020
|
}
|
|
3128
|
-
|
|
4021
|
+
logger10.info("\u68C0\u6D4B\u5230\u9A8C\u8BC1\u7801\u5BB9\u5668\uFF0C\u5F00\u59CB\u7B49\u5F85 iframe \u52A0\u8F7D\u3002");
|
|
3129
4022
|
let iframeLocator = page.locator(options.iframeSelector).first();
|
|
3130
4023
|
let isIframeVisible = await waitForVisible(
|
|
3131
4024
|
iframeLocator,
|
|
3132
4025
|
options.iframeVisibleTimeoutMs
|
|
3133
4026
|
);
|
|
3134
4027
|
if (!isIframeVisible) {
|
|
3135
|
-
|
|
4028
|
+
logger10.warn("\u672A\u5728\u9884\u671F\u9009\u62E9\u5668\u4E2D\u627E\u5230 verifycenter iframe\uFF0C\u5C1D\u8BD5\u5BB9\u5668\u5185\u4EFB\u610F iframe\u3002");
|
|
3136
4029
|
iframeLocator = captchaContainer.locator(options.iframeFallbackSelector).first();
|
|
3137
4030
|
isIframeVisible = await waitForVisible(
|
|
3138
4031
|
iframeLocator,
|
|
@@ -3142,7 +4035,7 @@ var getVerifycenterCaptchaContext = async (page, options) => {
|
|
|
3142
4035
|
if (!isIframeVisible) {
|
|
3143
4036
|
throw new Error("verifycenter iframe not found inside captcha container.");
|
|
3144
4037
|
}
|
|
3145
|
-
|
|
4038
|
+
logger10.info("\u9A8C\u8BC1\u7801 iframe \u5DF2\u53EF\u89C1\uFF0C\u5F00\u59CB\u89E3\u6790\u5185\u5BB9 frame\u3002");
|
|
3146
4039
|
const frame = await resolveContentFrame(page, iframeLocator, options);
|
|
3147
4040
|
if (!frame) {
|
|
3148
4041
|
throw new Error("Failed to resolve verifycenter iframe content frame.");
|
|
@@ -3152,7 +4045,7 @@ var getVerifycenterCaptchaContext = async (page, options) => {
|
|
|
3152
4045
|
var refreshCaptcha = async (page, frame, options) => {
|
|
3153
4046
|
const clicked = await clickCaptchaAction(frame, options.refreshTexts, options).catch(() => false);
|
|
3154
4047
|
if (!clicked) {
|
|
3155
|
-
|
|
4048
|
+
logger10.warn("Refresh button not found.");
|
|
3156
4049
|
return false;
|
|
3157
4050
|
}
|
|
3158
4051
|
await page.waitForTimeout(options.refreshWaitMs);
|
|
@@ -3193,18 +4086,18 @@ var waitForCaptchaChallengeReady = async (page, frame, options) => {
|
|
|
3193
4086
|
const imageCount = await sourceImages.count().catch(() => 0);
|
|
3194
4087
|
const hasVisibleSourceImage = imageCount > 0 ? await sourceImages.first().isVisible({ timeout: options.loadingIndicatorVisibleTimeoutMs }).catch(() => false) : false;
|
|
3195
4088
|
if (!isLoadingVisible && hasVisibleSourceImage) {
|
|
3196
|
-
|
|
4089
|
+
logger10.info(hasSeenLoading ? "\u9A8C\u8BC1\u7801\u56FE\u7247\u5DF2\u52A0\u8F7D\u5B8C\u6210\u3002" : "\u9A8C\u8BC1\u7801\u56FE\u7247\u5DF2\u5C31\u7EEA\u3002");
|
|
3197
4090
|
return;
|
|
3198
4091
|
}
|
|
3199
4092
|
if (hasErrorTextVisible) {
|
|
3200
|
-
|
|
4093
|
+
logger10.warn("\u9A8C\u8BC1\u7801\u9762\u677F\u51FA\u73B0\u7F51\u7EDC\u5F02\u5E38\u6587\u6848\uFF0C\u5C1D\u8BD5\u7ACB\u5373\u5237\u65B0\u9898\u76EE\u3002");
|
|
3201
4094
|
await refreshCaptcha(page, frame, options);
|
|
3202
4095
|
refreshDeadline = Date.now() + options.challengeReadyRefreshTimeoutMs;
|
|
3203
4096
|
hasSeenLoading = false;
|
|
3204
4097
|
continue;
|
|
3205
4098
|
}
|
|
3206
4099
|
if (!hasVisibleSourceImage && Date.now() >= refreshDeadline) {
|
|
3207
|
-
|
|
4100
|
+
logger10.warn(`\u9A8C\u8BC1\u7801\u56FE\u7247\u8D85\u8FC7 ${options.challengeReadyRefreshTimeoutMs}ms \u4ECD\u672A\u51FA\u73B0\uFF0C\u5C1D\u8BD5\u5237\u65B0\u9898\u76EE\u3002`);
|
|
3208
4101
|
await refreshCaptcha(page, frame, options);
|
|
3209
4102
|
refreshDeadline = Date.now() + options.challengeReadyRefreshTimeoutMs;
|
|
3210
4103
|
hasSeenLoading = false;
|
|
@@ -3224,16 +4117,16 @@ async function solveCaptcha(page, options = {}, dependencies = {}) {
|
|
|
3224
4117
|
...options
|
|
3225
4118
|
};
|
|
3226
4119
|
if (!config.token) {
|
|
3227
|
-
|
|
4120
|
+
logger10.warn("\u7F3A\u5C11\u9A8C\u8BC1\u7801 token\uFF0C\u8DF3\u8FC7\u81EA\u52A8\u8BC6\u522B\u3002");
|
|
3228
4121
|
return false;
|
|
3229
4122
|
}
|
|
3230
|
-
|
|
4123
|
+
logger10.info("\u5F53\u524D\u4F7F\u7528\u672Ctool\u2014\u2014\u6D4B\u8BD5\u7248\u672C");
|
|
3231
4124
|
for (let attempt = 1; attempt <= config.maxRetries; attempt++) {
|
|
3232
|
-
|
|
4125
|
+
logger10.info(`\u5F00\u59CB\u7B2C ${attempt}/${config.maxRetries} \u6B21 verifycenter \u9A8C\u8BC1\u7801\u8BC6\u522B\u3002`);
|
|
3233
4126
|
try {
|
|
3234
4127
|
const captchaContext = await getVerifycenterCaptchaContext(page, config);
|
|
3235
4128
|
if (!captchaContext) {
|
|
3236
|
-
|
|
4129
|
+
logger10.info("Captcha container is not visible anymore.");
|
|
3237
4130
|
return true;
|
|
3238
4131
|
}
|
|
3239
4132
|
const { iframeLocator, frame } = captchaContext;
|
|
@@ -3248,16 +4141,16 @@ async function solveCaptcha(page, options = {}, dependencies = {}) {
|
|
|
3248
4141
|
});
|
|
3249
4142
|
const serialNumbers = extractCaptchaSerialNumbers(apiResponse);
|
|
3250
4143
|
if (apiResponse?.code !== config.recognitionSuccessCode || serialNumbers.length === 0) {
|
|
3251
|
-
|
|
4144
|
+
logger10.warn(
|
|
3252
4145
|
`\u9A8C\u8BC1\u7801\u8BC6\u522B\u5931\u8D25\u3002code=${apiResponse?.code}, msg=${apiResponse?.msg || "unknown"}`
|
|
3253
4146
|
);
|
|
3254
4147
|
await refreshCaptcha(page, frame, config);
|
|
3255
4148
|
continue;
|
|
3256
4149
|
}
|
|
3257
|
-
|
|
4150
|
+
logger10.info(`\u9A8C\u8BC1\u7801\u8BC6\u522B\u6210\u529F\uFF0C\u5E8F\u53F7\uFF1A${serialNumbers.join(", ")}`);
|
|
3258
4151
|
const dropTarget = await findCaptchaDropTarget(frame, config);
|
|
3259
4152
|
if (!dropTarget) {
|
|
3260
|
-
|
|
4153
|
+
logger10.warn("\u672A\u627E\u5230\u9A8C\u8BC1\u7801\u62D6\u62FD\u76EE\u6807\u533A\u57DF\u3002");
|
|
3261
4154
|
await refreshCaptcha(page, frame, config);
|
|
3262
4155
|
continue;
|
|
3263
4156
|
}
|
|
@@ -3280,31 +4173,31 @@ async function solveCaptcha(page, options = {}, dependencies = {}) {
|
|
|
3280
4173
|
}
|
|
3281
4174
|
const submitted = await clickCaptchaAction(frame, config.submitTexts, config).catch(() => false);
|
|
3282
4175
|
if (!submitted) {
|
|
3283
|
-
|
|
4176
|
+
logger10.warn("\u672A\u627E\u5230\u63D0\u4EA4\u6309\u94AE\uFF0C\u53EF\u80FD\u4F1A\u81EA\u52A8\u63D0\u4EA4\u3002");
|
|
3284
4177
|
}
|
|
3285
4178
|
await page.waitForTimeout(config.submitWaitMs);
|
|
3286
4179
|
const stillVisible = await iframeLocator.isVisible({ timeout: config.containerVisibleTimeoutMs }).catch(() => false);
|
|
3287
4180
|
if (!stillVisible) {
|
|
3288
|
-
|
|
4181
|
+
logger10.info("\u9A8C\u8BC1\u7801\u8BC6\u522B\u5E76\u63D0\u4EA4\u6210\u529F\u3002");
|
|
3289
4182
|
return true;
|
|
3290
4183
|
}
|
|
3291
|
-
|
|
4184
|
+
logger10.warn("\u63D0\u4EA4\u540E\u9A8C\u8BC1\u7801 iframe \u4ECD\u7136\u53EF\u89C1\uFF0C\u51C6\u5907\u5237\u65B0\u540E\u91CD\u8BD5\u3002");
|
|
3292
4185
|
await page.waitForTimeout(2e3);
|
|
3293
4186
|
await refreshCaptcha(page, frame, config);
|
|
3294
4187
|
} catch (error) {
|
|
3295
|
-
|
|
4188
|
+
logger10.error(`\u7B2C ${attempt}/${config.maxRetries} \u6B21\u9A8C\u8BC1\u7801\u8BC6\u522B\u5931\u8D25\uFF1A${error?.message || error}`);
|
|
3296
4189
|
}
|
|
3297
4190
|
if (attempt < config.maxRetries) {
|
|
3298
4191
|
await page.waitForTimeout(config.retryDelayBaseMs + attempt * config.retryDelayStepMs);
|
|
3299
4192
|
}
|
|
3300
4193
|
}
|
|
3301
|
-
|
|
4194
|
+
logger10.error(`\u91CD\u8BD5 ${config.maxRetries} \u6B21\u540E\uFF0C\u9A8C\u8BC1\u7801\u4ECD\u672A\u8BC6\u522B\u6210\u529F\u3002`);
|
|
3302
4195
|
return false;
|
|
3303
4196
|
}
|
|
3304
4197
|
var sloveCaptcha = solveCaptcha;
|
|
3305
4198
|
|
|
3306
4199
|
// src/chaptcha.js
|
|
3307
|
-
var
|
|
4200
|
+
var logger11 = createInternalLogger("Captcha");
|
|
3308
4201
|
var DEFAULT_CAPTCHA_RECOGNITION_OPTIONS = Object.freeze({
|
|
3309
4202
|
token: "eKJvBfwfN0YRav0-VD_44E2VBSfm7l0YtddUQ7cFySI",
|
|
3310
4203
|
apiUrl: "https://api.jfbym.com/api/YmServer/customApi"
|
|
@@ -3391,7 +4284,7 @@ function useCaptchaMonitor(page, options) {
|
|
|
3391
4284
|
};
|
|
3392
4285
|
})();
|
|
3393
4286
|
}, { selector: domSelector, callbackName: exposedFunctionName, cleanerName });
|
|
3394
|
-
|
|
4287
|
+
logger11.success("useCaptchaMonitor", `DOM \u76D1\u63A7\u5DF2\u542F\u7528\uFF1A${domSelector}`);
|
|
3395
4288
|
cleanupFns.push(async () => {
|
|
3396
4289
|
try {
|
|
3397
4290
|
await page.evaluate((name) => {
|
|
@@ -3415,14 +4308,14 @@ function useCaptchaMonitor(page, options) {
|
|
|
3415
4308
|
}
|
|
3416
4309
|
};
|
|
3417
4310
|
page.on("framenavigated", frameHandler);
|
|
3418
|
-
|
|
4311
|
+
logger11.success("useCaptchaMonitor", `URL \u76D1\u63A7\u5DF2\u542F\u7528\uFF1A${urlPattern}`);
|
|
3419
4312
|
cleanupFns.push(async () => {
|
|
3420
4313
|
page.off("framenavigated", frameHandler);
|
|
3421
4314
|
});
|
|
3422
4315
|
}
|
|
3423
4316
|
return {
|
|
3424
4317
|
stop: async () => {
|
|
3425
|
-
|
|
4318
|
+
logger11.info("\u6B63\u5728\u505C\u6B62\u9A8C\u8BC1\u7801\u76D1\u63A7...");
|
|
3426
4319
|
for (const fn of cleanupFns) {
|
|
3427
4320
|
await fn();
|
|
3428
4321
|
}
|
|
@@ -3461,7 +4354,7 @@ async function solveCaptchaWithStrategy(strategyName, page, options = {}) {
|
|
|
3461
4354
|
);
|
|
3462
4355
|
return strategy.sloveCaptcha(page, resolvedOptions, {
|
|
3463
4356
|
callCaptchaRecognitionApi,
|
|
3464
|
-
logger:
|
|
4357
|
+
logger: logger11
|
|
3465
4358
|
});
|
|
3466
4359
|
}
|
|
3467
4360
|
var Captcha = {
|
|
@@ -3472,7 +4365,7 @@ var Captcha = {
|
|
|
3472
4365
|
// src/mutation.js
|
|
3473
4366
|
var import_node_crypto = require("node:crypto");
|
|
3474
4367
|
var import_uuid2 = require("uuid");
|
|
3475
|
-
var
|
|
4368
|
+
var logger12 = createInternalLogger("Mutation");
|
|
3476
4369
|
var MUTATION_MONITOR_MODE = Object.freeze({
|
|
3477
4370
|
Added: "added",
|
|
3478
4371
|
Changed: "changed",
|
|
@@ -3505,14 +4398,14 @@ var Mutation = {
|
|
|
3505
4398
|
const stableTime = options.stableTime ?? 5 * 1e3;
|
|
3506
4399
|
const timeout = options.timeout ?? 120 * 1e3;
|
|
3507
4400
|
const onMutation = options.onMutation;
|
|
3508
|
-
|
|
4401
|
+
logger12.start("waitForStable", `\u76D1\u63A7 ${selectorList.length} \u4E2A\u9009\u62E9\u5668, \u7A33\u5B9A\u65F6\u95F4=${stableTime}ms`);
|
|
3509
4402
|
if (initialTimeout > 0) {
|
|
3510
4403
|
const selectorQuery = selectorList.join(",");
|
|
3511
4404
|
try {
|
|
3512
4405
|
await page.waitForSelector(selectorQuery, { timeout: initialTimeout });
|
|
3513
|
-
|
|
4406
|
+
logger12.info(`waitForStable \u5DF2\u68C0\u6D4B\u5230\u5143\u7D20: ${selectorQuery}`);
|
|
3514
4407
|
} catch (e) {
|
|
3515
|
-
|
|
4408
|
+
logger12.warning(`waitForStable \u521D\u59CB\u7B49\u5F85\u8D85\u65F6 (${initialTimeout}ms): ${selectorQuery}`);
|
|
3516
4409
|
throw e;
|
|
3517
4410
|
}
|
|
3518
4411
|
}
|
|
@@ -3528,7 +4421,7 @@ var Mutation = {
|
|
|
3528
4421
|
return "__CONTINUE__";
|
|
3529
4422
|
}
|
|
3530
4423
|
});
|
|
3531
|
-
|
|
4424
|
+
logger12.info("waitForStable \u5DF2\u542F\u7528 onMutation \u56DE\u8C03");
|
|
3532
4425
|
} catch (e) {
|
|
3533
4426
|
}
|
|
3534
4427
|
}
|
|
@@ -3643,9 +4536,9 @@ var Mutation = {
|
|
|
3643
4536
|
{ selectorList, stableTime, timeout, callbackName, hasCallback: !!onMutation }
|
|
3644
4537
|
);
|
|
3645
4538
|
if (result.mutationCount === 0 && result.stableTime === 0) {
|
|
3646
|
-
|
|
4539
|
+
logger12.warning("waitForStable \u672A\u627E\u5230\u53EF\u76D1\u63A7\u7684\u5143\u7D20");
|
|
3647
4540
|
}
|
|
3648
|
-
|
|
4541
|
+
logger12.success("waitForStable", `DOM \u7A33\u5B9A, \u603B\u5171 ${result.mutationCount} \u6B21\u53D8\u5316${result.wasPaused ? ", \u66FE\u6682\u505C\u8BA1\u65F6" : ""}`);
|
|
3649
4542
|
return result;
|
|
3650
4543
|
},
|
|
3651
4544
|
/**
|
|
@@ -3817,22 +4710,22 @@ var Mutation = {
|
|
|
3817
4710
|
return "__CONTINUE__";
|
|
3818
4711
|
}
|
|
3819
4712
|
};
|
|
3820
|
-
|
|
4713
|
+
logger12.start(
|
|
3821
4714
|
"waitForStableAcrossRoots",
|
|
3822
4715
|
`\u76D1\u63A7 ${selectorList.length} \u4E2A\u9009\u62E9\u5668(\u8DE8 root), \u7A33\u5B9A\u65F6\u95F4=${waitForStableTime}ms`
|
|
3823
4716
|
);
|
|
3824
4717
|
if (initialTimeout > 0) {
|
|
3825
4718
|
try {
|
|
3826
4719
|
await page.waitForSelector(selectorQuery, { timeout: initialTimeout });
|
|
3827
|
-
|
|
4720
|
+
logger12.info(`waitForStableAcrossRoots \u5DF2\u68C0\u6D4B\u5230\u5143\u7D20: ${selectorQuery}`);
|
|
3828
4721
|
} catch (e) {
|
|
3829
|
-
|
|
4722
|
+
logger12.warning(`waitForStableAcrossRoots \u521D\u59CB\u7B49\u5F85\u8D85\u65F6 (${initialTimeout}ms): ${selectorQuery}`);
|
|
3830
4723
|
throw e;
|
|
3831
4724
|
}
|
|
3832
4725
|
}
|
|
3833
4726
|
let state = await buildState();
|
|
3834
4727
|
if (!state?.hasMatched) {
|
|
3835
|
-
|
|
4728
|
+
logger12.warning("waitForStableAcrossRoots \u672A\u627E\u5230\u53EF\u76D1\u63A7\u7684\u5143\u7D20");
|
|
3836
4729
|
return { mutationCount: 0, stableTime: 0, wasPaused: false };
|
|
3837
4730
|
}
|
|
3838
4731
|
let mutationCount = 0;
|
|
@@ -3869,7 +4762,7 @@ var Mutation = {
|
|
|
3869
4762
|
if (lastState.snapshotKey !== lastSnapshotKey) {
|
|
3870
4763
|
lastSnapshotKey = lastState.snapshotKey;
|
|
3871
4764
|
mutationCount += 1;
|
|
3872
|
-
|
|
4765
|
+
logger12.info(
|
|
3873
4766
|
`waitForStableAcrossRoots \u53D8\u5316#${mutationCount}, len=${lastState.snapshotLength}, path=${lastState.primaryPath || "unknown"}, preview="${truncate(lastState.text, 120)}"`
|
|
3874
4767
|
);
|
|
3875
4768
|
const signal = await invokeMutationCallback({
|
|
@@ -3882,7 +4775,7 @@ var Mutation = {
|
|
|
3882
4775
|
continue;
|
|
3883
4776
|
}
|
|
3884
4777
|
if (!isPaused && stableSince > 0 && Date.now() - stableSince >= waitForStableTime) {
|
|
3885
|
-
|
|
4778
|
+
logger12.success("waitForStableAcrossRoots", `DOM \u7A33\u5B9A, \u603B\u5171 ${mutationCount} \u6B21\u53D8\u5316${wasPaused ? ", \u66FE\u6682\u505C\u8BA1\u65F6" : ""}`);
|
|
3886
4779
|
return {
|
|
3887
4780
|
mutationCount,
|
|
3888
4781
|
stableTime: waitForStableTime,
|
|
@@ -3909,7 +4802,7 @@ var Mutation = {
|
|
|
3909
4802
|
const onMutation = options.onMutation;
|
|
3910
4803
|
const rawMode = String(options.mode || MUTATION_MONITOR_MODE.Added).toLowerCase();
|
|
3911
4804
|
const mode = [MUTATION_MONITOR_MODE.Added, MUTATION_MONITOR_MODE.Changed, MUTATION_MONITOR_MODE.All].includes(rawMode) ? rawMode : MUTATION_MONITOR_MODE.Added;
|
|
3912
|
-
|
|
4805
|
+
logger12.start("useMonitor", `\u76D1\u63A7 ${selectorList.length} \u4E2A\u9009\u62E9\u5668, mode=${mode}`);
|
|
3913
4806
|
const monitorKey = generateKey("pk_mon");
|
|
3914
4807
|
const callbackName = generateKey("pk_mon_cb");
|
|
3915
4808
|
const cleanerName = generateKey("pk_mon_clean");
|
|
@@ -4052,7 +4945,7 @@ var Mutation = {
|
|
|
4052
4945
|
return total;
|
|
4053
4946
|
};
|
|
4054
4947
|
}, { selectorList, monitorKey, callbackName, cleanerName, hasCallback: !!onMutation, mode });
|
|
4055
|
-
|
|
4948
|
+
logger12.success("useMonitor", "\u76D1\u63A7\u5668\u5DF2\u542F\u52A8");
|
|
4056
4949
|
return {
|
|
4057
4950
|
stop: async () => {
|
|
4058
4951
|
let totalMutations = 0;
|
|
@@ -4065,7 +4958,7 @@ var Mutation = {
|
|
|
4065
4958
|
}, cleanerName);
|
|
4066
4959
|
} catch (e) {
|
|
4067
4960
|
}
|
|
4068
|
-
|
|
4961
|
+
logger12.success("useMonitor.stop", `\u76D1\u63A7\u5DF2\u505C\u6B62, \u5171 ${totalMutations} \u6B21\u53D8\u5316`);
|
|
4069
4962
|
return { totalMutations };
|
|
4070
4963
|
}
|
|
4071
4964
|
};
|
|
@@ -4934,7 +5827,7 @@ var createTemplateLogger = (baseLogger = createBaseLogger()) => {
|
|
|
4934
5827
|
};
|
|
4935
5828
|
var getDefaultBaseLogger = () => createBaseLogger("");
|
|
4936
5829
|
var Logger = {
|
|
4937
|
-
setLogger: (
|
|
5830
|
+
setLogger: (logger16) => setDefaultLogger(logger16),
|
|
4938
5831
|
info: (message) => getDefaultBaseLogger().info(message),
|
|
4939
5832
|
success: (message) => getDefaultBaseLogger().success(message),
|
|
4940
5833
|
warning: (message) => getDefaultBaseLogger().warning(message),
|
|
@@ -4942,14 +5835,14 @@ var Logger = {
|
|
|
4942
5835
|
error: (message) => getDefaultBaseLogger().error(message),
|
|
4943
5836
|
debug: (message) => getDefaultBaseLogger().debug(message),
|
|
4944
5837
|
start: (message) => getDefaultBaseLogger().start(message),
|
|
4945
|
-
useTemplate: (
|
|
4946
|
-
if (
|
|
5838
|
+
useTemplate: (logger16) => {
|
|
5839
|
+
if (logger16) return createTemplateLogger(createBaseLogger("", logger16));
|
|
4947
5840
|
return createTemplateLogger();
|
|
4948
5841
|
}
|
|
4949
5842
|
};
|
|
4950
5843
|
|
|
4951
5844
|
// src/share.js
|
|
4952
|
-
var
|
|
5845
|
+
var import_delay3 = __toESM(require("delay"), 1);
|
|
4953
5846
|
|
|
4954
5847
|
// src/internals/watermarkify.js
|
|
4955
5848
|
var DEFAULT_TIMEZONE_OFFSET = 8;
|
|
@@ -5008,7 +5901,7 @@ var LOCATION_NETWORK_SUFFIX_PATTERNS = [
|
|
|
5008
5901
|
];
|
|
5009
5902
|
var cachedStripLogoSrcPromise = null;
|
|
5010
5903
|
var cachedEnrichmentByContext = /* @__PURE__ */ new WeakMap();
|
|
5011
|
-
var
|
|
5904
|
+
var logger13 = createInternalLogger("Watermarkify");
|
|
5012
5905
|
var normalizeText = (value) => String(value || "").trim();
|
|
5013
5906
|
var toInline = (value, maxLen = 200) => {
|
|
5014
5907
|
const text = normalizeText(value);
|
|
@@ -5250,9 +6143,9 @@ var resolveWithCustomResolver = async (page, baseMeta, options = {}) => {
|
|
|
5250
6143
|
location: toInline(resolved.location, 80)
|
|
5251
6144
|
};
|
|
5252
6145
|
if (enrichment.ip || enrichment.location) {
|
|
5253
|
-
|
|
6146
|
+
logger13.info(`\u81EA\u5B9A\u4E49 resolver \u547D\u4E2D: ip=${enrichment.ip || "-"}, loc=${enrichment.location || "-"}`);
|
|
5254
6147
|
} else {
|
|
5255
|
-
|
|
6148
|
+
logger13.warning("\u81EA\u5B9A\u4E49 resolver \u5DF2\u6267\u884C\uFF0C\u4F46\u672A\u8FD4\u56DE IP/Loc");
|
|
5256
6149
|
}
|
|
5257
6150
|
return enrichment;
|
|
5258
6151
|
} finally {
|
|
@@ -5436,12 +6329,12 @@ var buildWatermarkifyRenderHtml = ({ imageSrc, overlaySvg, width, height }) => {
|
|
|
5436
6329
|
};
|
|
5437
6330
|
var composeScreenshotBufferWithBrowser = async (page, buffer, overlaySvg, imageInfo = {}) => {
|
|
5438
6331
|
if (!page || typeof page.context !== "function") {
|
|
5439
|
-
|
|
6332
|
+
logger13.warning("watermarkify \u6D4F\u89C8\u5668\u5408\u6210\u8DF3\u8FC7: \u7F3A\u5C11\u53EF\u7528 page");
|
|
5440
6333
|
return buffer;
|
|
5441
6334
|
}
|
|
5442
6335
|
const renderScope = await openProbePage(page);
|
|
5443
6336
|
if (!renderScope?.page) {
|
|
5444
|
-
|
|
6337
|
+
logger13.warning("watermarkify \u6D4F\u89C8\u5668\u5408\u6210\u8DF3\u8FC7: \u65E0\u6CD5\u521B\u5EFA render page");
|
|
5445
6338
|
return buffer;
|
|
5446
6339
|
}
|
|
5447
6340
|
try {
|
|
@@ -5484,13 +6377,13 @@ var composeScreenshotBufferWithBrowser = async (page, buffer, overlaySvg, imageI
|
|
|
5484
6377
|
fullPage: true,
|
|
5485
6378
|
animations: "disabled"
|
|
5486
6379
|
}).catch((error) => {
|
|
5487
|
-
|
|
6380
|
+
logger13.warning(`watermarkify \u6D4F\u89C8\u5668\u5408\u6210\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`);
|
|
5488
6381
|
return null;
|
|
5489
6382
|
});
|
|
5490
6383
|
if (Buffer.isBuffer(composed) && composed.length > 0) {
|
|
5491
6384
|
return composed;
|
|
5492
6385
|
}
|
|
5493
|
-
|
|
6386
|
+
logger13.warning("watermarkify \u6D4F\u89C8\u5668\u5408\u6210\u5931\u8D25: \u672A\u5F97\u5230\u6709\u6548\u622A\u56FE\u7ED3\u679C");
|
|
5494
6387
|
return buffer;
|
|
5495
6388
|
} finally {
|
|
5496
6389
|
await renderScope.close().catch(() => {
|
|
@@ -5503,7 +6396,7 @@ var resolveWithIpLookup = async (page, options = {}) => {
|
|
|
5503
6396
|
}
|
|
5504
6397
|
const probeScope = await openProbePage(page);
|
|
5505
6398
|
if (!probeScope?.page) {
|
|
5506
|
-
|
|
6399
|
+
logger13.warning("ipLookup \u8DF3\u8FC7: \u65E0\u6CD5\u521B\u5EFA probe page");
|
|
5507
6400
|
return null;
|
|
5508
6401
|
}
|
|
5509
6402
|
const timeoutMs = Math.max(
|
|
@@ -5512,12 +6405,12 @@ var resolveWithIpLookup = async (page, options = {}) => {
|
|
|
5512
6405
|
);
|
|
5513
6406
|
try {
|
|
5514
6407
|
const probePage = probeScope.page;
|
|
5515
|
-
|
|
6408
|
+
logger13.info(`ipLookup \u5C1D\u8BD5: url=${DEFAULT_IP_LOOKUP_URL}, timeoutMs=${timeoutMs}`);
|
|
5516
6409
|
const response = await probePage.goto(DEFAULT_IP_LOOKUP_URL, {
|
|
5517
6410
|
waitUntil: "commit",
|
|
5518
6411
|
timeout: timeoutMs
|
|
5519
6412
|
}).catch((error) => {
|
|
5520
|
-
|
|
6413
|
+
logger13.warning(`ipLookup \u8BF7\u6C42\u5931\u8D25: url=${DEFAULT_IP_LOOKUP_URL}, error=${error instanceof Error ? error.message : String(error)}`);
|
|
5521
6414
|
return null;
|
|
5522
6415
|
});
|
|
5523
6416
|
const status = response && typeof response.status === "function" ? response.status() : 0;
|
|
@@ -5539,13 +6432,13 @@ var resolveWithIpLookup = async (page, options = {}) => {
|
|
|
5539
6432
|
}
|
|
5540
6433
|
const parsed = parseIpIpJsonResponse(rawText);
|
|
5541
6434
|
if (parsed?.ip || parsed?.location) {
|
|
5542
|
-
|
|
6435
|
+
logger13.info(`ipLookup \u6210\u529F: url=${DEFAULT_IP_LOOKUP_URL}, status=${status || "-"}, contentType=${contentType || "-"}, ip=${parsed.ip || "-"}, loc=${parsed.location || "-"}`);
|
|
5543
6436
|
return parsed;
|
|
5544
6437
|
}
|
|
5545
|
-
|
|
6438
|
+
logger13.warning(`ipLookup \u672A\u89E3\u6790\u51FA IP/Loc: url=${DEFAULT_IP_LOOKUP_URL}, status=${status || "-"}, contentType=${contentType || "-"}, preview=${shortenTail(rawText, 120) || "[empty]"}`);
|
|
5546
6439
|
return null;
|
|
5547
6440
|
} catch (error) {
|
|
5548
|
-
|
|
6441
|
+
logger13.warning(`ipLookup \u6267\u884C\u5F02\u5E38\uFF0C\u672A\u83B7\u5F97 IP/Loc: ${error instanceof Error ? error.message : String(error)}`);
|
|
5549
6442
|
return null;
|
|
5550
6443
|
} finally {
|
|
5551
6444
|
await probeScope.close().catch(() => {
|
|
@@ -5559,10 +6452,10 @@ var resolveEnrichment = async (page, baseMeta, options) => {
|
|
|
5559
6452
|
ip: toInline(options.ip, 80),
|
|
5560
6453
|
location: toInline(options.location, 80)
|
|
5561
6454
|
};
|
|
5562
|
-
|
|
6455
|
+
logger13.info(`enrichment \u5F00\u59CB: host=${baseMeta.hostname || "-"}, hasPresetIp=${Boolean(merged.ip)}, hasPresetLoc=${Boolean(merged.location)}, ipLookup=${options.ipLookup !== false}`);
|
|
5563
6456
|
if (!merged.ip || !merged.location) {
|
|
5564
6457
|
if (cached?.ip || cached?.location) {
|
|
5565
|
-
|
|
6458
|
+
logger13.info(`enrichment \u547D\u4E2D\u4E0A\u4E0B\u6587\u7F13\u5B58: ip=${cached.ip || "-"}, loc=${cached.location || "-"}`);
|
|
5566
6459
|
}
|
|
5567
6460
|
fillEnrichment(merged, cached);
|
|
5568
6461
|
}
|
|
@@ -5586,15 +6479,15 @@ var resolveEnrichment = async (page, baseMeta, options) => {
|
|
|
5586
6479
|
"x-geo-country"
|
|
5587
6480
|
]), 80);
|
|
5588
6481
|
if (!merged.location || isWeakLocationValue(merged.location) && headerLocation) {
|
|
5589
|
-
|
|
6482
|
+
logger13.info(`enrichment \u4F7F\u7528\u54CD\u5E94\u5934\u8865\u5145 Loc: ${headerLocation || "-"}`);
|
|
5590
6483
|
merged.location = headerLocation || merged.location;
|
|
5591
6484
|
}
|
|
5592
6485
|
}
|
|
5593
6486
|
writeCachedEnrichment(page, merged);
|
|
5594
6487
|
if (merged.ip || merged.location) {
|
|
5595
|
-
|
|
6488
|
+
logger13.info(`enrichment \u5B8C\u6210: ip=${merged.ip || "-"}, loc=${merged.location || "-"}`);
|
|
5596
6489
|
} else {
|
|
5597
|
-
|
|
6490
|
+
logger13.warning("enrichment \u5B8C\u6210: \u672A\u83B7\u5F97 IP/Loc");
|
|
5598
6491
|
}
|
|
5599
6492
|
return merged;
|
|
5600
6493
|
};
|
|
@@ -6262,7 +7155,7 @@ var watermarkifyScreenshotBuffer = async (buffer, meta, page = null) => {
|
|
|
6262
7155
|
}
|
|
6263
7156
|
const imageInfo = readImageInfo(buffer);
|
|
6264
7157
|
if (!imageInfo.width || !imageInfo.height || !imageInfo.mimeType) {
|
|
6265
|
-
|
|
7158
|
+
logger13.warning("watermarkify \u8DF3\u8FC7: \u65E0\u6CD5\u89E3\u6790\u622A\u56FE\u5C3A\u5BF8\u6216\u683C\u5F0F");
|
|
6266
7159
|
return buffer;
|
|
6267
7160
|
}
|
|
6268
7161
|
const overlaySvg = buildWatermarkifySvg(meta, imageInfo.width, imageInfo.height);
|
|
@@ -6274,7 +7167,7 @@ var watermarkifyScreenshotBuffer = async (buffer, meta, page = null) => {
|
|
|
6274
7167
|
|
|
6275
7168
|
// src/internals/screenshot-compression.js
|
|
6276
7169
|
var import_jimp = require("jimp");
|
|
6277
|
-
var
|
|
7170
|
+
var logger14 = createInternalLogger("ScreenshotCompression");
|
|
6278
7171
|
var DEFAULT_SCREENSHOT_MAX_BYTES = 5 * 1024 * 1024;
|
|
6279
7172
|
var DEFAULT_SCREENSHOT_OUTPUT_TYPE = "jpeg";
|
|
6280
7173
|
var DEFAULT_SCREENSHOT_QUALITY = 0.72;
|
|
@@ -6393,18 +7286,18 @@ var compressScreenshotBufferToBase64 = async (buffer, compression) => {
|
|
|
6393
7286
|
return buffer.toString("base64");
|
|
6394
7287
|
}
|
|
6395
7288
|
const result = await compressScreenshotBuffer(buffer, compression).catch((error) => {
|
|
6396
|
-
|
|
7289
|
+
logger14.warning(`captureScreen \u538B\u7F29\u5931\u8D25\uFF0C\u8FD4\u56DE\u539F\u56FE: ${error instanceof Error ? error.message : String(error)}`);
|
|
6397
7290
|
return null;
|
|
6398
7291
|
});
|
|
6399
7292
|
if (!result?.buffer) {
|
|
6400
7293
|
return buffer.toString("base64");
|
|
6401
7294
|
}
|
|
6402
7295
|
if (result.withinLimit) {
|
|
6403
|
-
|
|
7296
|
+
logger14.info(
|
|
6404
7297
|
`captureScreen \u5DF2\u538B\u7F29: ${originalBytes} -> ${result.bytes} bytes, format=${result.format}, quality=${result.quality}, scale=${result.scale}, size=${result.width}x${result.height}`
|
|
6405
7298
|
);
|
|
6406
7299
|
} else {
|
|
6407
|
-
|
|
7300
|
+
logger14.warning(
|
|
6408
7301
|
`captureScreen \u538B\u7F29\u540E\u4ECD\u8D85\u8FC7\u76EE\u6807: ${originalBytes} -> ${result.bytes} bytes, maxBytes=${compression.maxBytes}, format=${result.format}, quality=${result.quality}, scale=${result.scale}`
|
|
6409
7302
|
);
|
|
6410
7303
|
}
|
|
@@ -6412,7 +7305,7 @@ var compressScreenshotBufferToBase64 = async (buffer, compression) => {
|
|
|
6412
7305
|
};
|
|
6413
7306
|
|
|
6414
7307
|
// src/share.js
|
|
6415
|
-
var
|
|
7308
|
+
var logger15 = createInternalLogger("Share");
|
|
6416
7309
|
var DEFAULT_TIMEOUT_MS2 = 50 * 1e3;
|
|
6417
7310
|
var DEFAULT_PAYLOAD_SNAPSHOT_MAX_LEN = 500;
|
|
6418
7311
|
var DEFAULT_POLL_INTERVAL_MS = 120;
|
|
@@ -6549,7 +7442,7 @@ var createDomShareMonitor = async (page, options = {}) => {
|
|
|
6549
7442
|
const onMatch = typeof options.onMatch === "function" ? options.onMatch : null;
|
|
6550
7443
|
const onTelemetry = typeof options.onTelemetry === "function" ? options.onTelemetry : null;
|
|
6551
7444
|
let matched = false;
|
|
6552
|
-
|
|
7445
|
+
logger15.info(`DOM \u76D1\u542C\u51C6\u5907\u6302\u8F7D: selectors=${toJsonInline(selectors, 120)}, mode=${mode}`);
|
|
6553
7446
|
const monitor = await Mutation.useMonitor(page, selectors, {
|
|
6554
7447
|
mode,
|
|
6555
7448
|
onMutation: (context = {}) => {
|
|
@@ -6567,12 +7460,12 @@ ${text}`;
|
|
|
6567
7460
|
});
|
|
6568
7461
|
}
|
|
6569
7462
|
if (mutationCount <= 5 || mutationCount % 50 === 0) {
|
|
6570
|
-
|
|
7463
|
+
logger15.info(`DOM \u53D8\u5316\u5DF2\u6355\u83B7: mutationCount=${mutationCount}, mutationNodes=${mutationNodes.length}`);
|
|
6571
7464
|
}
|
|
6572
7465
|
const [candidate] = Utils.parseLinks(rawDom, { prefix }) || [];
|
|
6573
7466
|
if (!candidate) return;
|
|
6574
7467
|
matched = true;
|
|
6575
|
-
|
|
7468
|
+
logger15.success("captureLink.domHit", `DOM \u547D\u4E2D\u5206\u4EAB\u94FE\u63A5: mutationCount=${mutationCount}, link=${candidate}`);
|
|
6576
7469
|
if (onMatch) {
|
|
6577
7470
|
onMatch({
|
|
6578
7471
|
link: candidate,
|
|
@@ -6588,7 +7481,7 @@ ${text}`;
|
|
|
6588
7481
|
return {
|
|
6589
7482
|
stop: async () => {
|
|
6590
7483
|
const result = await monitor.stop();
|
|
6591
|
-
|
|
7484
|
+
logger15.info(`DOM \u76D1\u542C\u5DF2\u505C\u6B62: totalMutations=${result?.totalMutations || 0}`);
|
|
6592
7485
|
return result;
|
|
6593
7486
|
}
|
|
6594
7487
|
};
|
|
@@ -6628,8 +7521,8 @@ var Share = {
|
|
|
6628
7521
|
if (share.mode === "response" && apiMatchers.length === 0) {
|
|
6629
7522
|
throw new Error("Share.captureLink requires share.xurl[0] api matcher when mode=response");
|
|
6630
7523
|
}
|
|
6631
|
-
|
|
6632
|
-
|
|
7524
|
+
logger15.start("captureLink", `mode=${share.mode}, timeoutMs=${timeoutMs}, prefix=${share.prefix}`);
|
|
7525
|
+
logger15.info(`captureLink \u914D\u7F6E: xurl=${toJsonInline(share.xurl)}, domMode=${domMode}, domSelectors=${toJsonInline(domSelectors, 120)}`);
|
|
6633
7526
|
const stats = {
|
|
6634
7527
|
actionTimedOut: false,
|
|
6635
7528
|
domMutationCount: 0,
|
|
@@ -6654,7 +7547,7 @@ var Share = {
|
|
|
6654
7547
|
link: validated,
|
|
6655
7548
|
payloadText: String(payloadText || "")
|
|
6656
7549
|
};
|
|
6657
|
-
|
|
7550
|
+
logger15.info(`\u5019\u9009\u94FE\u63A5\u5DF2\u786E\u8BA4: source=${source}, link=${validated}`);
|
|
6658
7551
|
return true;
|
|
6659
7552
|
};
|
|
6660
7553
|
const resolveResponseCandidate = (responseText) => {
|
|
@@ -6689,7 +7582,7 @@ var Share = {
|
|
|
6689
7582
|
try {
|
|
6690
7583
|
await monitor.stop();
|
|
6691
7584
|
} catch (error) {
|
|
6692
|
-
|
|
7585
|
+
logger15.warning(`\u505C\u6B62 DOM \u76D1\u542C\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`);
|
|
6693
7586
|
}
|
|
6694
7587
|
};
|
|
6695
7588
|
const onResponse = async (response) => {
|
|
@@ -6702,29 +7595,29 @@ var Share = {
|
|
|
6702
7595
|
stats.responseSampleUrls.push(url);
|
|
6703
7596
|
}
|
|
6704
7597
|
if (stats.responseObserved <= 5) {
|
|
6705
|
-
|
|
7598
|
+
logger15.info(`\u63A5\u53E3\u54CD\u5E94\u91C7\u6837(${stats.responseObserved}): ${url}`);
|
|
6706
7599
|
}
|
|
6707
7600
|
if (!apiMatchers.some((matcher) => url.includes(matcher))) return;
|
|
6708
7601
|
stats.responseMatched += 1;
|
|
6709
7602
|
stats.lastMatchedUrl = url;
|
|
6710
|
-
|
|
7603
|
+
logger15.info(`\u63A5\u53E3\u547D\u4E2D\u5339\u914D(${stats.responseMatched}): ${url}`);
|
|
6711
7604
|
const text = await response.text();
|
|
6712
7605
|
const hit = resolveResponseCandidate(text);
|
|
6713
7606
|
if (!hit?.link) {
|
|
6714
7607
|
if (stats.responseMatched <= 3) {
|
|
6715
|
-
|
|
7608
|
+
logger15.info(`\u63A5\u53E3\u89E3\u6790\u5B8C\u6210\u4F46\u672A\u63D0\u53D6\u5230\u5206\u4EAB\u94FE\u63A5: payloadSize=${text.length}`);
|
|
6716
7609
|
}
|
|
6717
7610
|
return;
|
|
6718
7611
|
}
|
|
6719
7612
|
stats.responseResolved += 1;
|
|
6720
|
-
|
|
7613
|
+
logger15.success("captureLink.responseHit", `\u63A5\u53E3\u547D\u4E2D\u5206\u4EAB\u94FE\u63A5: url=${url}, link=${hit.link}`);
|
|
6721
7614
|
setCandidate("response", hit.link, hit.payloadText);
|
|
6722
7615
|
} catch (error) {
|
|
6723
|
-
|
|
7616
|
+
logger15.warning(`\u63A5\u53E3\u54CD\u5E94\u5904\u7406\u5F02\u5E38: ${error instanceof Error ? error.message : String(error)}`);
|
|
6724
7617
|
}
|
|
6725
7618
|
};
|
|
6726
7619
|
if (share.mode === "dom") {
|
|
6727
|
-
|
|
7620
|
+
logger15.info("\u5F53\u524D\u4E3A DOM \u6A21\u5F0F\uFF0C\u4EC5\u542F\u7528 DOM \u76D1\u542C");
|
|
6728
7621
|
domMonitor = await createDomShareMonitor(page, {
|
|
6729
7622
|
prefix: share.prefix,
|
|
6730
7623
|
selectors: domSelectors,
|
|
@@ -6739,14 +7632,14 @@ var Share = {
|
|
|
6739
7632
|
});
|
|
6740
7633
|
}
|
|
6741
7634
|
if (share.mode === "response") {
|
|
6742
|
-
|
|
7635
|
+
logger15.info(`\u5F53\u524D\u4E3A\u63A5\u53E3\u6A21\u5F0F\uFF0C\u6302\u8F7D response \u76D1\u542C: apiMatchers=${toJsonInline(apiMatchers, 160)}`);
|
|
6743
7636
|
page.on("response", onResponse);
|
|
6744
7637
|
}
|
|
6745
7638
|
const deadline = Date.now() + timeoutMs;
|
|
6746
7639
|
const getRemainingMs = () => Math.max(0, deadline - Date.now());
|
|
6747
7640
|
try {
|
|
6748
7641
|
const actionTimeout = getRemainingMs();
|
|
6749
|
-
|
|
7642
|
+
logger15.start("captureLink.performActions", `\u6267\u884C\u52A8\u4F5C\u9884\u7B97=${actionTimeout}ms`);
|
|
6750
7643
|
if (actionTimeout > 0) {
|
|
6751
7644
|
let timer = null;
|
|
6752
7645
|
let actionError = null;
|
|
@@ -6760,21 +7653,21 @@ var Share = {
|
|
|
6760
7653
|
const actionResult = await Promise.race([actionPromise, timeoutPromise]);
|
|
6761
7654
|
if (timer) clearTimeout(timer);
|
|
6762
7655
|
if (actionResult === "__ACTION_ERROR__") {
|
|
6763
|
-
|
|
7656
|
+
logger15.fail("captureLink.performActions", actionError);
|
|
6764
7657
|
throw actionError;
|
|
6765
7658
|
}
|
|
6766
7659
|
if (actionResult === "__ACTION_TIMEOUT__") {
|
|
6767
7660
|
stats.actionTimedOut = true;
|
|
6768
|
-
|
|
7661
|
+
logger15.warning(`performActions \u5DF2\u8D85\u65F6 (${actionTimeout}ms)\uFF0C\u52A8\u4F5C\u53EF\u80FD\u4ECD\u5728\u5F02\u6B65\u6267\u884C`);
|
|
6769
7662
|
} else {
|
|
6770
|
-
|
|
7663
|
+
logger15.success("captureLink.performActions", "\u6267\u884C\u52A8\u4F5C\u5B8C\u6210");
|
|
6771
7664
|
}
|
|
6772
7665
|
}
|
|
6773
7666
|
let nextProgressLogTs = Date.now() + 3e3;
|
|
6774
7667
|
while (true) {
|
|
6775
7668
|
const selected = share.mode === "dom" ? candidates.dom : candidates.response;
|
|
6776
7669
|
if (selected?.link) {
|
|
6777
|
-
|
|
7670
|
+
logger15.success("captureLink", `\u6355\u83B7\u6210\u529F: source=${share.mode}, link=${selected.link}`);
|
|
6778
7671
|
return {
|
|
6779
7672
|
link: selected.link,
|
|
6780
7673
|
payloadText: selected.payloadText,
|
|
@@ -6786,19 +7679,19 @@ var Share = {
|
|
|
6786
7679
|
if (remaining <= 0) break;
|
|
6787
7680
|
const now = Date.now();
|
|
6788
7681
|
if (now >= nextProgressLogTs) {
|
|
6789
|
-
|
|
7682
|
+
logger15.info(
|
|
6790
7683
|
`captureLink \u7B49\u5F85\u4E2D: remaining=${remaining}ms, domMutationCount=${stats.domMutationCount}, responseMatched=${stats.responseMatched}`
|
|
6791
7684
|
);
|
|
6792
7685
|
nextProgressLogTs = now + 5e3;
|
|
6793
7686
|
}
|
|
6794
|
-
await (0,
|
|
7687
|
+
await (0, import_delay3.default)(Math.max(0, Math.min(DEFAULT_POLL_INTERVAL_MS, remaining)));
|
|
6795
7688
|
}
|
|
6796
7689
|
if (share.mode === "response" && stats.responseMatched === 0) {
|
|
6797
|
-
|
|
7690
|
+
logger15.warning(
|
|
6798
7691
|
`\u63A5\u53E3\u76D1\u542C\u672A\u547D\u4E2D: apiMatchers=${toJsonInline(apiMatchers, 220)}, \u54CD\u5E94\u6837\u672CURLs=${toJsonInline(stats.responseSampleUrls, 420)}`
|
|
6799
7692
|
);
|
|
6800
7693
|
}
|
|
6801
|
-
|
|
7694
|
+
logger15.warning(
|
|
6802
7695
|
`captureLink \u8D85\u65F6\u672A\u62FF\u5230\u94FE\u63A5: mode=${share.mode}, actionTimedOut=${stats.actionTimedOut}, domMutationCount=${stats.domMutationCount}, responseObserved=${stats.responseObserved}, responseMatched=${stats.responseMatched}, lastMatchedUrl=${stats.lastMatchedUrl || "none"}`
|
|
6803
7696
|
);
|
|
6804
7697
|
return {
|
|
@@ -6810,7 +7703,7 @@ var Share = {
|
|
|
6810
7703
|
} finally {
|
|
6811
7704
|
if (share.mode === "response") {
|
|
6812
7705
|
page.off("response", onResponse);
|
|
6813
|
-
|
|
7706
|
+
logger15.info("response \u76D1\u542C\u5DF2\u5378\u8F7D");
|
|
6814
7707
|
}
|
|
6815
7708
|
await stopDomMonitor();
|
|
6816
7709
|
}
|
|
@@ -6862,7 +7755,7 @@ var Share = {
|
|
|
6862
7755
|
width: originalViewport.width,
|
|
6863
7756
|
height: targetHeight
|
|
6864
7757
|
});
|
|
6865
|
-
await (0,
|
|
7758
|
+
await (0, import_delay3.default)(1e3);
|
|
6866
7759
|
const capturedAt = /* @__PURE__ */ new Date();
|
|
6867
7760
|
const rawBuffer = await capturePageScreenshot(page, {
|
|
6868
7761
|
fullPage: true,
|
|
@@ -6903,7 +7796,7 @@ var usePlaywrightToolKit = () => {
|
|
|
6903
7796
|
return {
|
|
6904
7797
|
ApifyKit,
|
|
6905
7798
|
AntiCheat,
|
|
6906
|
-
Humanize,
|
|
7799
|
+
Humanize: Humanize2,
|
|
6907
7800
|
Launch,
|
|
6908
7801
|
LiveView,
|
|
6909
7802
|
Constants: constants_exports,
|