@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.js
CHANGED
|
@@ -12,8 +12,10 @@ var constants_exports = {};
|
|
|
12
12
|
__export(constants_exports, {
|
|
13
13
|
ActorInfo: () => ActorInfo,
|
|
14
14
|
Code: () => Code,
|
|
15
|
+
Device: () => Device,
|
|
15
16
|
PresetOfLiveViewKey: () => PresetOfLiveViewKey,
|
|
16
|
-
Status: () => Status
|
|
17
|
+
Status: () => Status,
|
|
18
|
+
normalizeDevice: () => normalizeDevice
|
|
17
19
|
});
|
|
18
20
|
var Code = {
|
|
19
21
|
Success: 0,
|
|
@@ -29,6 +31,17 @@ var Status = {
|
|
|
29
31
|
Failed: "FAILED"
|
|
30
32
|
};
|
|
31
33
|
var PresetOfLiveViewKey = "LIVE_VIEW_SCREENSHOT";
|
|
34
|
+
var Device = Object.freeze({
|
|
35
|
+
Desktop: "desktop",
|
|
36
|
+
Mobile: "mobile"
|
|
37
|
+
});
|
|
38
|
+
var normalizeDevice = (value, fallback = Device.Desktop) => {
|
|
39
|
+
const normalizedFallback = String(fallback || "").trim().toLowerCase() === Device.Mobile ? Device.Mobile : Device.Desktop;
|
|
40
|
+
const raw = String(value || "").trim().toLowerCase();
|
|
41
|
+
if (raw === Device.Mobile) return Device.Mobile;
|
|
42
|
+
if (raw === Device.Desktop) return Device.Desktop;
|
|
43
|
+
return normalizedFallback;
|
|
44
|
+
};
|
|
32
45
|
var createActorInfo = (info) => {
|
|
33
46
|
const normalizeDomain = (value) => {
|
|
34
47
|
if (!value) return "";
|
|
@@ -106,12 +119,14 @@ var createActorInfo = (info) => {
|
|
|
106
119
|
const domain = normalizeDomain(info.domain);
|
|
107
120
|
const path2 = normalizePath(info.path);
|
|
108
121
|
const share = normalizeShare2(info.share);
|
|
122
|
+
const device = normalizeDevice(info.device);
|
|
109
123
|
return {
|
|
110
124
|
...info,
|
|
111
125
|
protocol,
|
|
112
126
|
domain,
|
|
113
127
|
path: path2,
|
|
114
128
|
share,
|
|
129
|
+
device,
|
|
115
130
|
get icon() {
|
|
116
131
|
if (info.icon) return info.icon;
|
|
117
132
|
return buildIcon(this);
|
|
@@ -163,6 +178,7 @@ var ActorInfo = {
|
|
|
163
178
|
name: "\u8C46\u5305",
|
|
164
179
|
domain: "www.doubao.com",
|
|
165
180
|
path: "/",
|
|
181
|
+
device: Device.Mobile,
|
|
166
182
|
share: {
|
|
167
183
|
mode: "response",
|
|
168
184
|
prefix: "https://www.doubao.com/thread/",
|
|
@@ -178,6 +194,7 @@ var ActorInfo = {
|
|
|
178
194
|
name: "DeepSeek",
|
|
179
195
|
domain: "chat.deepseek.com",
|
|
180
196
|
path: "/",
|
|
197
|
+
device: Device.Mobile,
|
|
181
198
|
share: {
|
|
182
199
|
mode: "response",
|
|
183
200
|
prefix: "https://chat.deepseek.com/share/",
|
|
@@ -334,18 +351,18 @@ var fallbackLog = {
|
|
|
334
351
|
error: (...args) => console.error(...args),
|
|
335
352
|
debug: (...args) => console.debug ? console.debug(...args) : console.log(...args)
|
|
336
353
|
};
|
|
337
|
-
var resolveLogMethod = (
|
|
338
|
-
if (
|
|
339
|
-
return
|
|
354
|
+
var resolveLogMethod = (logger16, name) => {
|
|
355
|
+
if (logger16 && typeof logger16[name] === "function") {
|
|
356
|
+
return logger16[name].bind(logger16);
|
|
340
357
|
}
|
|
341
|
-
if (name === "warning" &&
|
|
342
|
-
return
|
|
358
|
+
if (name === "warning" && logger16 && typeof logger16.warn === "function") {
|
|
359
|
+
return logger16.warn.bind(logger16);
|
|
343
360
|
}
|
|
344
361
|
return fallbackLog[name];
|
|
345
362
|
};
|
|
346
363
|
var defaultLogger = null;
|
|
347
|
-
var setDefaultLogger = (
|
|
348
|
-
defaultLogger =
|
|
364
|
+
var setDefaultLogger = (logger16) => {
|
|
365
|
+
defaultLogger = logger16;
|
|
349
366
|
};
|
|
350
367
|
var resolveLogger = (explicitLogger) => {
|
|
351
368
|
if (explicitLogger && typeof explicitLogger.info === "function") {
|
|
@@ -372,8 +389,8 @@ var colorize = (text, color) => {
|
|
|
372
389
|
var createBaseLogger = (prefix = "", explicitLogger) => {
|
|
373
390
|
const name = prefix ? String(prefix) : "";
|
|
374
391
|
const dispatch = (methodName, icon, message, color) => {
|
|
375
|
-
const
|
|
376
|
-
const logFn = resolveLogMethod(
|
|
392
|
+
const logger16 = resolveLogger(explicitLogger);
|
|
393
|
+
const logFn = resolveLogMethod(logger16, methodName);
|
|
377
394
|
const timestamp = colorize(`[${formatTimestamp()}]`, ANSI.gray);
|
|
378
395
|
const line = formatLine(name, icon, message);
|
|
379
396
|
const coloredLine = colorize(line, color);
|
|
@@ -972,6 +989,12 @@ var PageRuntimeStateKey = "__playwright_toolkit_runtime_state__";
|
|
|
972
989
|
var BROWSER_PROFILE_SCHEMA_VERSION = 1;
|
|
973
990
|
var rememberedRuntimeState = null;
|
|
974
991
|
var isPlainObject = (value) => value && typeof value === "object" && !Array.isArray(value);
|
|
992
|
+
var normalizeKnownDevice = (value) => {
|
|
993
|
+
const raw = String(value || "").trim().toLowerCase();
|
|
994
|
+
if (raw === Device.Mobile) return Device.Mobile;
|
|
995
|
+
if (raw === Device.Desktop) return Device.Desktop;
|
|
996
|
+
return "";
|
|
997
|
+
};
|
|
975
998
|
var deepClone = (value) => {
|
|
976
999
|
if (value == null) return value;
|
|
977
1000
|
try {
|
|
@@ -1323,6 +1346,10 @@ var normalizeObservedBrowserProfile = (value) => {
|
|
|
1323
1346
|
var normalizeBrowserProfileCore = (value) => {
|
|
1324
1347
|
const source = isPlainObject(value) ? value : {};
|
|
1325
1348
|
const profile = {};
|
|
1349
|
+
const device = normalizeKnownDevice(source.device);
|
|
1350
|
+
if (device) {
|
|
1351
|
+
profile.device = device;
|
|
1352
|
+
}
|
|
1326
1353
|
if (isPlainObject(source.fingerprint) && Object.keys(source.fingerprint).length > 0) {
|
|
1327
1354
|
profile.fingerprint = deepClone(source.fingerprint);
|
|
1328
1355
|
}
|
|
@@ -1380,9 +1407,11 @@ var mergeBrowserProfilePayload = (target = {}, source = {}) => {
|
|
|
1380
1407
|
if (Object.keys(mergedCore).length === 0 && Object.keys(incomingCore).length > 0) {
|
|
1381
1408
|
mergedCore = incomingCore;
|
|
1382
1409
|
} else if (Object.keys(incomingCore).length > 0) {
|
|
1410
|
+
const currentDevice = normalizeKnownDevice(currentCore.device);
|
|
1411
|
+
const incomingDevice = normalizeKnownDevice(incomingCore.device);
|
|
1383
1412
|
const currentVersion = Number(currentCore.browser_major_version || 0);
|
|
1384
1413
|
const incomingVersion = Number(incomingCore.browser_major_version || 0);
|
|
1385
|
-
if (currentVersion > 0 && incomingVersion > 0 && currentVersion !== incomingVersion) {
|
|
1414
|
+
if (incomingDevice && currentDevice !== incomingDevice || currentVersion > 0 && incomingVersion > 0 && currentVersion !== incomingVersion) {
|
|
1386
1415
|
mergedCore = incomingCore;
|
|
1387
1416
|
}
|
|
1388
1417
|
}
|
|
@@ -1433,8 +1462,13 @@ var normalizeRuntimeState = (source = {}, actor = "") => {
|
|
|
1433
1462
|
}
|
|
1434
1463
|
return RuntimeEnv.parseInput(source, actor);
|
|
1435
1464
|
};
|
|
1465
|
+
var resolveInputDevice = (input = {}, runtime2 = {}, actor = "") => {
|
|
1466
|
+
const actorDevice = ActorInfo?.[actor]?.device;
|
|
1467
|
+
return normalizeDevice(actorDevice ?? input?.device ?? runtime2?.device);
|
|
1468
|
+
};
|
|
1436
1469
|
var RuntimeEnv = {
|
|
1437
1470
|
tryParseJSON,
|
|
1471
|
+
normalizeDevice,
|
|
1438
1472
|
normalizeCookies,
|
|
1439
1473
|
normalizeLocalStorage,
|
|
1440
1474
|
normalizeSessionStorage,
|
|
@@ -1460,6 +1494,7 @@ var RuntimeEnv = {
|
|
|
1460
1494
|
parseInput(input = {}, actor = "") {
|
|
1461
1495
|
const runtime2 = tryParseJSON(input?.runtime) || {};
|
|
1462
1496
|
const resolvedActor = String(actor || input?.actor || "").trim();
|
|
1497
|
+
const device = resolveInputDevice(input, runtime2, resolvedActor);
|
|
1463
1498
|
const query = String(input?.query || "").trim();
|
|
1464
1499
|
const cookies = normalizeCookies(runtime2?.cookies);
|
|
1465
1500
|
const cookieMap = buildCookieMap(cookies);
|
|
@@ -1479,6 +1514,7 @@ var RuntimeEnv = {
|
|
|
1479
1514
|
}
|
|
1480
1515
|
const state = {
|
|
1481
1516
|
actor: resolvedActor,
|
|
1517
|
+
device,
|
|
1482
1518
|
runtime: normalizedRuntime,
|
|
1483
1519
|
envId,
|
|
1484
1520
|
query,
|
|
@@ -1524,7 +1560,10 @@ var RuntimeEnv = {
|
|
|
1524
1560
|
},
|
|
1525
1561
|
setBrowserProfileCore(source = {}, core = {}, actor = "") {
|
|
1526
1562
|
const state = normalizeRuntimeState(source, actor);
|
|
1527
|
-
const normalizedCore = normalizeBrowserProfileCore(
|
|
1563
|
+
const normalizedCore = normalizeBrowserProfileCore({
|
|
1564
|
+
...core,
|
|
1565
|
+
device: normalizeKnownDevice(core?.device) || state.device
|
|
1566
|
+
});
|
|
1528
1567
|
state.browserProfileCore = normalizedCore;
|
|
1529
1568
|
state.browserProfile = buildBrowserProfilePayload(normalizedCore, state.browserProfileObserved);
|
|
1530
1569
|
if (Object.keys(state.browserProfile).length > 0) {
|
|
@@ -1999,15 +2038,25 @@ var DEFAULT_LAUNCH_ARGS = [
|
|
|
1999
2038
|
"--disable-setuid-sandbox",
|
|
2000
2039
|
"--window-position=0,0"
|
|
2001
2040
|
];
|
|
2002
|
-
function buildFingerprintOptions({ locale = BASE_CONFIG.locale, browserMajorVersion = 0 } = {}) {
|
|
2041
|
+
function buildFingerprintOptions({ locale = BASE_CONFIG.locale, browserMajorVersion = 0, device = Device.Desktop } = {}) {
|
|
2042
|
+
const resolvedDevice = normalizeDevice(device);
|
|
2003
2043
|
const normalizedBrowserMajorVersion = Number(browserMajorVersion || 0);
|
|
2004
2044
|
const browserDescriptor = normalizedBrowserMajorVersion > 0 ? [{ name: "chrome", minVersion: normalizedBrowserMajorVersion, maxVersion: normalizedBrowserMajorVersion }] : [{ name: "chrome", minVersion: 110 }];
|
|
2005
|
-
|
|
2045
|
+
const options = {
|
|
2006
2046
|
browsers: browserDescriptor,
|
|
2007
|
-
devices: [
|
|
2008
|
-
operatingSystems: ["windows", "macos", "linux"],
|
|
2047
|
+
devices: [resolvedDevice],
|
|
2048
|
+
operatingSystems: resolvedDevice === Device.Mobile ? ["android"] : ["windows", "macos", "linux"],
|
|
2009
2049
|
locales: [locale]
|
|
2010
2050
|
};
|
|
2051
|
+
if (resolvedDevice === Device.Mobile) {
|
|
2052
|
+
options.screen = {
|
|
2053
|
+
minWidth: 320,
|
|
2054
|
+
maxWidth: 480,
|
|
2055
|
+
minHeight: 640,
|
|
2056
|
+
maxHeight: 1100
|
|
2057
|
+
};
|
|
2058
|
+
}
|
|
2059
|
+
return options;
|
|
2011
2060
|
}
|
|
2012
2061
|
var AntiCheat = {
|
|
2013
2062
|
/**
|
|
@@ -2017,7 +2066,7 @@ var AntiCheat = {
|
|
|
2017
2066
|
return { ...BASE_CONFIG };
|
|
2018
2067
|
},
|
|
2019
2068
|
/**
|
|
2020
|
-
* 用于 Crawlee fingerprint generator
|
|
2069
|
+
* 用于 Crawlee fingerprint generator 的统一配置。
|
|
2021
2070
|
*/
|
|
2022
2071
|
getFingerprintGeneratorOptions(options = {}) {
|
|
2023
2072
|
return buildFingerprintOptions(options);
|
|
@@ -2030,10 +2079,10 @@ var AntiCheat = {
|
|
|
2030
2079
|
return [...DEFAULT_LAUNCH_ARGS, `--lang=${locale}`];
|
|
2031
2080
|
},
|
|
2032
2081
|
/**
|
|
2033
|
-
* 为 got-scraping 生成与浏览器一致的 TLS
|
|
2082
|
+
* 为 got-scraping 生成与浏览器一致的 TLS 指纹配置。
|
|
2034
2083
|
*/
|
|
2035
|
-
getTlsFingerprintOptions(userAgent = "", acceptLanguage = "") {
|
|
2036
|
-
return buildFingerprintOptions();
|
|
2084
|
+
getTlsFingerprintOptions(userAgent = "", acceptLanguage = "", options = {}) {
|
|
2085
|
+
return buildFingerprintOptions(options);
|
|
2037
2086
|
},
|
|
2038
2087
|
/**
|
|
2039
2088
|
* 规范化请求头
|
|
@@ -2046,7 +2095,7 @@ var AntiCheat = {
|
|
|
2046
2095
|
}
|
|
2047
2096
|
};
|
|
2048
2097
|
|
|
2049
|
-
// src/humanize.js
|
|
2098
|
+
// src/internals/humanize/desktop.js
|
|
2050
2099
|
import delay from "delay";
|
|
2051
2100
|
import { createCursor } from "ghost-cursor-playwright";
|
|
2052
2101
|
var logger6 = createInternalLogger("Humanize");
|
|
@@ -2071,7 +2120,7 @@ var Humanize = {
|
|
|
2071
2120
|
},
|
|
2072
2121
|
/**
|
|
2073
2122
|
* 初始化页面的 Ghost Cursor(必须在使用其他 cursor 相关方法前调用)
|
|
2074
|
-
*
|
|
2123
|
+
*
|
|
2075
2124
|
* @param {import('playwright').Page} page
|
|
2076
2125
|
* @returns {Promise<void>}
|
|
2077
2126
|
*/
|
|
@@ -2087,7 +2136,7 @@ var Humanize = {
|
|
|
2087
2136
|
},
|
|
2088
2137
|
/**
|
|
2089
2138
|
* 人类化鼠标移动 - 使用 ghost-cursor 移动到指定位置或元素
|
|
2090
|
-
*
|
|
2139
|
+
*
|
|
2091
2140
|
* @param {import('playwright').Page} page
|
|
2092
2141
|
* @param {string|{x: number, y: number}|import('playwright').ElementHandle} target - CSS选择器、坐标对象或元素句柄
|
|
2093
2142
|
*/
|
|
@@ -2129,7 +2178,7 @@ var Humanize = {
|
|
|
2129
2178
|
/**
|
|
2130
2179
|
* 渐进式滚动到元素可见(仅处理 Y 轴滚动)
|
|
2131
2180
|
* 返回 restore 方法,用于将滚动容器恢复到原位置
|
|
2132
|
-
*
|
|
2181
|
+
*
|
|
2133
2182
|
* @param {import('playwright').Page} page
|
|
2134
2183
|
* @param {string|import('playwright').ElementHandle} target - CSS 选择器或元素句柄
|
|
2135
2184
|
* @param {Object} [options]
|
|
@@ -2200,7 +2249,7 @@ var Humanize = {
|
|
|
2200
2249
|
return { code: "VISIBLE", isFixed };
|
|
2201
2250
|
});
|
|
2202
2251
|
};
|
|
2203
|
-
const
|
|
2252
|
+
const getScrollableRect2 = async () => {
|
|
2204
2253
|
return await element.evaluate((el) => {
|
|
2205
2254
|
const isScrollable = (node) => {
|
|
2206
2255
|
const style = window.getComputedStyle(node);
|
|
@@ -2243,7 +2292,7 @@ var Humanize = {
|
|
|
2243
2292
|
if (status.code === "OBSTRUCTED" && status.obstruction) {
|
|
2244
2293
|
logger6.debug(`humanScroll | \u88AB\u4EE5\u4E0B\u5143\u7D20\u906E\u6321 <${status.obstruction.tag} id="${status.obstruction.id}">`);
|
|
2245
2294
|
}
|
|
2246
|
-
const scrollRect = await
|
|
2295
|
+
const scrollRect = await getScrollableRect2();
|
|
2247
2296
|
if (!scrollRect && status.isFixed) {
|
|
2248
2297
|
logger6.warn("humanScroll | fixed \u5BB9\u5668\u5185\u4E14\u65E0\u53EF\u6EDA\u52A8\u7956\u5148\uFF0C\u8DF3\u8FC7\u6EDA\u52A8");
|
|
2249
2298
|
return { element, didScroll };
|
|
@@ -2290,7 +2339,7 @@ var Humanize = {
|
|
|
2290
2339
|
},
|
|
2291
2340
|
/**
|
|
2292
2341
|
* 人类化点击 - 使用 ghost-cursor 模拟人类鼠标移动轨迹并点击
|
|
2293
|
-
*
|
|
2342
|
+
*
|
|
2294
2343
|
* @param {import('playwright').Page} page
|
|
2295
2344
|
* @param {string|import('playwright').ElementHandle} [target] - CSS 选择器或元素句柄。如果为空,则点击当前鼠标位置
|
|
2296
2345
|
* @param {Object} [options]
|
|
@@ -2396,7 +2445,7 @@ var Humanize = {
|
|
|
2396
2445
|
* @param {import('playwright').Page} page
|
|
2397
2446
|
* @param {string} selector - 输入框选择器
|
|
2398
2447
|
* @param {string} text - 要输入的文本
|
|
2399
|
-
* @param {Object} [options]
|
|
2448
|
+
* @param {Object} [options]
|
|
2400
2449
|
* @param {number} [options.baseDelay=180] - 基础按键延迟 (ms),实际 ±40% 抖动
|
|
2401
2450
|
* @param {number} [options.pauseProbability=0.08] - 停顿概率 (0-1)
|
|
2402
2451
|
* @param {number} [options.pauseBase=800] - 停顿时长基础值 (ms),实际 ±50% 抖动
|
|
@@ -2524,6 +2573,787 @@ var Humanize = {
|
|
|
2524
2573
|
}
|
|
2525
2574
|
};
|
|
2526
2575
|
|
|
2576
|
+
// src/internals/humanize/shared.js
|
|
2577
|
+
import delay2 from "delay";
|
|
2578
|
+
var jitterMs = (base, jitterPercent = 0.3) => {
|
|
2579
|
+
const jitter = Number(base || 0) * Number(jitterPercent || 0) * (Math.random() * 2 - 1);
|
|
2580
|
+
return Math.max(10, Math.round(Number(base || 0) + jitter));
|
|
2581
|
+
};
|
|
2582
|
+
var randomPointInBox = (box, paddingRatio = 0.24) => {
|
|
2583
|
+
const safePaddingX = Math.max(2, Math.min(box.width * paddingRatio, box.width / 2));
|
|
2584
|
+
const safePaddingY = Math.max(2, Math.min(box.height * paddingRatio, box.height / 2));
|
|
2585
|
+
const x = box.x + safePaddingX + Math.random() * Math.max(1, box.width - safePaddingX * 2);
|
|
2586
|
+
const y = box.y + safePaddingY + Math.random() * Math.max(1, box.height - safePaddingY * 2);
|
|
2587
|
+
return { x, y };
|
|
2588
|
+
};
|
|
2589
|
+
var resolveElement = async (page, target, { throwOnMissing = true } = {}) => {
|
|
2590
|
+
if (target == null) return null;
|
|
2591
|
+
let element = target;
|
|
2592
|
+
if (typeof target === "string") {
|
|
2593
|
+
element = await page.$(target);
|
|
2594
|
+
}
|
|
2595
|
+
if (!element) {
|
|
2596
|
+
if (throwOnMissing) {
|
|
2597
|
+
throw new Error(`\u627E\u4E0D\u5230\u5143\u7D20 ${String(target)}`);
|
|
2598
|
+
}
|
|
2599
|
+
return null;
|
|
2600
|
+
}
|
|
2601
|
+
return element;
|
|
2602
|
+
};
|
|
2603
|
+
var waitJitter = (base, jitterPercent = 0.3) => delay2(jitterMs(base, jitterPercent));
|
|
2604
|
+
|
|
2605
|
+
// src/internals/humanize/mobile.js
|
|
2606
|
+
var logger7 = createInternalLogger("Humanize.Mobile");
|
|
2607
|
+
var initializedPages = /* @__PURE__ */ new WeakSet();
|
|
2608
|
+
var clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
2609
|
+
var resolveViewport = (page) => page?.viewportSize?.() || { width: 390, height: 844 };
|
|
2610
|
+
var describeTarget = (target) => {
|
|
2611
|
+
if (target == null) return "Current Position";
|
|
2612
|
+
return typeof target === "string" ? target : "ElementHandle";
|
|
2613
|
+
};
|
|
2614
|
+
var clipBoxToViewport = (box, viewport) => {
|
|
2615
|
+
if (!box || !viewport) return box;
|
|
2616
|
+
const left = clamp(box.x, 0, viewport.width);
|
|
2617
|
+
const top = clamp(box.y, 0, viewport.height);
|
|
2618
|
+
const right = clamp(box.x + box.width, 0, viewport.width);
|
|
2619
|
+
const bottom = clamp(box.y + box.height, 0, viewport.height);
|
|
2620
|
+
if (right - left > 1 && bottom - top > 1) {
|
|
2621
|
+
return {
|
|
2622
|
+
x: left,
|
|
2623
|
+
y: top,
|
|
2624
|
+
width: right - left,
|
|
2625
|
+
height: bottom - top
|
|
2626
|
+
};
|
|
2627
|
+
}
|
|
2628
|
+
return box;
|
|
2629
|
+
};
|
|
2630
|
+
var checkElementVisibility = async (element) => {
|
|
2631
|
+
return element.evaluate((el) => {
|
|
2632
|
+
const targetStyle = window.getComputedStyle(el);
|
|
2633
|
+
if (!targetStyle || targetStyle.display === "none" || targetStyle.visibility === "hidden" || targetStyle.visibility === "collapse") {
|
|
2634
|
+
return { code: "NOT_INTERACTABLE", reason: "\u5143\u7D20\u4E0D\u53EF\u89C1", direction: "down" };
|
|
2635
|
+
}
|
|
2636
|
+
const rect = el.getBoundingClientRect();
|
|
2637
|
+
if (!rect || rect.width <= 0 || rect.height <= 0) {
|
|
2638
|
+
return { code: "ZERO_DIMENSIONS", reason: "\u5C3A\u5BF8\u4E3A\u96F6", direction: "down" };
|
|
2639
|
+
}
|
|
2640
|
+
const viewW = window.innerWidth;
|
|
2641
|
+
const viewH = window.innerHeight;
|
|
2642
|
+
const centerY = rect.top + rect.height / 2;
|
|
2643
|
+
let clipLeft = 0;
|
|
2644
|
+
let clipRight = viewW;
|
|
2645
|
+
let clipTop = 0;
|
|
2646
|
+
let clipBottom = viewH;
|
|
2647
|
+
let isFixed = false;
|
|
2648
|
+
for (let node = el; node && node !== document.body; node = node.parentElement) {
|
|
2649
|
+
const style = window.getComputedStyle(node);
|
|
2650
|
+
if (style && (style.position === "fixed" || style.position === "sticky")) {
|
|
2651
|
+
isFixed = true;
|
|
2652
|
+
break;
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
for (let node = el.parentElement; node && node !== document.body; node = node.parentElement) {
|
|
2656
|
+
const style = window.getComputedStyle(node);
|
|
2657
|
+
if (!style) continue;
|
|
2658
|
+
const clipsY = ["auto", "scroll", "overlay", "hidden", "clip"].includes(style.overflowY);
|
|
2659
|
+
const clipsX = ["auto", "scroll", "overlay", "hidden", "clip"].includes(style.overflowX);
|
|
2660
|
+
if (!clipsX && !clipsY) continue;
|
|
2661
|
+
const nodeRect = node.getBoundingClientRect();
|
|
2662
|
+
if (!nodeRect || nodeRect.width <= 0 || nodeRect.height <= 0) continue;
|
|
2663
|
+
if (clipsX) {
|
|
2664
|
+
clipLeft = Math.max(clipLeft, nodeRect.left);
|
|
2665
|
+
clipRight = Math.min(clipRight, nodeRect.right);
|
|
2666
|
+
}
|
|
2667
|
+
if (clipsY) {
|
|
2668
|
+
clipTop = Math.max(clipTop, nodeRect.top);
|
|
2669
|
+
clipBottom = Math.min(clipBottom, nodeRect.bottom);
|
|
2670
|
+
}
|
|
2671
|
+
}
|
|
2672
|
+
const visibleLeft = Math.max(clipLeft, Math.min(clipRight, rect.left));
|
|
2673
|
+
const visibleRight = Math.max(clipLeft, Math.min(clipRight, rect.right));
|
|
2674
|
+
const visibleTop = Math.max(clipTop, Math.min(clipBottom, rect.top));
|
|
2675
|
+
const visibleBottom = Math.max(clipTop, Math.min(clipBottom, rect.bottom));
|
|
2676
|
+
const visibleWidth = visibleRight - visibleLeft;
|
|
2677
|
+
const visibleHeight = visibleBottom - visibleTop;
|
|
2678
|
+
const cx = visibleLeft + visibleWidth / 2;
|
|
2679
|
+
const cy = visibleTop + visibleHeight / 2;
|
|
2680
|
+
if (visibleWidth <= 1 || visibleHeight <= 1) {
|
|
2681
|
+
return {
|
|
2682
|
+
code: "OUT_OF_VIEWPORT",
|
|
2683
|
+
reason: "\u4E0D\u5728\u89C6\u53E3\u5185",
|
|
2684
|
+
direction: centerY < clipTop ? "up" : "down",
|
|
2685
|
+
cy: centerY,
|
|
2686
|
+
viewH,
|
|
2687
|
+
isFixed
|
|
2688
|
+
};
|
|
2689
|
+
}
|
|
2690
|
+
const interactiveSelector = [
|
|
2691
|
+
"button",
|
|
2692
|
+
'[role="button"]',
|
|
2693
|
+
"a[href]",
|
|
2694
|
+
"label",
|
|
2695
|
+
"input",
|
|
2696
|
+
"textarea",
|
|
2697
|
+
"select",
|
|
2698
|
+
"summary",
|
|
2699
|
+
'[contenteditable="true"]',
|
|
2700
|
+
'[tabindex]:not([tabindex="-1"])'
|
|
2701
|
+
].join(",");
|
|
2702
|
+
const targetInteractive = typeof el.closest === "function" ? el.closest(interactiveSelector) : null;
|
|
2703
|
+
const sameInteractiveTarget = (pointElement) => {
|
|
2704
|
+
if (!pointElement) return false;
|
|
2705
|
+
if (pointElement === el || el.contains(pointElement) || pointElement.contains(el)) {
|
|
2706
|
+
return true;
|
|
2707
|
+
}
|
|
2708
|
+
if (!targetInteractive || typeof pointElement.closest !== "function") {
|
|
2709
|
+
return false;
|
|
2710
|
+
}
|
|
2711
|
+
return pointElement.closest(interactiveSelector) === targetInteractive;
|
|
2712
|
+
};
|
|
2713
|
+
const describeElement = (node) => {
|
|
2714
|
+
if (!node) return null;
|
|
2715
|
+
const className = typeof node.className === "string" ? node.className : node.className && typeof node.className.baseVal === "string" ? node.className.baseVal : "";
|
|
2716
|
+
const rect2 = node.getBoundingClientRect?.();
|
|
2717
|
+
const style = window.getComputedStyle(node);
|
|
2718
|
+
return {
|
|
2719
|
+
tag: node.tagName,
|
|
2720
|
+
id: node.id || "",
|
|
2721
|
+
className,
|
|
2722
|
+
isFixed: Boolean(style && (style.position === "fixed" || style.position === "sticky")),
|
|
2723
|
+
top: rect2 ? rect2.top : null,
|
|
2724
|
+
bottom: rect2 ? rect2.bottom : null,
|
|
2725
|
+
left: rect2 ? rect2.left : null,
|
|
2726
|
+
right: rect2 ? rect2.right : null
|
|
2727
|
+
};
|
|
2728
|
+
};
|
|
2729
|
+
const samplePoints = [
|
|
2730
|
+
{ x: cx, y: cy },
|
|
2731
|
+
{ x: visibleLeft + Math.min(8, Math.max(1, visibleWidth * 0.25)), y: cy },
|
|
2732
|
+
{ x: visibleRight - Math.min(8, Math.max(1, visibleWidth * 0.25)), y: cy },
|
|
2733
|
+
{ x: cx, y: visibleTop + Math.min(8, Math.max(1, visibleHeight * 0.25)) },
|
|
2734
|
+
{ x: cx, y: visibleBottom - Math.min(8, Math.max(1, visibleHeight * 0.25)) }
|
|
2735
|
+
];
|
|
2736
|
+
let obstruction = null;
|
|
2737
|
+
for (const point of samplePoints) {
|
|
2738
|
+
const pointElement = document.elementFromPoint(point.x, point.y);
|
|
2739
|
+
if (sameInteractiveTarget(pointElement)) {
|
|
2740
|
+
return { code: "VISIBLE", isFixed };
|
|
2741
|
+
}
|
|
2742
|
+
obstruction = obstruction || describeElement(pointElement);
|
|
2743
|
+
}
|
|
2744
|
+
if (obstruction) {
|
|
2745
|
+
return {
|
|
2746
|
+
code: "OBSTRUCTED",
|
|
2747
|
+
reason: "\u88AB\u906E\u6321",
|
|
2748
|
+
direction: cy > viewH / 2 ? "down" : "up",
|
|
2749
|
+
obstruction,
|
|
2750
|
+
cy,
|
|
2751
|
+
viewH,
|
|
2752
|
+
isFixed
|
|
2753
|
+
};
|
|
2754
|
+
}
|
|
2755
|
+
return { code: "VISIBLE", isFixed };
|
|
2756
|
+
});
|
|
2757
|
+
};
|
|
2758
|
+
var resolveSafeTapPoint = async (element) => {
|
|
2759
|
+
return element.evaluate((el) => {
|
|
2760
|
+
const rect = el.getBoundingClientRect();
|
|
2761
|
+
if (!rect || rect.width <= 0 || rect.height <= 0) {
|
|
2762
|
+
return null;
|
|
2763
|
+
}
|
|
2764
|
+
const viewW = window.innerWidth;
|
|
2765
|
+
const viewH = window.innerHeight;
|
|
2766
|
+
let clipLeft = 0;
|
|
2767
|
+
let clipRight = viewW;
|
|
2768
|
+
let clipTop = 0;
|
|
2769
|
+
let clipBottom = viewH;
|
|
2770
|
+
for (let node = el.parentElement; node && node !== document.body; node = node.parentElement) {
|
|
2771
|
+
const style = window.getComputedStyle(node);
|
|
2772
|
+
if (!style) continue;
|
|
2773
|
+
const clipsX = ["auto", "scroll", "overlay", "hidden", "clip"].includes(style.overflowX);
|
|
2774
|
+
const clipsY = ["auto", "scroll", "overlay", "hidden", "clip"].includes(style.overflowY);
|
|
2775
|
+
if (!clipsX && !clipsY) continue;
|
|
2776
|
+
const nodeRect = node.getBoundingClientRect();
|
|
2777
|
+
if (!nodeRect || nodeRect.width <= 0 || nodeRect.height <= 0) continue;
|
|
2778
|
+
if (clipsX) {
|
|
2779
|
+
clipLeft = Math.max(clipLeft, nodeRect.left);
|
|
2780
|
+
clipRight = Math.min(clipRight, nodeRect.right);
|
|
2781
|
+
}
|
|
2782
|
+
if (clipsY) {
|
|
2783
|
+
clipTop = Math.max(clipTop, nodeRect.top);
|
|
2784
|
+
clipBottom = Math.min(clipBottom, nodeRect.bottom);
|
|
2785
|
+
}
|
|
2786
|
+
}
|
|
2787
|
+
const visibleLeft = Math.max(clipLeft, Math.min(clipRight, rect.left));
|
|
2788
|
+
const visibleRight = Math.max(clipLeft, Math.min(clipRight, rect.right));
|
|
2789
|
+
const visibleTop = Math.max(clipTop, Math.min(clipBottom, rect.top));
|
|
2790
|
+
const visibleBottom = Math.max(clipTop, Math.min(clipBottom, rect.bottom));
|
|
2791
|
+
const visibleWidth = visibleRight - visibleLeft;
|
|
2792
|
+
const visibleHeight = visibleBottom - visibleTop;
|
|
2793
|
+
if (visibleWidth <= 1 || visibleHeight <= 1) {
|
|
2794
|
+
return null;
|
|
2795
|
+
}
|
|
2796
|
+
const interactiveSelector = [
|
|
2797
|
+
"button",
|
|
2798
|
+
'[role="button"]',
|
|
2799
|
+
"a[href]",
|
|
2800
|
+
"label",
|
|
2801
|
+
"input",
|
|
2802
|
+
"textarea",
|
|
2803
|
+
"select",
|
|
2804
|
+
"summary",
|
|
2805
|
+
'[contenteditable="true"]',
|
|
2806
|
+
'[tabindex]:not([tabindex="-1"])'
|
|
2807
|
+
].join(",");
|
|
2808
|
+
const targetInteractive = typeof el.closest === "function" ? el.closest(interactiveSelector) : null;
|
|
2809
|
+
const sameInteractiveTarget = (pointElement) => {
|
|
2810
|
+
if (!pointElement) return false;
|
|
2811
|
+
if (pointElement === el || el.contains(pointElement) || pointElement.contains(el)) {
|
|
2812
|
+
return true;
|
|
2813
|
+
}
|
|
2814
|
+
if (!targetInteractive || typeof pointElement.closest !== "function") {
|
|
2815
|
+
return false;
|
|
2816
|
+
}
|
|
2817
|
+
return pointElement.closest(interactiveSelector) === targetInteractive;
|
|
2818
|
+
};
|
|
2819
|
+
const cx = visibleLeft + visibleWidth / 2;
|
|
2820
|
+
const cy = visibleTop + visibleHeight / 2;
|
|
2821
|
+
const smallX = Math.min(12, Math.max(2, visibleWidth * 0.18));
|
|
2822
|
+
const smallY = Math.min(12, Math.max(2, visibleHeight * 0.18));
|
|
2823
|
+
const points = [
|
|
2824
|
+
{ x: cx, y: cy },
|
|
2825
|
+
{ x: visibleLeft + smallX, y: visibleTop + smallY },
|
|
2826
|
+
{ x: visibleRight - smallX, y: visibleTop + smallY },
|
|
2827
|
+
{ x: visibleLeft + smallX, y: visibleBottom - smallY },
|
|
2828
|
+
{ x: visibleRight - smallX, y: visibleBottom - smallY },
|
|
2829
|
+
{ x: cx, y: visibleTop + Math.min(10, Math.max(2, visibleHeight * 0.2)) },
|
|
2830
|
+
{ x: cx, y: visibleBottom - Math.min(10, Math.max(2, visibleHeight * 0.2)) },
|
|
2831
|
+
{ x: visibleLeft + Math.min(10, Math.max(2, visibleWidth * 0.2)), y: cy },
|
|
2832
|
+
{ x: visibleRight - Math.min(10, Math.max(2, visibleWidth * 0.2)), y: cy }
|
|
2833
|
+
];
|
|
2834
|
+
const safePoints = points.filter((point) => {
|
|
2835
|
+
const pointElement = document.elementFromPoint(point.x, point.y);
|
|
2836
|
+
return sameInteractiveTarget(pointElement);
|
|
2837
|
+
});
|
|
2838
|
+
const candidates = safePoints.length ? safePoints : points;
|
|
2839
|
+
const chosen = candidates[Math.floor(Math.random() * candidates.length)];
|
|
2840
|
+
if (!chosen) return null;
|
|
2841
|
+
return {
|
|
2842
|
+
x: chosen.x,
|
|
2843
|
+
y: chosen.y
|
|
2844
|
+
};
|
|
2845
|
+
});
|
|
2846
|
+
};
|
|
2847
|
+
var getScrollableRect = async (element) => {
|
|
2848
|
+
return element.evaluate((el) => {
|
|
2849
|
+
const isScrollable = (node) => {
|
|
2850
|
+
const style = window.getComputedStyle(node);
|
|
2851
|
+
if (!style) return false;
|
|
2852
|
+
const overflowY = style.overflowY;
|
|
2853
|
+
if (!["auto", "scroll", "overlay"].includes(overflowY)) return false;
|
|
2854
|
+
return node.scrollHeight > node.clientHeight + 1;
|
|
2855
|
+
};
|
|
2856
|
+
let current = el;
|
|
2857
|
+
while (current && current !== document.body) {
|
|
2858
|
+
if (isScrollable(current)) {
|
|
2859
|
+
const rect = current.getBoundingClientRect();
|
|
2860
|
+
if (rect && rect.width > 0 && rect.height > 0) {
|
|
2861
|
+
return {
|
|
2862
|
+
x: rect.x,
|
|
2863
|
+
y: rect.y,
|
|
2864
|
+
width: rect.width,
|
|
2865
|
+
height: rect.height
|
|
2866
|
+
};
|
|
2867
|
+
}
|
|
2868
|
+
}
|
|
2869
|
+
current = current.parentElement;
|
|
2870
|
+
}
|
|
2871
|
+
return null;
|
|
2872
|
+
});
|
|
2873
|
+
};
|
|
2874
|
+
var scrollScrollableAncestor = async (element, deltaY) => {
|
|
2875
|
+
return element.evaluate((el, amount) => {
|
|
2876
|
+
const isScrollable = (node) => {
|
|
2877
|
+
const style = window.getComputedStyle(node);
|
|
2878
|
+
if (!style) return false;
|
|
2879
|
+
const overflowY = style.overflowY;
|
|
2880
|
+
if (!["auto", "scroll", "overlay"].includes(overflowY)) return false;
|
|
2881
|
+
return node.scrollHeight > node.clientHeight + 1;
|
|
2882
|
+
};
|
|
2883
|
+
let current = el;
|
|
2884
|
+
while (current && current !== document.body) {
|
|
2885
|
+
if (isScrollable(current)) {
|
|
2886
|
+
const beforeTop2 = current.scrollTop;
|
|
2887
|
+
current.scrollTop = beforeTop2 + amount;
|
|
2888
|
+
return {
|
|
2889
|
+
scroller: true,
|
|
2890
|
+
moved: current.scrollTop !== beforeTop2,
|
|
2891
|
+
scrollTop: current.scrollTop
|
|
2892
|
+
};
|
|
2893
|
+
}
|
|
2894
|
+
current = current.parentElement;
|
|
2895
|
+
}
|
|
2896
|
+
const beforeTop = window.scrollY;
|
|
2897
|
+
window.scrollBy(0, amount);
|
|
2898
|
+
return {
|
|
2899
|
+
scroller: null,
|
|
2900
|
+
moved: window.scrollY !== beforeTop,
|
|
2901
|
+
scrollTop: window.scrollY
|
|
2902
|
+
};
|
|
2903
|
+
}, deltaY);
|
|
2904
|
+
};
|
|
2905
|
+
var dispatchTouchSwipe = async (page, deltaY, options = {}) => {
|
|
2906
|
+
const viewport = resolveViewport(page);
|
|
2907
|
+
const rawRect = options.rect || null;
|
|
2908
|
+
const rect = rawRect ? {
|
|
2909
|
+
x: clamp(rawRect.x, 0, viewport.width),
|
|
2910
|
+
y: clamp(rawRect.y, 0, viewport.height),
|
|
2911
|
+
width: clamp(rawRect.width, 0, viewport.width),
|
|
2912
|
+
height: clamp(rawRect.height, 0, viewport.height)
|
|
2913
|
+
} : null;
|
|
2914
|
+
const area = rect && rect.width > 24 && rect.height > 48 ? {
|
|
2915
|
+
left: rect.x,
|
|
2916
|
+
right: Math.min(viewport.width, rect.x + rect.width),
|
|
2917
|
+
top: rect.y,
|
|
2918
|
+
bottom: Math.min(viewport.height, rect.y + rect.height)
|
|
2919
|
+
} : {
|
|
2920
|
+
left: 0,
|
|
2921
|
+
right: viewport.width,
|
|
2922
|
+
top: 0,
|
|
2923
|
+
bottom: viewport.height
|
|
2924
|
+
};
|
|
2925
|
+
const areaWidth = Math.max(1, area.right - area.left);
|
|
2926
|
+
const areaHeight = Math.max(1, area.bottom - area.top);
|
|
2927
|
+
const maxDistance = rect ? Math.max(60, areaHeight * 0.72) : Math.max(120, viewport.height * 0.72);
|
|
2928
|
+
const distance = clamp(Math.abs(deltaY), Math.min(80, maxDistance), maxDistance);
|
|
2929
|
+
const direction = deltaY >= 0 ? 1 : -1;
|
|
2930
|
+
const startX = clamp(
|
|
2931
|
+
area.left + areaWidth * (0.45 + Math.random() * 0.1),
|
|
2932
|
+
24,
|
|
2933
|
+
viewport.width - 24
|
|
2934
|
+
);
|
|
2935
|
+
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);
|
|
2936
|
+
const endY = clamp(startY - direction * distance, Math.max(16, area.top + 12), Math.min(viewport.height - 16, area.bottom - 12));
|
|
2937
|
+
const steps = Math.max(4, Math.round(Number(options.steps || 6)));
|
|
2938
|
+
const durationMs = jitterMs(options.durationMs || 320, 0.35);
|
|
2939
|
+
let client = null;
|
|
2940
|
+
const scrollBefore = await page.evaluate(() => ({ x: window.scrollX, y: window.scrollY })).catch(() => null);
|
|
2941
|
+
try {
|
|
2942
|
+
if (page.context && typeof page.context().newCDPSession === "function") {
|
|
2943
|
+
client = await page.context().newCDPSession(page);
|
|
2944
|
+
}
|
|
2945
|
+
if (!client) throw new Error("CDP session unavailable");
|
|
2946
|
+
await client.send("Input.synthesizeScrollGesture", {
|
|
2947
|
+
x: startX,
|
|
2948
|
+
y: startY,
|
|
2949
|
+
yDistance: -direction * distance,
|
|
2950
|
+
xOverscroll: (Math.random() - 0.5) * 4,
|
|
2951
|
+
yOverscroll: (Math.random() - 0.5) * 8,
|
|
2952
|
+
speed: 700 + Math.round(Math.random() * 350),
|
|
2953
|
+
gestureSourceType: "touch"
|
|
2954
|
+
});
|
|
2955
|
+
await waitJitter(160, 0.35);
|
|
2956
|
+
const scrollAfterSynth = await page.evaluate(() => ({ x: window.scrollX, y: window.scrollY })).catch(() => null);
|
|
2957
|
+
if (scrollBefore && scrollAfterSynth && (Math.abs(scrollAfterSynth.y - scrollBefore.y) > 2 || Math.abs(scrollAfterSynth.x - scrollBefore.x) > 2)) {
|
|
2958
|
+
return true;
|
|
2959
|
+
}
|
|
2960
|
+
await client.send("Input.dispatchTouchEvent", {
|
|
2961
|
+
type: "touchStart",
|
|
2962
|
+
touchPoints: [{ x: startX, y: startY, id: 1 }]
|
|
2963
|
+
});
|
|
2964
|
+
for (let i = 1; i <= steps; i += 1) {
|
|
2965
|
+
const progress = i / steps;
|
|
2966
|
+
const eased = 1 - Math.pow(1 - progress, 2);
|
|
2967
|
+
const x = startX + (Math.random() - 0.5) * 3;
|
|
2968
|
+
const y = startY + (endY - startY) * eased + (Math.random() - 0.5) * 5;
|
|
2969
|
+
await waitJitter(durationMs / steps, 0.25);
|
|
2970
|
+
await client.send("Input.dispatchTouchEvent", {
|
|
2971
|
+
type: "touchMove",
|
|
2972
|
+
touchPoints: [{ x, y, id: 1 }]
|
|
2973
|
+
});
|
|
2974
|
+
}
|
|
2975
|
+
await client.send("Input.dispatchTouchEvent", {
|
|
2976
|
+
type: "touchEnd",
|
|
2977
|
+
touchPoints: []
|
|
2978
|
+
});
|
|
2979
|
+
await waitJitter(120, 0.35);
|
|
2980
|
+
const scrollAfterTouch = await page.evaluate(() => ({ x: window.scrollX, y: window.scrollY })).catch(() => null);
|
|
2981
|
+
if (scrollBefore && scrollAfterTouch && Math.abs(scrollAfterTouch.y - scrollBefore.y) <= 2 && Math.abs(scrollAfterTouch.x - scrollBefore.x) <= 2) {
|
|
2982
|
+
await page.evaluate((amount) => window.scrollBy(0, amount), deltaY);
|
|
2983
|
+
await waitJitter(120, 0.35);
|
|
2984
|
+
}
|
|
2985
|
+
return true;
|
|
2986
|
+
} catch (error) {
|
|
2987
|
+
logger7.debug(`touch swipe fallback: ${error?.message || error}`);
|
|
2988
|
+
try {
|
|
2989
|
+
await page.evaluate((amount) => window.scrollBy(0, amount), deltaY);
|
|
2990
|
+
await waitJitter(120, 0.35);
|
|
2991
|
+
return true;
|
|
2992
|
+
} catch {
|
|
2993
|
+
await page.mouse.wheel(0, deltaY);
|
|
2994
|
+
await waitJitter(120, 0.35);
|
|
2995
|
+
return true;
|
|
2996
|
+
}
|
|
2997
|
+
} finally {
|
|
2998
|
+
if (client && typeof client.detach === "function") {
|
|
2999
|
+
try {
|
|
3000
|
+
await client.detach();
|
|
3001
|
+
} catch {
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
}
|
|
3005
|
+
};
|
|
3006
|
+
var tapPoint = async (page, point) => {
|
|
3007
|
+
if (page.touchscreen && typeof page.touchscreen.tap === "function") {
|
|
3008
|
+
await page.touchscreen.tap(point.x, point.y);
|
|
3009
|
+
return;
|
|
3010
|
+
}
|
|
3011
|
+
await page.mouse.click(point.x, point.y);
|
|
3012
|
+
};
|
|
3013
|
+
var MobileHumanize = {
|
|
3014
|
+
jitterMs,
|
|
3015
|
+
async initializeCursor(page) {
|
|
3016
|
+
if (initializedPages.has(page)) return;
|
|
3017
|
+
initializedPages.add(page);
|
|
3018
|
+
logger7.debug("initializeCursor: mobile mode uses touch gestures, cursor init skipped");
|
|
3019
|
+
},
|
|
3020
|
+
async humanMove(page, target) {
|
|
3021
|
+
logger7.debug(`humanMove: mobile no-op target=${typeof target === "string" ? target : "element/coords"}`);
|
|
3022
|
+
if (typeof target === "string" || target && typeof target.boundingBox === "function") {
|
|
3023
|
+
const element = await resolveElement(page, target, { throwOnMissing: false });
|
|
3024
|
+
if (!element) {
|
|
3025
|
+
return false;
|
|
3026
|
+
}
|
|
3027
|
+
}
|
|
3028
|
+
await waitJitter(80, 0.4);
|
|
3029
|
+
return true;
|
|
3030
|
+
},
|
|
3031
|
+
async humanScroll(page, target, options = {}) {
|
|
3032
|
+
const {
|
|
3033
|
+
maxSteps = 12,
|
|
3034
|
+
minStep = 180,
|
|
3035
|
+
maxStep = 520,
|
|
3036
|
+
maxDurationMs = maxSteps * 280 + 1200,
|
|
3037
|
+
throwOnMissing = false
|
|
3038
|
+
} = options;
|
|
3039
|
+
const targetDesc = describeTarget(target);
|
|
3040
|
+
logger7.start("humanScroll", `target=${targetDesc}`);
|
|
3041
|
+
const element = await resolveElement(page, target, { throwOnMissing });
|
|
3042
|
+
if (!element) {
|
|
3043
|
+
logger7.warn(`humanScroll | \u5143\u7D20\u672A\u627E\u5230: ${targetDesc}`);
|
|
3044
|
+
return { element: null, didScroll: false, restore: null };
|
|
3045
|
+
}
|
|
3046
|
+
const startTime = Date.now();
|
|
3047
|
+
let didScroll = false;
|
|
3048
|
+
for (let i = 0; i < maxSteps; i += 1) {
|
|
3049
|
+
const status = await checkElementVisibility(element);
|
|
3050
|
+
if (status.code === "VISIBLE") {
|
|
3051
|
+
if (status.isFixed) {
|
|
3052
|
+
logger7.info("humanScroll | fixed/sticky \u5BB9\u5668\u5185\uFF0C\u8DF3\u8FC7\u6EDA\u52A8");
|
|
3053
|
+
} else {
|
|
3054
|
+
logger7.debug("humanScroll | \u5143\u7D20\u53EF\u89C1\u4E14\u65E0\u906E\u6321");
|
|
3055
|
+
}
|
|
3056
|
+
logger7.success("humanScroll", didScroll ? "\u5DF2\u6EDA\u52A8" : "\u65E0\u9700\u6EDA\u52A8");
|
|
3057
|
+
return { element, didScroll, restore: null };
|
|
3058
|
+
}
|
|
3059
|
+
if (status.code === "ZERO_DIMENSIONS" || status.code === "NOT_INTERACTABLE") {
|
|
3060
|
+
logger7.warn(`humanScroll | \u5143\u7D20\u4E0D\u53EF\u6EDA\u52A8\u81F3\u53EF\u70B9\u51FB\u72B6\u6001: ${status.reason || status.code}`);
|
|
3061
|
+
return { element, didScroll, restore: null };
|
|
3062
|
+
}
|
|
3063
|
+
const scrollRect = await getScrollableRect(element);
|
|
3064
|
+
if (!scrollRect && status.isFixed && status.code === "OBSTRUCTED") {
|
|
3065
|
+
logger7.warn(`humanScroll | fixed/sticky \u76EE\u6807\u88AB\u906E\u6321\uFF0C\u6EDA\u52A8\u65E0\u6CD5\u89E3\u9664 (${status.obstruction?.tag || "unknown"})`);
|
|
3066
|
+
return { element, didScroll, restore: null };
|
|
3067
|
+
}
|
|
3068
|
+
if (Date.now() - startTime > maxDurationMs) {
|
|
3069
|
+
logger7.warn(`humanScroll | mobile timeout (${maxDurationMs}ms, status=${status.code}, direction=${status.direction || "unknown"}, fixed=${Boolean(status.isFixed)})`);
|
|
3070
|
+
return { element, didScroll, restore: null };
|
|
3071
|
+
}
|
|
3072
|
+
const stepMin = scrollRect ? Math.min(minStep, Math.max(60, scrollRect.height * 0.4)) : minStep;
|
|
3073
|
+
const stepMax = scrollRect ? Math.min(maxStep, Math.max(stepMin + 40, scrollRect.height * 0.8)) : maxStep;
|
|
3074
|
+
logger7.debug(`humanScroll | \u6B65\u9AA4 ${i + 1}/${maxSteps}: ${status.reason || status.code} ${status.direction ? `(${status.direction})` : ""}`);
|
|
3075
|
+
const distance = stepMin + Math.random() * Math.max(1, stepMax - stepMin);
|
|
3076
|
+
let deltaY = status.direction === "up" ? -distance : distance;
|
|
3077
|
+
if (status.code === "OBSTRUCTED") {
|
|
3078
|
+
if (status.obstruction?.isFixed && status.obstruction.top != null) {
|
|
3079
|
+
const obstructionMiddle = (Number(status.obstruction.top || 0) + Number(status.obstruction.bottom || status.obstruction.top || 0)) / 2;
|
|
3080
|
+
const visibleMiddle = scrollRect ? scrollRect.y + scrollRect.height / 2 : status.viewH / 2;
|
|
3081
|
+
deltaY = obstructionMiddle < visibleMiddle ? -distance : distance;
|
|
3082
|
+
} else {
|
|
3083
|
+
const halfY = scrollRect ? scrollRect.y + scrollRect.height / 2 : status.viewH / 2;
|
|
3084
|
+
deltaY = status.cy > halfY ? distance : -distance;
|
|
3085
|
+
}
|
|
3086
|
+
}
|
|
3087
|
+
const beforeWindowState = scrollRect ? await page.evaluate(() => ({ x: window.scrollX, y: window.scrollY })) : null;
|
|
3088
|
+
const beforeState = scrollRect ? await element.evaluate((el) => {
|
|
3089
|
+
const isScrollable = (node) => {
|
|
3090
|
+
const style = window.getComputedStyle(node);
|
|
3091
|
+
if (!style) return false;
|
|
3092
|
+
const overflowY = style.overflowY;
|
|
3093
|
+
if (!["auto", "scroll", "overlay"].includes(overflowY)) return false;
|
|
3094
|
+
return node.scrollHeight > node.clientHeight + 1;
|
|
3095
|
+
};
|
|
3096
|
+
let current = el;
|
|
3097
|
+
while (current && current !== document.body) {
|
|
3098
|
+
if (isScrollable(current)) {
|
|
3099
|
+
return {
|
|
3100
|
+
kind: "element",
|
|
3101
|
+
top: current.scrollTop,
|
|
3102
|
+
left: current.scrollLeft
|
|
3103
|
+
};
|
|
3104
|
+
}
|
|
3105
|
+
current = current.parentElement;
|
|
3106
|
+
}
|
|
3107
|
+
return { kind: "window", top: window.scrollY, left: window.scrollX };
|
|
3108
|
+
}) : null;
|
|
3109
|
+
await dispatchTouchSwipe(page, deltaY, { rect: scrollRect });
|
|
3110
|
+
if (scrollRect && beforeWindowState) {
|
|
3111
|
+
const afterWindowState = await page.evaluate(() => ({ x: window.scrollX, y: window.scrollY }));
|
|
3112
|
+
if (Math.abs(afterWindowState.x - beforeWindowState.x) > 2 || Math.abs(afterWindowState.y - beforeWindowState.y) > 2) {
|
|
3113
|
+
await page.evaluate((state) => window.scrollTo(state.x, state.y), beforeWindowState);
|
|
3114
|
+
logger7.debug(`humanScroll | \u7A97\u53E3\u6EDA\u52A8\u56DE\u6536 from=${Math.round(afterWindowState.y)} to=${Math.round(beforeWindowState.y)}`);
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
3117
|
+
if (scrollRect && beforeState) {
|
|
3118
|
+
const afterState = await element.evaluate((el) => {
|
|
3119
|
+
const isScrollable = (node) => {
|
|
3120
|
+
const style = window.getComputedStyle(node);
|
|
3121
|
+
if (!style) return false;
|
|
3122
|
+
const overflowY = style.overflowY;
|
|
3123
|
+
if (!["auto", "scroll", "overlay"].includes(overflowY)) return false;
|
|
3124
|
+
return node.scrollHeight > node.clientHeight + 1;
|
|
3125
|
+
};
|
|
3126
|
+
let current = el;
|
|
3127
|
+
while (current && current !== document.body) {
|
|
3128
|
+
if (isScrollable(current)) {
|
|
3129
|
+
return {
|
|
3130
|
+
kind: "element",
|
|
3131
|
+
top: current.scrollTop,
|
|
3132
|
+
left: current.scrollLeft
|
|
3133
|
+
};
|
|
3134
|
+
}
|
|
3135
|
+
current = current.parentElement;
|
|
3136
|
+
}
|
|
3137
|
+
return { kind: "window", top: window.scrollY, left: window.scrollX };
|
|
3138
|
+
});
|
|
3139
|
+
const topDelta = Number(afterState.top || 0) - Number(beforeState.top || 0);
|
|
3140
|
+
const leftDelta = Number(afterState.left || 0) - Number(beforeState.left || 0);
|
|
3141
|
+
const expectedDelta = Number(deltaY || 0);
|
|
3142
|
+
const moved = beforeState.kind !== afterState.kind || Math.abs(topDelta) > 2 || Math.abs(leftDelta) > 2;
|
|
3143
|
+
if (!moved) {
|
|
3144
|
+
const fallback = await scrollScrollableAncestor(element, deltaY);
|
|
3145
|
+
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)}`);
|
|
3146
|
+
} 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)) {
|
|
3147
|
+
const residualDelta = expectedDelta - topDelta;
|
|
3148
|
+
if (Math.sign(residualDelta) === Math.sign(expectedDelta) && Math.abs(residualDelta) > 24) {
|
|
3149
|
+
const fallback = await scrollScrollableAncestor(element, residualDelta);
|
|
3150
|
+
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)}`);
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
}
|
|
3154
|
+
didScroll = true;
|
|
3155
|
+
}
|
|
3156
|
+
try {
|
|
3157
|
+
await element.scrollIntoViewIfNeeded?.();
|
|
3158
|
+
await waitJitter(80, 0.3);
|
|
3159
|
+
const finalStatus = await checkElementVisibility(element);
|
|
3160
|
+
if (finalStatus.code === "VISIBLE") {
|
|
3161
|
+
logger7.info("humanScroll | \u539F\u751F scrollIntoViewIfNeeded \u515C\u5E95\u6210\u529F");
|
|
3162
|
+
logger7.success("humanScroll", didScroll ? "\u5DF2\u6EDA\u52A8" : "\u65E0\u9700\u6EDA\u52A8");
|
|
3163
|
+
return { element, didScroll: true, restore: null };
|
|
3164
|
+
}
|
|
3165
|
+
} catch (fallbackError) {
|
|
3166
|
+
logger7.debug(`humanScroll | native fallback failed: ${fallbackError?.message || fallbackError}`);
|
|
3167
|
+
}
|
|
3168
|
+
logger7.warn(`humanScroll | \u5728 ${maxSteps} \u6B65\u540E\u65E0\u6CD5\u786E\u4FDD\u53EF\u89C1\u6027`);
|
|
3169
|
+
return { element, didScroll, restore: null };
|
|
3170
|
+
},
|
|
3171
|
+
async humanClick(page, target, options = {}) {
|
|
3172
|
+
const {
|
|
3173
|
+
reactionDelay = 220,
|
|
3174
|
+
throwOnMissing = true,
|
|
3175
|
+
scrollIfNeeded = true
|
|
3176
|
+
} = options;
|
|
3177
|
+
const targetDesc = describeTarget(target);
|
|
3178
|
+
logger7.start("humanClick", `target=${targetDesc}`);
|
|
3179
|
+
try {
|
|
3180
|
+
if (target == null) {
|
|
3181
|
+
const viewport = resolveViewport(page);
|
|
3182
|
+
await waitJitter(reactionDelay, 0.45);
|
|
3183
|
+
await tapPoint(page, {
|
|
3184
|
+
x: viewport.width * (0.45 + Math.random() * 0.1),
|
|
3185
|
+
y: viewport.height * (0.48 + Math.random() * 0.12)
|
|
3186
|
+
});
|
|
3187
|
+
logger7.success("humanClick", "Tapped current position");
|
|
3188
|
+
return true;
|
|
3189
|
+
}
|
|
3190
|
+
const element = await resolveElement(page, target, { throwOnMissing });
|
|
3191
|
+
if (!element) {
|
|
3192
|
+
logger7.warn(`humanClick: \u5143\u7D20\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7\u70B9\u51FB ${targetDesc}`);
|
|
3193
|
+
return false;
|
|
3194
|
+
}
|
|
3195
|
+
if (scrollIfNeeded) {
|
|
3196
|
+
await MobileHumanize.humanScroll(page, element, { throwOnMissing });
|
|
3197
|
+
}
|
|
3198
|
+
const status = await checkElementVisibility(element).catch(() => null);
|
|
3199
|
+
if (status && (status.code === "ZERO_DIMENSIONS" || status.code === "NOT_INTERACTABLE")) {
|
|
3200
|
+
const message = `\u5143\u7D20\u4E0D\u53EF\u70B9\u51FB: ${status.reason || status.code}`;
|
|
3201
|
+
if (throwOnMissing) throw new Error(message);
|
|
3202
|
+
logger7.warn(`humanClick: ${message}\uFF0C\u8DF3\u8FC7\u70B9\u51FB`);
|
|
3203
|
+
return false;
|
|
3204
|
+
}
|
|
3205
|
+
const box = await element.boundingBox();
|
|
3206
|
+
if (!box) {
|
|
3207
|
+
if (throwOnMissing) throw new Error("\u65E0\u6CD5\u83B7\u53D6\u5143\u7D20\u4F4D\u7F6E");
|
|
3208
|
+
logger7.warn("humanClick: \u65E0\u6CD5\u83B7\u53D6\u4F4D\u7F6E\uFF0C\u8DF3\u8FC7\u70B9\u51FB");
|
|
3209
|
+
return false;
|
|
3210
|
+
}
|
|
3211
|
+
await waitJitter(reactionDelay, 0.45);
|
|
3212
|
+
const safePoint = await resolveSafeTapPoint(element).catch(() => null);
|
|
3213
|
+
const tapTarget = safePoint || randomPointInBox(clipBoxToViewport(box, resolveViewport(page)), 0.2);
|
|
3214
|
+
await tapPoint(page, tapTarget);
|
|
3215
|
+
await waitJitter(120, 0.35);
|
|
3216
|
+
logger7.success("humanClick");
|
|
3217
|
+
return true;
|
|
3218
|
+
} catch (error) {
|
|
3219
|
+
logger7.fail("humanClick", error);
|
|
3220
|
+
throw error;
|
|
3221
|
+
}
|
|
3222
|
+
},
|
|
3223
|
+
async randomSleep(baseMs, jitterPercent = 0.3) {
|
|
3224
|
+
await waitJitter(baseMs, jitterPercent);
|
|
3225
|
+
},
|
|
3226
|
+
async simulateGaze(page, baseDurationMs = 2500) {
|
|
3227
|
+
const durationMs = jitterMs(baseDurationMs, 0.4);
|
|
3228
|
+
const startTime = Date.now();
|
|
3229
|
+
while (Date.now() - startTime < durationMs) {
|
|
3230
|
+
if (Math.random() < 0.28) {
|
|
3231
|
+
const distance = 70 + Math.random() * 120;
|
|
3232
|
+
await dispatchTouchSwipe(page, Math.random() < 0.7 ? distance : -distance, {
|
|
3233
|
+
durationMs: 180,
|
|
3234
|
+
steps: 4
|
|
3235
|
+
});
|
|
3236
|
+
} else {
|
|
3237
|
+
await waitJitter(420, 0.55);
|
|
3238
|
+
}
|
|
3239
|
+
}
|
|
3240
|
+
},
|
|
3241
|
+
async humanType(page, selector, text, options = {}) {
|
|
3242
|
+
const {
|
|
3243
|
+
baseDelay = 160,
|
|
3244
|
+
pauseProbability = 0.08,
|
|
3245
|
+
pauseBase = 700
|
|
3246
|
+
} = options;
|
|
3247
|
+
await MobileHumanize.humanClick(page, selector, { scrollIfNeeded: true });
|
|
3248
|
+
await waitJitter(220, 0.45);
|
|
3249
|
+
for (let i = 0; i < String(text).length; i += 1) {
|
|
3250
|
+
const char = String(text)[i];
|
|
3251
|
+
const charDelay = /[,.!?;:,。!?;:]/.test(char) ? jitterMs(baseDelay * 1.45, 0.4) : jitterMs(char === " " ? baseDelay * 0.65 : baseDelay, 0.4);
|
|
3252
|
+
await page.keyboard.type(char);
|
|
3253
|
+
await waitJitter(charDelay, 0.15);
|
|
3254
|
+
if (Math.random() < pauseProbability && i < String(text).length - 1) {
|
|
3255
|
+
await waitJitter(pauseBase, 0.5);
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
},
|
|
3259
|
+
async humanClear(page, selector) {
|
|
3260
|
+
const locator = page.locator(selector);
|
|
3261
|
+
await MobileHumanize.humanClick(page, locator, { scrollIfNeeded: true });
|
|
3262
|
+
await waitJitter(160, 0.4);
|
|
3263
|
+
await locator.evaluate((el) => {
|
|
3264
|
+
if ("value" in el) {
|
|
3265
|
+
el.value = "";
|
|
3266
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3267
|
+
el.dispatchEvent(new Event("change", { bubbles: true }));
|
|
3268
|
+
return;
|
|
3269
|
+
}
|
|
3270
|
+
el.textContent = "";
|
|
3271
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
3272
|
+
});
|
|
3273
|
+
},
|
|
3274
|
+
async warmUpBrowsing(page, baseDuration = 3500) {
|
|
3275
|
+
const durationMs = jitterMs(baseDuration, 0.4);
|
|
3276
|
+
const startTime = Date.now();
|
|
3277
|
+
while (Date.now() - startTime < durationMs) {
|
|
3278
|
+
const action = Math.random();
|
|
3279
|
+
if (action < 0.5) {
|
|
3280
|
+
await dispatchTouchSwipe(page, 120 + Math.random() * 220, {
|
|
3281
|
+
durationMs: 240,
|
|
3282
|
+
steps: 5
|
|
3283
|
+
});
|
|
3284
|
+
} else if (action < 0.7) {
|
|
3285
|
+
await dispatchTouchSwipe(page, -(80 + Math.random() * 160), {
|
|
3286
|
+
durationMs: 220,
|
|
3287
|
+
steps: 4
|
|
3288
|
+
});
|
|
3289
|
+
} else {
|
|
3290
|
+
await waitJitter(560, 0.55);
|
|
3291
|
+
}
|
|
3292
|
+
}
|
|
3293
|
+
},
|
|
3294
|
+
async naturalScroll(page, direction = "down", distance = 300, baseSteps = 5) {
|
|
3295
|
+
const steps = Math.max(3, baseSteps + Math.floor(Math.random() * 3) - 1);
|
|
3296
|
+
const actualDistance = jitterMs(distance, 0.15);
|
|
3297
|
+
const sign = direction === "down" ? 1 : -1;
|
|
3298
|
+
for (let i = 0; i < steps; i += 1) {
|
|
3299
|
+
const factor = 1 - i / steps * 0.45;
|
|
3300
|
+
await dispatchTouchSwipe(page, actualDistance / steps * factor * sign, {
|
|
3301
|
+
durationMs: 150 + i * 20,
|
|
3302
|
+
steps: 4
|
|
3303
|
+
});
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3306
|
+
};
|
|
3307
|
+
|
|
3308
|
+
// src/humanize.js
|
|
3309
|
+
var resolveDeviceFromPage = (page) => normalizeDevice(page?.[PageRuntimeStateKey]?.device);
|
|
3310
|
+
var resolveDelegate = (page) => {
|
|
3311
|
+
return resolveDeviceFromPage(page) === Device.Mobile ? MobileHumanize : Humanize;
|
|
3312
|
+
};
|
|
3313
|
+
var callDelegate = (method, page, args) => {
|
|
3314
|
+
const delegate = resolveDelegate(page);
|
|
3315
|
+
return delegate[method](page, ...args);
|
|
3316
|
+
};
|
|
3317
|
+
var Humanize2 = {
|
|
3318
|
+
jitterMs(base, jitterPercent = 0.3) {
|
|
3319
|
+
return Humanize.jitterMs(base, jitterPercent);
|
|
3320
|
+
},
|
|
3321
|
+
initializeCursor(page) {
|
|
3322
|
+
return callDelegate("initializeCursor", page, []);
|
|
3323
|
+
},
|
|
3324
|
+
humanMove(page, target) {
|
|
3325
|
+
return callDelegate("humanMove", page, [target]);
|
|
3326
|
+
},
|
|
3327
|
+
humanScroll(page, target, options = {}) {
|
|
3328
|
+
return callDelegate("humanScroll", page, [target, options]);
|
|
3329
|
+
},
|
|
3330
|
+
humanClick(page, target, options = {}) {
|
|
3331
|
+
return callDelegate("humanClick", page, [target, options]);
|
|
3332
|
+
},
|
|
3333
|
+
randomSleep(pageOrBaseMs, maybeBaseMs, maybeJitterPercent) {
|
|
3334
|
+
if (pageOrBaseMs && typeof pageOrBaseMs === "object" && typeof pageOrBaseMs.evaluate === "function") {
|
|
3335
|
+
const delegate = resolveDelegate(pageOrBaseMs);
|
|
3336
|
+
return delegate.randomSleep(maybeBaseMs, maybeJitterPercent);
|
|
3337
|
+
}
|
|
3338
|
+
return Humanize.randomSleep(pageOrBaseMs, maybeBaseMs);
|
|
3339
|
+
},
|
|
3340
|
+
simulateGaze(page, baseDurationMs = 2500) {
|
|
3341
|
+
return callDelegate("simulateGaze", page, [baseDurationMs]);
|
|
3342
|
+
},
|
|
3343
|
+
humanType(page, selector, text, options = {}) {
|
|
3344
|
+
return callDelegate("humanType", page, [selector, text, options]);
|
|
3345
|
+
},
|
|
3346
|
+
humanClear(page, selector) {
|
|
3347
|
+
return callDelegate("humanClear", page, [selector]);
|
|
3348
|
+
},
|
|
3349
|
+
warmUpBrowsing(page, baseDuration = 3500) {
|
|
3350
|
+
return callDelegate("warmUpBrowsing", page, [baseDuration]);
|
|
3351
|
+
},
|
|
3352
|
+
naturalScroll(page, direction = "down", distance = 300, baseSteps = 5) {
|
|
3353
|
+
return callDelegate("naturalScroll", page, [direction, distance, baseSteps]);
|
|
3354
|
+
}
|
|
3355
|
+
};
|
|
3356
|
+
|
|
2527
3357
|
// src/launch.js
|
|
2528
3358
|
import { execFileSync } from "node:child_process";
|
|
2529
3359
|
import { FingerprintGenerator } from "fingerprint-generator";
|
|
@@ -2610,7 +3440,7 @@ var ByPass = {
|
|
|
2610
3440
|
};
|
|
2611
3441
|
|
|
2612
3442
|
// src/launch.js
|
|
2613
|
-
var
|
|
3443
|
+
var logger8 = createInternalLogger("Launch");
|
|
2614
3444
|
var REQUEST_HOOK_FLAG = Symbol("playwright-toolkit-request-hook");
|
|
2615
3445
|
var injectedContexts = /* @__PURE__ */ new WeakSet();
|
|
2616
3446
|
var browserMajorVersionCache = /* @__PURE__ */ new Map();
|
|
@@ -2662,20 +3492,29 @@ var detectBrowserMajorVersion = (launcher) => {
|
|
|
2662
3492
|
});
|
|
2663
3493
|
detectedVersion = parseChromeMajorVersion(rawVersion);
|
|
2664
3494
|
} catch (error) {
|
|
2665
|
-
|
|
3495
|
+
logger8.warn(`\u8BFB\u53D6\u6D4F\u89C8\u5668\u7248\u672C\u5931\u8D25: ${error?.message || error}`);
|
|
2666
3496
|
}
|
|
2667
3497
|
browserMajorVersionCache.set(executablePath, detectedVersion);
|
|
2668
3498
|
return detectedVersion;
|
|
2669
3499
|
};
|
|
2670
|
-
var
|
|
3500
|
+
var resolveRuntimeDevice = (runtimeState = {}) => normalizeDevice(runtimeState?.device);
|
|
3501
|
+
var resolveCoreDevice = (core = {}) => {
|
|
3502
|
+
const raw = String(core?.device || "").trim().toLowerCase();
|
|
3503
|
+
if (raw === Device.Mobile) return Device.Mobile;
|
|
3504
|
+
if (raw === Device.Desktop) return Device.Desktop;
|
|
3505
|
+
return "";
|
|
3506
|
+
};
|
|
3507
|
+
var buildFingerprintGenerator = ({ locale, browserMajorVersion, device }) => {
|
|
2671
3508
|
return new FingerprintGenerator(
|
|
2672
3509
|
AntiCheat.getFingerprintGeneratorOptions({
|
|
2673
3510
|
locale,
|
|
2674
|
-
browserMajorVersion
|
|
3511
|
+
browserMajorVersion,
|
|
3512
|
+
device
|
|
2675
3513
|
})
|
|
2676
3514
|
);
|
|
2677
3515
|
};
|
|
2678
3516
|
var buildReplayableBrowserProfile = (runtimeState, launcher) => {
|
|
3517
|
+
const device = resolveRuntimeDevice(runtimeState);
|
|
2679
3518
|
if (!runtimeState || !RuntimeEnv.hasLoginState(runtimeState)) {
|
|
2680
3519
|
return { runtimeState, browserProfileCore: null };
|
|
2681
3520
|
}
|
|
@@ -2685,29 +3524,34 @@ var buildReplayableBrowserProfile = (runtimeState, launcher) => {
|
|
|
2685
3524
|
const locale = DEFAULT_LOCALE;
|
|
2686
3525
|
const currentBrowserMajorVersion = detectBrowserMajorVersion(launcher);
|
|
2687
3526
|
const storedBrowserMajorVersion = Number(browserProfileCore?.browser_major_version || 0);
|
|
2688
|
-
const
|
|
3527
|
+
const storedDevice = resolveCoreDevice(browserProfileCore);
|
|
3528
|
+
const needsDeviceRebuild = storedDevice ? storedDevice !== device : device === Device.Mobile;
|
|
3529
|
+
const needsRebuild = !browserProfileCore?.fingerprint || Object.keys(browserProfileCore.fingerprint || {}).length === 0 || needsDeviceRebuild || currentBrowserMajorVersion > 0 && storedBrowserMajorVersion > 0 && storedBrowserMajorVersion !== currentBrowserMajorVersion;
|
|
2689
3530
|
if (needsRebuild) {
|
|
2690
3531
|
const generator = buildFingerprintGenerator({
|
|
2691
3532
|
locale,
|
|
2692
|
-
browserMajorVersion: currentBrowserMajorVersion
|
|
3533
|
+
browserMajorVersion: currentBrowserMajorVersion,
|
|
3534
|
+
device
|
|
2693
3535
|
});
|
|
2694
3536
|
const fingerprint = generator.getFingerprint();
|
|
2695
3537
|
const fingerprintBrowserMajorVersion = parseChromeMajorVersion(fingerprint?.fingerprint?.navigator?.userAgent || "");
|
|
2696
3538
|
browserProfileCore = {
|
|
2697
3539
|
fingerprint,
|
|
3540
|
+
device,
|
|
2698
3541
|
timezone_id: timezoneId,
|
|
2699
3542
|
locale,
|
|
2700
3543
|
browser_major_version: currentBrowserMajorVersion > 0 ? currentBrowserMajorVersion : fingerprintBrowserMajorVersion,
|
|
2701
3544
|
schema_version: DEFAULT_BROWSER_PROFILE_SCHEMA_VERSION
|
|
2702
3545
|
};
|
|
2703
3546
|
nextState = RuntimeEnv.setBrowserProfileCore(nextState, browserProfileCore);
|
|
2704
|
-
|
|
2705
|
-
`\u5DF2\u751F\u6210\u6D4F\u89C8\u5668\u6307\u7EB9\u771F\u6E90 | env=${String(nextState.envId || "-")} | version=${browserProfileCore.browser_major_version || "-"} | timezone=${timezoneId}`
|
|
3547
|
+
logger8.info(
|
|
3548
|
+
`\u5DF2\u751F\u6210\u6D4F\u89C8\u5668\u6307\u7EB9\u771F\u6E90 | env=${String(nextState.envId || "-")} | device=${device} | version=${browserProfileCore.browser_major_version || "-"} | timezone=${timezoneId}`
|
|
2706
3549
|
);
|
|
2707
3550
|
return { runtimeState: nextState, browserProfileCore };
|
|
2708
3551
|
}
|
|
2709
3552
|
const normalizedBrowserProfileCore = {
|
|
2710
3553
|
...browserProfileCore,
|
|
3554
|
+
device,
|
|
2711
3555
|
timezone_id: timezoneId,
|
|
2712
3556
|
locale,
|
|
2713
3557
|
schema_version: Number(browserProfileCore.schema_version || 0) > 0 ? Number(browserProfileCore.schema_version || 0) : DEFAULT_BROWSER_PROFILE_SCHEMA_VERSION,
|
|
@@ -2723,6 +3567,35 @@ var buildReplayableBrowserProfile = (runtimeState, launcher) => {
|
|
|
2723
3567
|
browserProfileCore: normalizedBrowserProfileCore
|
|
2724
3568
|
};
|
|
2725
3569
|
};
|
|
3570
|
+
var applyDevicePageOptions = (pageOptions = {}, { device = Device.Desktop, fingerprint = null } = {}) => {
|
|
3571
|
+
if (!pageOptions || typeof pageOptions !== "object") return;
|
|
3572
|
+
const resolvedDevice = normalizeDevice(device);
|
|
3573
|
+
const screen = fingerprint?.screen || {};
|
|
3574
|
+
const dpr = Number(screen.devicePixelRatio || 0);
|
|
3575
|
+
if (resolvedDevice === Device.Mobile) {
|
|
3576
|
+
pageOptions.isMobile = true;
|
|
3577
|
+
pageOptions.hasTouch = true;
|
|
3578
|
+
pageOptions.deviceScaleFactor = dpr > 0 ? dpr : 2;
|
|
3579
|
+
if (!pageOptions.viewport) {
|
|
3580
|
+
pageOptions.viewport = {
|
|
3581
|
+
width: Number(screen.width || 390),
|
|
3582
|
+
height: Number(screen.height || 844)
|
|
3583
|
+
};
|
|
3584
|
+
}
|
|
3585
|
+
if (!pageOptions.screen) {
|
|
3586
|
+
pageOptions.screen = {
|
|
3587
|
+
width: Number(screen.width || pageOptions.viewport.width || 390),
|
|
3588
|
+
height: Number(screen.height || pageOptions.viewport.height || 844)
|
|
3589
|
+
};
|
|
3590
|
+
}
|
|
3591
|
+
return;
|
|
3592
|
+
}
|
|
3593
|
+
pageOptions.isMobile = false;
|
|
3594
|
+
pageOptions.hasTouch = false;
|
|
3595
|
+
if (!pageOptions.deviceScaleFactor) {
|
|
3596
|
+
pageOptions.deviceScaleFactor = dpr > 0 ? dpr : 1;
|
|
3597
|
+
}
|
|
3598
|
+
};
|
|
2726
3599
|
var buildReplayBrowserPoolOptions = (browserProfileCore) => {
|
|
2727
3600
|
const fingerprintWithHeaders = browserProfileCore?.fingerprint;
|
|
2728
3601
|
const fingerprint = fingerprintWithHeaders?.fingerprint;
|
|
@@ -2755,6 +3628,10 @@ var buildReplayBrowserPoolOptions = (browserProfileCore) => {
|
|
|
2755
3628
|
if (browserProfileCore.timezone_id) {
|
|
2756
3629
|
pageOptions.timezoneId = browserProfileCore.timezone_id;
|
|
2757
3630
|
}
|
|
3631
|
+
applyDevicePageOptions(pageOptions, {
|
|
3632
|
+
device: browserProfileCore.device,
|
|
3633
|
+
fingerprint
|
|
3634
|
+
});
|
|
2758
3635
|
}
|
|
2759
3636
|
],
|
|
2760
3637
|
postPageCreateHooks: [
|
|
@@ -2784,6 +3661,7 @@ var Launch = {
|
|
|
2784
3661
|
postNavigationHooks = [],
|
|
2785
3662
|
runtimeState = null
|
|
2786
3663
|
} = normalizedOptions;
|
|
3664
|
+
const device = resolveRuntimeDevice(runtimeState);
|
|
2787
3665
|
const { byPassDomains, enableProxy, proxyUrl } = resolveProxyLaunchOptions(proxyConfiguration);
|
|
2788
3666
|
const byPassRules = ByPass.buildByPassDomainRules(byPassDomains);
|
|
2789
3667
|
const proxyMeter = enableProxy && proxyUrl ? ProxyMeterRuntime.startProxyMeter({ proxyUrl, debugMode }) : null;
|
|
@@ -2812,18 +3690,18 @@ var Launch = {
|
|
|
2812
3690
|
upstreamLabel = `${parsedProxyUrl.protocol}//${parsedProxyUrl.host}`;
|
|
2813
3691
|
} catch {
|
|
2814
3692
|
}
|
|
2815
|
-
|
|
3693
|
+
logger8.info(
|
|
2816
3694
|
`[\u4EE3\u7406\u5DF2\u542F\u7528] \u672C\u5730=${launchProxy.server} \u4E0A\u6E38=${upstreamLabel || "-"} \u76F4\u8FDE\u57DF\u540D=${(byPassDomains || []).join(",")}`
|
|
2817
3695
|
);
|
|
2818
|
-
|
|
3696
|
+
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`);
|
|
2819
3697
|
} else if (enableByPassLogger && enableProxy && !launchProxy) {
|
|
2820
|
-
|
|
2821
|
-
|
|
3698
|
+
logger8.info("[\u4EE3\u7406\u672A\u542F\u7528] enable_proxy=true \u4F46 proxy_url \u4E3A\u7A7A");
|
|
3699
|
+
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`);
|
|
2822
3700
|
} else if (enableByPassLogger && !enableProxy && proxyUrl) {
|
|
2823
|
-
|
|
2824
|
-
|
|
3701
|
+
logger8.info("[\u4EE3\u7406\u672A\u542F\u7528] enable_proxy=false \u4E14 proxy_url \u5DF2\u914D\u7F6E");
|
|
3702
|
+
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`);
|
|
2825
3703
|
} else if (enableByPassLogger) {
|
|
2826
|
-
|
|
3704
|
+
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`);
|
|
2827
3705
|
}
|
|
2828
3706
|
const onPageCreated = (page) => {
|
|
2829
3707
|
const recommendedGotoOptions = {
|
|
@@ -2845,7 +3723,7 @@ var Launch = {
|
|
|
2845
3723
|
}
|
|
2846
3724
|
if (!enableByPassLogger || byPassDomains.length === 0) return;
|
|
2847
3725
|
if (!matched || !matched.rule) return;
|
|
2848
|
-
|
|
3726
|
+
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}`);
|
|
2849
3727
|
};
|
|
2850
3728
|
page.on("request", requestHandler);
|
|
2851
3729
|
return recommendedGotoOptions;
|
|
@@ -2864,8 +3742,23 @@ var Launch = {
|
|
|
2864
3742
|
browserPoolOptions: replayBrowserPoolOptions || {
|
|
2865
3743
|
useFingerprints: true,
|
|
2866
3744
|
fingerprintOptions: {
|
|
2867
|
-
fingerprintGeneratorOptions: AntiCheat.getFingerprintGeneratorOptions(
|
|
2868
|
-
|
|
3745
|
+
fingerprintGeneratorOptions: AntiCheat.getFingerprintGeneratorOptions({
|
|
3746
|
+
locale: launchLocale,
|
|
3747
|
+
device
|
|
3748
|
+
})
|
|
3749
|
+
},
|
|
3750
|
+
prePageCreateHooks: [
|
|
3751
|
+
(_pageId, _browserController, pageOptions = {}) => {
|
|
3752
|
+
applyDevicePageOptions(pageOptions, { device });
|
|
3753
|
+
if (launchLocale) {
|
|
3754
|
+
pageOptions.locale = launchLocale;
|
|
3755
|
+
}
|
|
3756
|
+
const timezoneId = AntiCheat.getBaseConfig().timezoneId;
|
|
3757
|
+
if (timezoneId) {
|
|
3758
|
+
pageOptions.timezoneId = timezoneId;
|
|
3759
|
+
}
|
|
3760
|
+
}
|
|
3761
|
+
]
|
|
2869
3762
|
},
|
|
2870
3763
|
launchContext
|
|
2871
3764
|
};
|
|
@@ -2888,7 +3781,7 @@ var Launch = {
|
|
|
2888
3781
|
// src/live-view.js
|
|
2889
3782
|
import express from "express";
|
|
2890
3783
|
import { Actor } from "apify";
|
|
2891
|
-
var
|
|
3784
|
+
var logger9 = createInternalLogger("LiveView");
|
|
2892
3785
|
async function startLiveViewServer(liveViewKey) {
|
|
2893
3786
|
const app = express();
|
|
2894
3787
|
app.get("/", async (req, res) => {
|
|
@@ -2913,13 +3806,13 @@ async function startLiveViewServer(liveViewKey) {
|
|
|
2913
3806
|
</html>
|
|
2914
3807
|
`);
|
|
2915
3808
|
} catch (error) {
|
|
2916
|
-
|
|
3809
|
+
logger9.fail("Live View Server", error);
|
|
2917
3810
|
res.status(500).send(`\u65E0\u6CD5\u52A0\u8F7D\u5C4F\u5E55\u622A\u56FE: ${error.message}`);
|
|
2918
3811
|
}
|
|
2919
3812
|
});
|
|
2920
3813
|
const port = process.env.APIFY_CONTAINER_PORT || 4321;
|
|
2921
3814
|
app.listen(port, () => {
|
|
2922
|
-
|
|
3815
|
+
logger9.success("startLiveViewServer", `\u76D1\u542C\u7AEF\u53E3 ${port}`);
|
|
2923
3816
|
});
|
|
2924
3817
|
}
|
|
2925
3818
|
async function takeLiveScreenshot(liveViewKey, page, logMessage) {
|
|
@@ -2927,10 +3820,10 @@ async function takeLiveScreenshot(liveViewKey, page, logMessage) {
|
|
|
2927
3820
|
const buffer = await capturePageScreenshot(page, { type: "png" });
|
|
2928
3821
|
await Actor.setValue(liveViewKey, buffer, { contentType: "image/png" });
|
|
2929
3822
|
if (logMessage) {
|
|
2930
|
-
|
|
3823
|
+
logger9.info(`(\u622A\u56FE): ${logMessage}`);
|
|
2931
3824
|
}
|
|
2932
3825
|
} catch (e) {
|
|
2933
|
-
|
|
3826
|
+
logger9.warn(`\u65E0\u6CD5\u6355\u83B7 Live View \u5C4F\u5E55\u622A\u56FE: ${e.message}`);
|
|
2934
3827
|
}
|
|
2935
3828
|
}
|
|
2936
3829
|
var useLiveView = (liveViewKey = PresetOfLiveViewKey) => {
|
|
@@ -3030,7 +3923,7 @@ var dragCaptchaWithMouse = async (page, sourceLocator, targetLocator) => {
|
|
|
3030
3923
|
};
|
|
3031
3924
|
|
|
3032
3925
|
// src/internals/captcha/bytedance.js
|
|
3033
|
-
var
|
|
3926
|
+
var logger10 = createInternalLogger("Captcha");
|
|
3034
3927
|
var DEFAULT_BYTEDANCE_CAPTCHA_OPTIONS = Object.freeze({
|
|
3035
3928
|
apiType: "31234",
|
|
3036
3929
|
maxRetries: 3,
|
|
@@ -3097,14 +3990,14 @@ var getVerifycenterCaptchaContext = async (page, options) => {
|
|
|
3097
3990
|
if (!isContainerVisible) {
|
|
3098
3991
|
return null;
|
|
3099
3992
|
}
|
|
3100
|
-
|
|
3993
|
+
logger10.info("\u68C0\u6D4B\u5230\u9A8C\u8BC1\u7801\u5BB9\u5668\uFF0C\u5F00\u59CB\u7B49\u5F85 iframe \u52A0\u8F7D\u3002");
|
|
3101
3994
|
let iframeLocator = page.locator(options.iframeSelector).first();
|
|
3102
3995
|
let isIframeVisible = await waitForVisible(
|
|
3103
3996
|
iframeLocator,
|
|
3104
3997
|
options.iframeVisibleTimeoutMs
|
|
3105
3998
|
);
|
|
3106
3999
|
if (!isIframeVisible) {
|
|
3107
|
-
|
|
4000
|
+
logger10.warn("\u672A\u5728\u9884\u671F\u9009\u62E9\u5668\u4E2D\u627E\u5230 verifycenter iframe\uFF0C\u5C1D\u8BD5\u5BB9\u5668\u5185\u4EFB\u610F iframe\u3002");
|
|
3108
4001
|
iframeLocator = captchaContainer.locator(options.iframeFallbackSelector).first();
|
|
3109
4002
|
isIframeVisible = await waitForVisible(
|
|
3110
4003
|
iframeLocator,
|
|
@@ -3114,7 +4007,7 @@ var getVerifycenterCaptchaContext = async (page, options) => {
|
|
|
3114
4007
|
if (!isIframeVisible) {
|
|
3115
4008
|
throw new Error("verifycenter iframe not found inside captcha container.");
|
|
3116
4009
|
}
|
|
3117
|
-
|
|
4010
|
+
logger10.info("\u9A8C\u8BC1\u7801 iframe \u5DF2\u53EF\u89C1\uFF0C\u5F00\u59CB\u89E3\u6790\u5185\u5BB9 frame\u3002");
|
|
3118
4011
|
const frame = await resolveContentFrame(page, iframeLocator, options);
|
|
3119
4012
|
if (!frame) {
|
|
3120
4013
|
throw new Error("Failed to resolve verifycenter iframe content frame.");
|
|
@@ -3124,7 +4017,7 @@ var getVerifycenterCaptchaContext = async (page, options) => {
|
|
|
3124
4017
|
var refreshCaptcha = async (page, frame, options) => {
|
|
3125
4018
|
const clicked = await clickCaptchaAction(frame, options.refreshTexts, options).catch(() => false);
|
|
3126
4019
|
if (!clicked) {
|
|
3127
|
-
|
|
4020
|
+
logger10.warn("Refresh button not found.");
|
|
3128
4021
|
return false;
|
|
3129
4022
|
}
|
|
3130
4023
|
await page.waitForTimeout(options.refreshWaitMs);
|
|
@@ -3165,18 +4058,18 @@ var waitForCaptchaChallengeReady = async (page, frame, options) => {
|
|
|
3165
4058
|
const imageCount = await sourceImages.count().catch(() => 0);
|
|
3166
4059
|
const hasVisibleSourceImage = imageCount > 0 ? await sourceImages.first().isVisible({ timeout: options.loadingIndicatorVisibleTimeoutMs }).catch(() => false) : false;
|
|
3167
4060
|
if (!isLoadingVisible && hasVisibleSourceImage) {
|
|
3168
|
-
|
|
4061
|
+
logger10.info(hasSeenLoading ? "\u9A8C\u8BC1\u7801\u56FE\u7247\u5DF2\u52A0\u8F7D\u5B8C\u6210\u3002" : "\u9A8C\u8BC1\u7801\u56FE\u7247\u5DF2\u5C31\u7EEA\u3002");
|
|
3169
4062
|
return;
|
|
3170
4063
|
}
|
|
3171
4064
|
if (hasErrorTextVisible) {
|
|
3172
|
-
|
|
4065
|
+
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");
|
|
3173
4066
|
await refreshCaptcha(page, frame, options);
|
|
3174
4067
|
refreshDeadline = Date.now() + options.challengeReadyRefreshTimeoutMs;
|
|
3175
4068
|
hasSeenLoading = false;
|
|
3176
4069
|
continue;
|
|
3177
4070
|
}
|
|
3178
4071
|
if (!hasVisibleSourceImage && Date.now() >= refreshDeadline) {
|
|
3179
|
-
|
|
4072
|
+
logger10.warn(`\u9A8C\u8BC1\u7801\u56FE\u7247\u8D85\u8FC7 ${options.challengeReadyRefreshTimeoutMs}ms \u4ECD\u672A\u51FA\u73B0\uFF0C\u5C1D\u8BD5\u5237\u65B0\u9898\u76EE\u3002`);
|
|
3180
4073
|
await refreshCaptcha(page, frame, options);
|
|
3181
4074
|
refreshDeadline = Date.now() + options.challengeReadyRefreshTimeoutMs;
|
|
3182
4075
|
hasSeenLoading = false;
|
|
@@ -3196,16 +4089,16 @@ async function solveCaptcha(page, options = {}, dependencies = {}) {
|
|
|
3196
4089
|
...options
|
|
3197
4090
|
};
|
|
3198
4091
|
if (!config.token) {
|
|
3199
|
-
|
|
4092
|
+
logger10.warn("\u7F3A\u5C11\u9A8C\u8BC1\u7801 token\uFF0C\u8DF3\u8FC7\u81EA\u52A8\u8BC6\u522B\u3002");
|
|
3200
4093
|
return false;
|
|
3201
4094
|
}
|
|
3202
|
-
|
|
4095
|
+
logger10.info("\u5F53\u524D\u4F7F\u7528\u672Ctool\u2014\u2014\u6D4B\u8BD5\u7248\u672C");
|
|
3203
4096
|
for (let attempt = 1; attempt <= config.maxRetries; attempt++) {
|
|
3204
|
-
|
|
4097
|
+
logger10.info(`\u5F00\u59CB\u7B2C ${attempt}/${config.maxRetries} \u6B21 verifycenter \u9A8C\u8BC1\u7801\u8BC6\u522B\u3002`);
|
|
3205
4098
|
try {
|
|
3206
4099
|
const captchaContext = await getVerifycenterCaptchaContext(page, config);
|
|
3207
4100
|
if (!captchaContext) {
|
|
3208
|
-
|
|
4101
|
+
logger10.info("Captcha container is not visible anymore.");
|
|
3209
4102
|
return true;
|
|
3210
4103
|
}
|
|
3211
4104
|
const { iframeLocator, frame } = captchaContext;
|
|
@@ -3220,16 +4113,16 @@ async function solveCaptcha(page, options = {}, dependencies = {}) {
|
|
|
3220
4113
|
});
|
|
3221
4114
|
const serialNumbers = extractCaptchaSerialNumbers(apiResponse);
|
|
3222
4115
|
if (apiResponse?.code !== config.recognitionSuccessCode || serialNumbers.length === 0) {
|
|
3223
|
-
|
|
4116
|
+
logger10.warn(
|
|
3224
4117
|
`\u9A8C\u8BC1\u7801\u8BC6\u522B\u5931\u8D25\u3002code=${apiResponse?.code}, msg=${apiResponse?.msg || "unknown"}`
|
|
3225
4118
|
);
|
|
3226
4119
|
await refreshCaptcha(page, frame, config);
|
|
3227
4120
|
continue;
|
|
3228
4121
|
}
|
|
3229
|
-
|
|
4122
|
+
logger10.info(`\u9A8C\u8BC1\u7801\u8BC6\u522B\u6210\u529F\uFF0C\u5E8F\u53F7\uFF1A${serialNumbers.join(", ")}`);
|
|
3230
4123
|
const dropTarget = await findCaptchaDropTarget(frame, config);
|
|
3231
4124
|
if (!dropTarget) {
|
|
3232
|
-
|
|
4125
|
+
logger10.warn("\u672A\u627E\u5230\u9A8C\u8BC1\u7801\u62D6\u62FD\u76EE\u6807\u533A\u57DF\u3002");
|
|
3233
4126
|
await refreshCaptcha(page, frame, config);
|
|
3234
4127
|
continue;
|
|
3235
4128
|
}
|
|
@@ -3252,31 +4145,31 @@ async function solveCaptcha(page, options = {}, dependencies = {}) {
|
|
|
3252
4145
|
}
|
|
3253
4146
|
const submitted = await clickCaptchaAction(frame, config.submitTexts, config).catch(() => false);
|
|
3254
4147
|
if (!submitted) {
|
|
3255
|
-
|
|
4148
|
+
logger10.warn("\u672A\u627E\u5230\u63D0\u4EA4\u6309\u94AE\uFF0C\u53EF\u80FD\u4F1A\u81EA\u52A8\u63D0\u4EA4\u3002");
|
|
3256
4149
|
}
|
|
3257
4150
|
await page.waitForTimeout(config.submitWaitMs);
|
|
3258
4151
|
const stillVisible = await iframeLocator.isVisible({ timeout: config.containerVisibleTimeoutMs }).catch(() => false);
|
|
3259
4152
|
if (!stillVisible) {
|
|
3260
|
-
|
|
4153
|
+
logger10.info("\u9A8C\u8BC1\u7801\u8BC6\u522B\u5E76\u63D0\u4EA4\u6210\u529F\u3002");
|
|
3261
4154
|
return true;
|
|
3262
4155
|
}
|
|
3263
|
-
|
|
4156
|
+
logger10.warn("\u63D0\u4EA4\u540E\u9A8C\u8BC1\u7801 iframe \u4ECD\u7136\u53EF\u89C1\uFF0C\u51C6\u5907\u5237\u65B0\u540E\u91CD\u8BD5\u3002");
|
|
3264
4157
|
await page.waitForTimeout(2e3);
|
|
3265
4158
|
await refreshCaptcha(page, frame, config);
|
|
3266
4159
|
} catch (error) {
|
|
3267
|
-
|
|
4160
|
+
logger10.error(`\u7B2C ${attempt}/${config.maxRetries} \u6B21\u9A8C\u8BC1\u7801\u8BC6\u522B\u5931\u8D25\uFF1A${error?.message || error}`);
|
|
3268
4161
|
}
|
|
3269
4162
|
if (attempt < config.maxRetries) {
|
|
3270
4163
|
await page.waitForTimeout(config.retryDelayBaseMs + attempt * config.retryDelayStepMs);
|
|
3271
4164
|
}
|
|
3272
4165
|
}
|
|
3273
|
-
|
|
4166
|
+
logger10.error(`\u91CD\u8BD5 ${config.maxRetries} \u6B21\u540E\uFF0C\u9A8C\u8BC1\u7801\u4ECD\u672A\u8BC6\u522B\u6210\u529F\u3002`);
|
|
3274
4167
|
return false;
|
|
3275
4168
|
}
|
|
3276
4169
|
var sloveCaptcha = solveCaptcha;
|
|
3277
4170
|
|
|
3278
4171
|
// src/chaptcha.js
|
|
3279
|
-
var
|
|
4172
|
+
var logger11 = createInternalLogger("Captcha");
|
|
3280
4173
|
var DEFAULT_CAPTCHA_RECOGNITION_OPTIONS = Object.freeze({
|
|
3281
4174
|
token: "eKJvBfwfN0YRav0-VD_44E2VBSfm7l0YtddUQ7cFySI",
|
|
3282
4175
|
apiUrl: "https://api.jfbym.com/api/YmServer/customApi"
|
|
@@ -3363,7 +4256,7 @@ function useCaptchaMonitor(page, options) {
|
|
|
3363
4256
|
};
|
|
3364
4257
|
})();
|
|
3365
4258
|
}, { selector: domSelector, callbackName: exposedFunctionName, cleanerName });
|
|
3366
|
-
|
|
4259
|
+
logger11.success("useCaptchaMonitor", `DOM \u76D1\u63A7\u5DF2\u542F\u7528\uFF1A${domSelector}`);
|
|
3367
4260
|
cleanupFns.push(async () => {
|
|
3368
4261
|
try {
|
|
3369
4262
|
await page.evaluate((name) => {
|
|
@@ -3387,14 +4280,14 @@ function useCaptchaMonitor(page, options) {
|
|
|
3387
4280
|
}
|
|
3388
4281
|
};
|
|
3389
4282
|
page.on("framenavigated", frameHandler);
|
|
3390
|
-
|
|
4283
|
+
logger11.success("useCaptchaMonitor", `URL \u76D1\u63A7\u5DF2\u542F\u7528\uFF1A${urlPattern}`);
|
|
3391
4284
|
cleanupFns.push(async () => {
|
|
3392
4285
|
page.off("framenavigated", frameHandler);
|
|
3393
4286
|
});
|
|
3394
4287
|
}
|
|
3395
4288
|
return {
|
|
3396
4289
|
stop: async () => {
|
|
3397
|
-
|
|
4290
|
+
logger11.info("\u6B63\u5728\u505C\u6B62\u9A8C\u8BC1\u7801\u76D1\u63A7...");
|
|
3398
4291
|
for (const fn of cleanupFns) {
|
|
3399
4292
|
await fn();
|
|
3400
4293
|
}
|
|
@@ -3433,7 +4326,7 @@ async function solveCaptchaWithStrategy(strategyName, page, options = {}) {
|
|
|
3433
4326
|
);
|
|
3434
4327
|
return strategy.sloveCaptcha(page, resolvedOptions, {
|
|
3435
4328
|
callCaptchaRecognitionApi,
|
|
3436
|
-
logger:
|
|
4329
|
+
logger: logger11
|
|
3437
4330
|
});
|
|
3438
4331
|
}
|
|
3439
4332
|
var Captcha = {
|
|
@@ -3444,7 +4337,7 @@ var Captcha = {
|
|
|
3444
4337
|
// src/mutation.js
|
|
3445
4338
|
import { createHash } from "node:crypto";
|
|
3446
4339
|
import { v4 as uuidv42 } from "uuid";
|
|
3447
|
-
var
|
|
4340
|
+
var logger12 = createInternalLogger("Mutation");
|
|
3448
4341
|
var MUTATION_MONITOR_MODE = Object.freeze({
|
|
3449
4342
|
Added: "added",
|
|
3450
4343
|
Changed: "changed",
|
|
@@ -3477,14 +4370,14 @@ var Mutation = {
|
|
|
3477
4370
|
const stableTime = options.stableTime ?? 5 * 1e3;
|
|
3478
4371
|
const timeout = options.timeout ?? 120 * 1e3;
|
|
3479
4372
|
const onMutation = options.onMutation;
|
|
3480
|
-
|
|
4373
|
+
logger12.start("waitForStable", `\u76D1\u63A7 ${selectorList.length} \u4E2A\u9009\u62E9\u5668, \u7A33\u5B9A\u65F6\u95F4=${stableTime}ms`);
|
|
3481
4374
|
if (initialTimeout > 0) {
|
|
3482
4375
|
const selectorQuery = selectorList.join(",");
|
|
3483
4376
|
try {
|
|
3484
4377
|
await page.waitForSelector(selectorQuery, { timeout: initialTimeout });
|
|
3485
|
-
|
|
4378
|
+
logger12.info(`waitForStable \u5DF2\u68C0\u6D4B\u5230\u5143\u7D20: ${selectorQuery}`);
|
|
3486
4379
|
} catch (e) {
|
|
3487
|
-
|
|
4380
|
+
logger12.warning(`waitForStable \u521D\u59CB\u7B49\u5F85\u8D85\u65F6 (${initialTimeout}ms): ${selectorQuery}`);
|
|
3488
4381
|
throw e;
|
|
3489
4382
|
}
|
|
3490
4383
|
}
|
|
@@ -3500,7 +4393,7 @@ var Mutation = {
|
|
|
3500
4393
|
return "__CONTINUE__";
|
|
3501
4394
|
}
|
|
3502
4395
|
});
|
|
3503
|
-
|
|
4396
|
+
logger12.info("waitForStable \u5DF2\u542F\u7528 onMutation \u56DE\u8C03");
|
|
3504
4397
|
} catch (e) {
|
|
3505
4398
|
}
|
|
3506
4399
|
}
|
|
@@ -3615,9 +4508,9 @@ var Mutation = {
|
|
|
3615
4508
|
{ selectorList, stableTime, timeout, callbackName, hasCallback: !!onMutation }
|
|
3616
4509
|
);
|
|
3617
4510
|
if (result.mutationCount === 0 && result.stableTime === 0) {
|
|
3618
|
-
|
|
4511
|
+
logger12.warning("waitForStable \u672A\u627E\u5230\u53EF\u76D1\u63A7\u7684\u5143\u7D20");
|
|
3619
4512
|
}
|
|
3620
|
-
|
|
4513
|
+
logger12.success("waitForStable", `DOM \u7A33\u5B9A, \u603B\u5171 ${result.mutationCount} \u6B21\u53D8\u5316${result.wasPaused ? ", \u66FE\u6682\u505C\u8BA1\u65F6" : ""}`);
|
|
3621
4514
|
return result;
|
|
3622
4515
|
},
|
|
3623
4516
|
/**
|
|
@@ -3789,22 +4682,22 @@ var Mutation = {
|
|
|
3789
4682
|
return "__CONTINUE__";
|
|
3790
4683
|
}
|
|
3791
4684
|
};
|
|
3792
|
-
|
|
4685
|
+
logger12.start(
|
|
3793
4686
|
"waitForStableAcrossRoots",
|
|
3794
4687
|
`\u76D1\u63A7 ${selectorList.length} \u4E2A\u9009\u62E9\u5668(\u8DE8 root), \u7A33\u5B9A\u65F6\u95F4=${waitForStableTime}ms`
|
|
3795
4688
|
);
|
|
3796
4689
|
if (initialTimeout > 0) {
|
|
3797
4690
|
try {
|
|
3798
4691
|
await page.waitForSelector(selectorQuery, { timeout: initialTimeout });
|
|
3799
|
-
|
|
4692
|
+
logger12.info(`waitForStableAcrossRoots \u5DF2\u68C0\u6D4B\u5230\u5143\u7D20: ${selectorQuery}`);
|
|
3800
4693
|
} catch (e) {
|
|
3801
|
-
|
|
4694
|
+
logger12.warning(`waitForStableAcrossRoots \u521D\u59CB\u7B49\u5F85\u8D85\u65F6 (${initialTimeout}ms): ${selectorQuery}`);
|
|
3802
4695
|
throw e;
|
|
3803
4696
|
}
|
|
3804
4697
|
}
|
|
3805
4698
|
let state = await buildState();
|
|
3806
4699
|
if (!state?.hasMatched) {
|
|
3807
|
-
|
|
4700
|
+
logger12.warning("waitForStableAcrossRoots \u672A\u627E\u5230\u53EF\u76D1\u63A7\u7684\u5143\u7D20");
|
|
3808
4701
|
return { mutationCount: 0, stableTime: 0, wasPaused: false };
|
|
3809
4702
|
}
|
|
3810
4703
|
let mutationCount = 0;
|
|
@@ -3841,7 +4734,7 @@ var Mutation = {
|
|
|
3841
4734
|
if (lastState.snapshotKey !== lastSnapshotKey) {
|
|
3842
4735
|
lastSnapshotKey = lastState.snapshotKey;
|
|
3843
4736
|
mutationCount += 1;
|
|
3844
|
-
|
|
4737
|
+
logger12.info(
|
|
3845
4738
|
`waitForStableAcrossRoots \u53D8\u5316#${mutationCount}, len=${lastState.snapshotLength}, path=${lastState.primaryPath || "unknown"}, preview="${truncate(lastState.text, 120)}"`
|
|
3846
4739
|
);
|
|
3847
4740
|
const signal = await invokeMutationCallback({
|
|
@@ -3854,7 +4747,7 @@ var Mutation = {
|
|
|
3854
4747
|
continue;
|
|
3855
4748
|
}
|
|
3856
4749
|
if (!isPaused && stableSince > 0 && Date.now() - stableSince >= waitForStableTime) {
|
|
3857
|
-
|
|
4750
|
+
logger12.success("waitForStableAcrossRoots", `DOM \u7A33\u5B9A, \u603B\u5171 ${mutationCount} \u6B21\u53D8\u5316${wasPaused ? ", \u66FE\u6682\u505C\u8BA1\u65F6" : ""}`);
|
|
3858
4751
|
return {
|
|
3859
4752
|
mutationCount,
|
|
3860
4753
|
stableTime: waitForStableTime,
|
|
@@ -3881,7 +4774,7 @@ var Mutation = {
|
|
|
3881
4774
|
const onMutation = options.onMutation;
|
|
3882
4775
|
const rawMode = String(options.mode || MUTATION_MONITOR_MODE.Added).toLowerCase();
|
|
3883
4776
|
const mode = [MUTATION_MONITOR_MODE.Added, MUTATION_MONITOR_MODE.Changed, MUTATION_MONITOR_MODE.All].includes(rawMode) ? rawMode : MUTATION_MONITOR_MODE.Added;
|
|
3884
|
-
|
|
4777
|
+
logger12.start("useMonitor", `\u76D1\u63A7 ${selectorList.length} \u4E2A\u9009\u62E9\u5668, mode=${mode}`);
|
|
3885
4778
|
const monitorKey = generateKey("pk_mon");
|
|
3886
4779
|
const callbackName = generateKey("pk_mon_cb");
|
|
3887
4780
|
const cleanerName = generateKey("pk_mon_clean");
|
|
@@ -4024,7 +4917,7 @@ var Mutation = {
|
|
|
4024
4917
|
return total;
|
|
4025
4918
|
};
|
|
4026
4919
|
}, { selectorList, monitorKey, callbackName, cleanerName, hasCallback: !!onMutation, mode });
|
|
4027
|
-
|
|
4920
|
+
logger12.success("useMonitor", "\u76D1\u63A7\u5668\u5DF2\u542F\u52A8");
|
|
4028
4921
|
return {
|
|
4029
4922
|
stop: async () => {
|
|
4030
4923
|
let totalMutations = 0;
|
|
@@ -4037,7 +4930,7 @@ var Mutation = {
|
|
|
4037
4930
|
}, cleanerName);
|
|
4038
4931
|
} catch (e) {
|
|
4039
4932
|
}
|
|
4040
|
-
|
|
4933
|
+
logger12.success("useMonitor.stop", `\u76D1\u63A7\u5DF2\u505C\u6B62, \u5171 ${totalMutations} \u6B21\u53D8\u5316`);
|
|
4041
4934
|
return { totalMutations };
|
|
4042
4935
|
}
|
|
4043
4936
|
};
|
|
@@ -4906,7 +5799,7 @@ var createTemplateLogger = (baseLogger = createBaseLogger()) => {
|
|
|
4906
5799
|
};
|
|
4907
5800
|
var getDefaultBaseLogger = () => createBaseLogger("");
|
|
4908
5801
|
var Logger = {
|
|
4909
|
-
setLogger: (
|
|
5802
|
+
setLogger: (logger16) => setDefaultLogger(logger16),
|
|
4910
5803
|
info: (message) => getDefaultBaseLogger().info(message),
|
|
4911
5804
|
success: (message) => getDefaultBaseLogger().success(message),
|
|
4912
5805
|
warning: (message) => getDefaultBaseLogger().warning(message),
|
|
@@ -4914,14 +5807,14 @@ var Logger = {
|
|
|
4914
5807
|
error: (message) => getDefaultBaseLogger().error(message),
|
|
4915
5808
|
debug: (message) => getDefaultBaseLogger().debug(message),
|
|
4916
5809
|
start: (message) => getDefaultBaseLogger().start(message),
|
|
4917
|
-
useTemplate: (
|
|
4918
|
-
if (
|
|
5810
|
+
useTemplate: (logger16) => {
|
|
5811
|
+
if (logger16) return createTemplateLogger(createBaseLogger("", logger16));
|
|
4919
5812
|
return createTemplateLogger();
|
|
4920
5813
|
}
|
|
4921
5814
|
};
|
|
4922
5815
|
|
|
4923
5816
|
// src/share.js
|
|
4924
|
-
import
|
|
5817
|
+
import delay3 from "delay";
|
|
4925
5818
|
|
|
4926
5819
|
// src/internals/watermarkify.js
|
|
4927
5820
|
var DEFAULT_TIMEZONE_OFFSET = 8;
|
|
@@ -4980,7 +5873,7 @@ var LOCATION_NETWORK_SUFFIX_PATTERNS = [
|
|
|
4980
5873
|
];
|
|
4981
5874
|
var cachedStripLogoSrcPromise = null;
|
|
4982
5875
|
var cachedEnrichmentByContext = /* @__PURE__ */ new WeakMap();
|
|
4983
|
-
var
|
|
5876
|
+
var logger13 = createInternalLogger("Watermarkify");
|
|
4984
5877
|
var normalizeText = (value) => String(value || "").trim();
|
|
4985
5878
|
var toInline = (value, maxLen = 200) => {
|
|
4986
5879
|
const text = normalizeText(value);
|
|
@@ -5222,9 +6115,9 @@ var resolveWithCustomResolver = async (page, baseMeta, options = {}) => {
|
|
|
5222
6115
|
location: toInline(resolved.location, 80)
|
|
5223
6116
|
};
|
|
5224
6117
|
if (enrichment.ip || enrichment.location) {
|
|
5225
|
-
|
|
6118
|
+
logger13.info(`\u81EA\u5B9A\u4E49 resolver \u547D\u4E2D: ip=${enrichment.ip || "-"}, loc=${enrichment.location || "-"}`);
|
|
5226
6119
|
} else {
|
|
5227
|
-
|
|
6120
|
+
logger13.warning("\u81EA\u5B9A\u4E49 resolver \u5DF2\u6267\u884C\uFF0C\u4F46\u672A\u8FD4\u56DE IP/Loc");
|
|
5228
6121
|
}
|
|
5229
6122
|
return enrichment;
|
|
5230
6123
|
} finally {
|
|
@@ -5408,12 +6301,12 @@ var buildWatermarkifyRenderHtml = ({ imageSrc, overlaySvg, width, height }) => {
|
|
|
5408
6301
|
};
|
|
5409
6302
|
var composeScreenshotBufferWithBrowser = async (page, buffer, overlaySvg, imageInfo = {}) => {
|
|
5410
6303
|
if (!page || typeof page.context !== "function") {
|
|
5411
|
-
|
|
6304
|
+
logger13.warning("watermarkify \u6D4F\u89C8\u5668\u5408\u6210\u8DF3\u8FC7: \u7F3A\u5C11\u53EF\u7528 page");
|
|
5412
6305
|
return buffer;
|
|
5413
6306
|
}
|
|
5414
6307
|
const renderScope = await openProbePage(page);
|
|
5415
6308
|
if (!renderScope?.page) {
|
|
5416
|
-
|
|
6309
|
+
logger13.warning("watermarkify \u6D4F\u89C8\u5668\u5408\u6210\u8DF3\u8FC7: \u65E0\u6CD5\u521B\u5EFA render page");
|
|
5417
6310
|
return buffer;
|
|
5418
6311
|
}
|
|
5419
6312
|
try {
|
|
@@ -5456,13 +6349,13 @@ var composeScreenshotBufferWithBrowser = async (page, buffer, overlaySvg, imageI
|
|
|
5456
6349
|
fullPage: true,
|
|
5457
6350
|
animations: "disabled"
|
|
5458
6351
|
}).catch((error) => {
|
|
5459
|
-
|
|
6352
|
+
logger13.warning(`watermarkify \u6D4F\u89C8\u5668\u5408\u6210\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`);
|
|
5460
6353
|
return null;
|
|
5461
6354
|
});
|
|
5462
6355
|
if (Buffer.isBuffer(composed) && composed.length > 0) {
|
|
5463
6356
|
return composed;
|
|
5464
6357
|
}
|
|
5465
|
-
|
|
6358
|
+
logger13.warning("watermarkify \u6D4F\u89C8\u5668\u5408\u6210\u5931\u8D25: \u672A\u5F97\u5230\u6709\u6548\u622A\u56FE\u7ED3\u679C");
|
|
5466
6359
|
return buffer;
|
|
5467
6360
|
} finally {
|
|
5468
6361
|
await renderScope.close().catch(() => {
|
|
@@ -5475,7 +6368,7 @@ var resolveWithIpLookup = async (page, options = {}) => {
|
|
|
5475
6368
|
}
|
|
5476
6369
|
const probeScope = await openProbePage(page);
|
|
5477
6370
|
if (!probeScope?.page) {
|
|
5478
|
-
|
|
6371
|
+
logger13.warning("ipLookup \u8DF3\u8FC7: \u65E0\u6CD5\u521B\u5EFA probe page");
|
|
5479
6372
|
return null;
|
|
5480
6373
|
}
|
|
5481
6374
|
const timeoutMs = Math.max(
|
|
@@ -5484,12 +6377,12 @@ var resolveWithIpLookup = async (page, options = {}) => {
|
|
|
5484
6377
|
);
|
|
5485
6378
|
try {
|
|
5486
6379
|
const probePage = probeScope.page;
|
|
5487
|
-
|
|
6380
|
+
logger13.info(`ipLookup \u5C1D\u8BD5: url=${DEFAULT_IP_LOOKUP_URL}, timeoutMs=${timeoutMs}`);
|
|
5488
6381
|
const response = await probePage.goto(DEFAULT_IP_LOOKUP_URL, {
|
|
5489
6382
|
waitUntil: "commit",
|
|
5490
6383
|
timeout: timeoutMs
|
|
5491
6384
|
}).catch((error) => {
|
|
5492
|
-
|
|
6385
|
+
logger13.warning(`ipLookup \u8BF7\u6C42\u5931\u8D25: url=${DEFAULT_IP_LOOKUP_URL}, error=${error instanceof Error ? error.message : String(error)}`);
|
|
5493
6386
|
return null;
|
|
5494
6387
|
});
|
|
5495
6388
|
const status = response && typeof response.status === "function" ? response.status() : 0;
|
|
@@ -5511,13 +6404,13 @@ var resolveWithIpLookup = async (page, options = {}) => {
|
|
|
5511
6404
|
}
|
|
5512
6405
|
const parsed = parseIpIpJsonResponse(rawText);
|
|
5513
6406
|
if (parsed?.ip || parsed?.location) {
|
|
5514
|
-
|
|
6407
|
+
logger13.info(`ipLookup \u6210\u529F: url=${DEFAULT_IP_LOOKUP_URL}, status=${status || "-"}, contentType=${contentType || "-"}, ip=${parsed.ip || "-"}, loc=${parsed.location || "-"}`);
|
|
5515
6408
|
return parsed;
|
|
5516
6409
|
}
|
|
5517
|
-
|
|
6410
|
+
logger13.warning(`ipLookup \u672A\u89E3\u6790\u51FA IP/Loc: url=${DEFAULT_IP_LOOKUP_URL}, status=${status || "-"}, contentType=${contentType || "-"}, preview=${shortenTail(rawText, 120) || "[empty]"}`);
|
|
5518
6411
|
return null;
|
|
5519
6412
|
} catch (error) {
|
|
5520
|
-
|
|
6413
|
+
logger13.warning(`ipLookup \u6267\u884C\u5F02\u5E38\uFF0C\u672A\u83B7\u5F97 IP/Loc: ${error instanceof Error ? error.message : String(error)}`);
|
|
5521
6414
|
return null;
|
|
5522
6415
|
} finally {
|
|
5523
6416
|
await probeScope.close().catch(() => {
|
|
@@ -5531,10 +6424,10 @@ var resolveEnrichment = async (page, baseMeta, options) => {
|
|
|
5531
6424
|
ip: toInline(options.ip, 80),
|
|
5532
6425
|
location: toInline(options.location, 80)
|
|
5533
6426
|
};
|
|
5534
|
-
|
|
6427
|
+
logger13.info(`enrichment \u5F00\u59CB: host=${baseMeta.hostname || "-"}, hasPresetIp=${Boolean(merged.ip)}, hasPresetLoc=${Boolean(merged.location)}, ipLookup=${options.ipLookup !== false}`);
|
|
5535
6428
|
if (!merged.ip || !merged.location) {
|
|
5536
6429
|
if (cached?.ip || cached?.location) {
|
|
5537
|
-
|
|
6430
|
+
logger13.info(`enrichment \u547D\u4E2D\u4E0A\u4E0B\u6587\u7F13\u5B58: ip=${cached.ip || "-"}, loc=${cached.location || "-"}`);
|
|
5538
6431
|
}
|
|
5539
6432
|
fillEnrichment(merged, cached);
|
|
5540
6433
|
}
|
|
@@ -5558,15 +6451,15 @@ var resolveEnrichment = async (page, baseMeta, options) => {
|
|
|
5558
6451
|
"x-geo-country"
|
|
5559
6452
|
]), 80);
|
|
5560
6453
|
if (!merged.location || isWeakLocationValue(merged.location) && headerLocation) {
|
|
5561
|
-
|
|
6454
|
+
logger13.info(`enrichment \u4F7F\u7528\u54CD\u5E94\u5934\u8865\u5145 Loc: ${headerLocation || "-"}`);
|
|
5562
6455
|
merged.location = headerLocation || merged.location;
|
|
5563
6456
|
}
|
|
5564
6457
|
}
|
|
5565
6458
|
writeCachedEnrichment(page, merged);
|
|
5566
6459
|
if (merged.ip || merged.location) {
|
|
5567
|
-
|
|
6460
|
+
logger13.info(`enrichment \u5B8C\u6210: ip=${merged.ip || "-"}, loc=${merged.location || "-"}`);
|
|
5568
6461
|
} else {
|
|
5569
|
-
|
|
6462
|
+
logger13.warning("enrichment \u5B8C\u6210: \u672A\u83B7\u5F97 IP/Loc");
|
|
5570
6463
|
}
|
|
5571
6464
|
return merged;
|
|
5572
6465
|
};
|
|
@@ -6234,7 +7127,7 @@ var watermarkifyScreenshotBuffer = async (buffer, meta, page = null) => {
|
|
|
6234
7127
|
}
|
|
6235
7128
|
const imageInfo = readImageInfo(buffer);
|
|
6236
7129
|
if (!imageInfo.width || !imageInfo.height || !imageInfo.mimeType) {
|
|
6237
|
-
|
|
7130
|
+
logger13.warning("watermarkify \u8DF3\u8FC7: \u65E0\u6CD5\u89E3\u6790\u622A\u56FE\u5C3A\u5BF8\u6216\u683C\u5F0F");
|
|
6238
7131
|
return buffer;
|
|
6239
7132
|
}
|
|
6240
7133
|
const overlaySvg = buildWatermarkifySvg(meta, imageInfo.width, imageInfo.height);
|
|
@@ -6246,7 +7139,7 @@ var watermarkifyScreenshotBuffer = async (buffer, meta, page = null) => {
|
|
|
6246
7139
|
|
|
6247
7140
|
// src/internals/screenshot-compression.js
|
|
6248
7141
|
import { Jimp, JimpMime, ResizeStrategy } from "jimp";
|
|
6249
|
-
var
|
|
7142
|
+
var logger14 = createInternalLogger("ScreenshotCompression");
|
|
6250
7143
|
var DEFAULT_SCREENSHOT_MAX_BYTES = 5 * 1024 * 1024;
|
|
6251
7144
|
var DEFAULT_SCREENSHOT_OUTPUT_TYPE = "jpeg";
|
|
6252
7145
|
var DEFAULT_SCREENSHOT_QUALITY = 0.72;
|
|
@@ -6365,18 +7258,18 @@ var compressScreenshotBufferToBase64 = async (buffer, compression) => {
|
|
|
6365
7258
|
return buffer.toString("base64");
|
|
6366
7259
|
}
|
|
6367
7260
|
const result = await compressScreenshotBuffer(buffer, compression).catch((error) => {
|
|
6368
|
-
|
|
7261
|
+
logger14.warning(`captureScreen \u538B\u7F29\u5931\u8D25\uFF0C\u8FD4\u56DE\u539F\u56FE: ${error instanceof Error ? error.message : String(error)}`);
|
|
6369
7262
|
return null;
|
|
6370
7263
|
});
|
|
6371
7264
|
if (!result?.buffer) {
|
|
6372
7265
|
return buffer.toString("base64");
|
|
6373
7266
|
}
|
|
6374
7267
|
if (result.withinLimit) {
|
|
6375
|
-
|
|
7268
|
+
logger14.info(
|
|
6376
7269
|
`captureScreen \u5DF2\u538B\u7F29: ${originalBytes} -> ${result.bytes} bytes, format=${result.format}, quality=${result.quality}, scale=${result.scale}, size=${result.width}x${result.height}`
|
|
6377
7270
|
);
|
|
6378
7271
|
} else {
|
|
6379
|
-
|
|
7272
|
+
logger14.warning(
|
|
6380
7273
|
`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}`
|
|
6381
7274
|
);
|
|
6382
7275
|
}
|
|
@@ -6384,7 +7277,7 @@ var compressScreenshotBufferToBase64 = async (buffer, compression) => {
|
|
|
6384
7277
|
};
|
|
6385
7278
|
|
|
6386
7279
|
// src/share.js
|
|
6387
|
-
var
|
|
7280
|
+
var logger15 = createInternalLogger("Share");
|
|
6388
7281
|
var DEFAULT_TIMEOUT_MS2 = 50 * 1e3;
|
|
6389
7282
|
var DEFAULT_PAYLOAD_SNAPSHOT_MAX_LEN = 500;
|
|
6390
7283
|
var DEFAULT_POLL_INTERVAL_MS = 120;
|
|
@@ -6521,7 +7414,7 @@ var createDomShareMonitor = async (page, options = {}) => {
|
|
|
6521
7414
|
const onMatch = typeof options.onMatch === "function" ? options.onMatch : null;
|
|
6522
7415
|
const onTelemetry = typeof options.onTelemetry === "function" ? options.onTelemetry : null;
|
|
6523
7416
|
let matched = false;
|
|
6524
|
-
|
|
7417
|
+
logger15.info(`DOM \u76D1\u542C\u51C6\u5907\u6302\u8F7D: selectors=${toJsonInline(selectors, 120)}, mode=${mode}`);
|
|
6525
7418
|
const monitor = await Mutation.useMonitor(page, selectors, {
|
|
6526
7419
|
mode,
|
|
6527
7420
|
onMutation: (context = {}) => {
|
|
@@ -6539,12 +7432,12 @@ ${text}`;
|
|
|
6539
7432
|
});
|
|
6540
7433
|
}
|
|
6541
7434
|
if (mutationCount <= 5 || mutationCount % 50 === 0) {
|
|
6542
|
-
|
|
7435
|
+
logger15.info(`DOM \u53D8\u5316\u5DF2\u6355\u83B7: mutationCount=${mutationCount}, mutationNodes=${mutationNodes.length}`);
|
|
6543
7436
|
}
|
|
6544
7437
|
const [candidate] = Utils.parseLinks(rawDom, { prefix }) || [];
|
|
6545
7438
|
if (!candidate) return;
|
|
6546
7439
|
matched = true;
|
|
6547
|
-
|
|
7440
|
+
logger15.success("captureLink.domHit", `DOM \u547D\u4E2D\u5206\u4EAB\u94FE\u63A5: mutationCount=${mutationCount}, link=${candidate}`);
|
|
6548
7441
|
if (onMatch) {
|
|
6549
7442
|
onMatch({
|
|
6550
7443
|
link: candidate,
|
|
@@ -6560,7 +7453,7 @@ ${text}`;
|
|
|
6560
7453
|
return {
|
|
6561
7454
|
stop: async () => {
|
|
6562
7455
|
const result = await monitor.stop();
|
|
6563
|
-
|
|
7456
|
+
logger15.info(`DOM \u76D1\u542C\u5DF2\u505C\u6B62: totalMutations=${result?.totalMutations || 0}`);
|
|
6564
7457
|
return result;
|
|
6565
7458
|
}
|
|
6566
7459
|
};
|
|
@@ -6600,8 +7493,8 @@ var Share = {
|
|
|
6600
7493
|
if (share.mode === "response" && apiMatchers.length === 0) {
|
|
6601
7494
|
throw new Error("Share.captureLink requires share.xurl[0] api matcher when mode=response");
|
|
6602
7495
|
}
|
|
6603
|
-
|
|
6604
|
-
|
|
7496
|
+
logger15.start("captureLink", `mode=${share.mode}, timeoutMs=${timeoutMs}, prefix=${share.prefix}`);
|
|
7497
|
+
logger15.info(`captureLink \u914D\u7F6E: xurl=${toJsonInline(share.xurl)}, domMode=${domMode}, domSelectors=${toJsonInline(domSelectors, 120)}`);
|
|
6605
7498
|
const stats = {
|
|
6606
7499
|
actionTimedOut: false,
|
|
6607
7500
|
domMutationCount: 0,
|
|
@@ -6626,7 +7519,7 @@ var Share = {
|
|
|
6626
7519
|
link: validated,
|
|
6627
7520
|
payloadText: String(payloadText || "")
|
|
6628
7521
|
};
|
|
6629
|
-
|
|
7522
|
+
logger15.info(`\u5019\u9009\u94FE\u63A5\u5DF2\u786E\u8BA4: source=${source}, link=${validated}`);
|
|
6630
7523
|
return true;
|
|
6631
7524
|
};
|
|
6632
7525
|
const resolveResponseCandidate = (responseText) => {
|
|
@@ -6661,7 +7554,7 @@ var Share = {
|
|
|
6661
7554
|
try {
|
|
6662
7555
|
await monitor.stop();
|
|
6663
7556
|
} catch (error) {
|
|
6664
|
-
|
|
7557
|
+
logger15.warning(`\u505C\u6B62 DOM \u76D1\u542C\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`);
|
|
6665
7558
|
}
|
|
6666
7559
|
};
|
|
6667
7560
|
const onResponse = async (response) => {
|
|
@@ -6674,29 +7567,29 @@ var Share = {
|
|
|
6674
7567
|
stats.responseSampleUrls.push(url);
|
|
6675
7568
|
}
|
|
6676
7569
|
if (stats.responseObserved <= 5) {
|
|
6677
|
-
|
|
7570
|
+
logger15.info(`\u63A5\u53E3\u54CD\u5E94\u91C7\u6837(${stats.responseObserved}): ${url}`);
|
|
6678
7571
|
}
|
|
6679
7572
|
if (!apiMatchers.some((matcher) => url.includes(matcher))) return;
|
|
6680
7573
|
stats.responseMatched += 1;
|
|
6681
7574
|
stats.lastMatchedUrl = url;
|
|
6682
|
-
|
|
7575
|
+
logger15.info(`\u63A5\u53E3\u547D\u4E2D\u5339\u914D(${stats.responseMatched}): ${url}`);
|
|
6683
7576
|
const text = await response.text();
|
|
6684
7577
|
const hit = resolveResponseCandidate(text);
|
|
6685
7578
|
if (!hit?.link) {
|
|
6686
7579
|
if (stats.responseMatched <= 3) {
|
|
6687
|
-
|
|
7580
|
+
logger15.info(`\u63A5\u53E3\u89E3\u6790\u5B8C\u6210\u4F46\u672A\u63D0\u53D6\u5230\u5206\u4EAB\u94FE\u63A5: payloadSize=${text.length}`);
|
|
6688
7581
|
}
|
|
6689
7582
|
return;
|
|
6690
7583
|
}
|
|
6691
7584
|
stats.responseResolved += 1;
|
|
6692
|
-
|
|
7585
|
+
logger15.success("captureLink.responseHit", `\u63A5\u53E3\u547D\u4E2D\u5206\u4EAB\u94FE\u63A5: url=${url}, link=${hit.link}`);
|
|
6693
7586
|
setCandidate("response", hit.link, hit.payloadText);
|
|
6694
7587
|
} catch (error) {
|
|
6695
|
-
|
|
7588
|
+
logger15.warning(`\u63A5\u53E3\u54CD\u5E94\u5904\u7406\u5F02\u5E38: ${error instanceof Error ? error.message : String(error)}`);
|
|
6696
7589
|
}
|
|
6697
7590
|
};
|
|
6698
7591
|
if (share.mode === "dom") {
|
|
6699
|
-
|
|
7592
|
+
logger15.info("\u5F53\u524D\u4E3A DOM \u6A21\u5F0F\uFF0C\u4EC5\u542F\u7528 DOM \u76D1\u542C");
|
|
6700
7593
|
domMonitor = await createDomShareMonitor(page, {
|
|
6701
7594
|
prefix: share.prefix,
|
|
6702
7595
|
selectors: domSelectors,
|
|
@@ -6711,14 +7604,14 @@ var Share = {
|
|
|
6711
7604
|
});
|
|
6712
7605
|
}
|
|
6713
7606
|
if (share.mode === "response") {
|
|
6714
|
-
|
|
7607
|
+
logger15.info(`\u5F53\u524D\u4E3A\u63A5\u53E3\u6A21\u5F0F\uFF0C\u6302\u8F7D response \u76D1\u542C: apiMatchers=${toJsonInline(apiMatchers, 160)}`);
|
|
6715
7608
|
page.on("response", onResponse);
|
|
6716
7609
|
}
|
|
6717
7610
|
const deadline = Date.now() + timeoutMs;
|
|
6718
7611
|
const getRemainingMs = () => Math.max(0, deadline - Date.now());
|
|
6719
7612
|
try {
|
|
6720
7613
|
const actionTimeout = getRemainingMs();
|
|
6721
|
-
|
|
7614
|
+
logger15.start("captureLink.performActions", `\u6267\u884C\u52A8\u4F5C\u9884\u7B97=${actionTimeout}ms`);
|
|
6722
7615
|
if (actionTimeout > 0) {
|
|
6723
7616
|
let timer = null;
|
|
6724
7617
|
let actionError = null;
|
|
@@ -6732,21 +7625,21 @@ var Share = {
|
|
|
6732
7625
|
const actionResult = await Promise.race([actionPromise, timeoutPromise]);
|
|
6733
7626
|
if (timer) clearTimeout(timer);
|
|
6734
7627
|
if (actionResult === "__ACTION_ERROR__") {
|
|
6735
|
-
|
|
7628
|
+
logger15.fail("captureLink.performActions", actionError);
|
|
6736
7629
|
throw actionError;
|
|
6737
7630
|
}
|
|
6738
7631
|
if (actionResult === "__ACTION_TIMEOUT__") {
|
|
6739
7632
|
stats.actionTimedOut = true;
|
|
6740
|
-
|
|
7633
|
+
logger15.warning(`performActions \u5DF2\u8D85\u65F6 (${actionTimeout}ms)\uFF0C\u52A8\u4F5C\u53EF\u80FD\u4ECD\u5728\u5F02\u6B65\u6267\u884C`);
|
|
6741
7634
|
} else {
|
|
6742
|
-
|
|
7635
|
+
logger15.success("captureLink.performActions", "\u6267\u884C\u52A8\u4F5C\u5B8C\u6210");
|
|
6743
7636
|
}
|
|
6744
7637
|
}
|
|
6745
7638
|
let nextProgressLogTs = Date.now() + 3e3;
|
|
6746
7639
|
while (true) {
|
|
6747
7640
|
const selected = share.mode === "dom" ? candidates.dom : candidates.response;
|
|
6748
7641
|
if (selected?.link) {
|
|
6749
|
-
|
|
7642
|
+
logger15.success("captureLink", `\u6355\u83B7\u6210\u529F: source=${share.mode}, link=${selected.link}`);
|
|
6750
7643
|
return {
|
|
6751
7644
|
link: selected.link,
|
|
6752
7645
|
payloadText: selected.payloadText,
|
|
@@ -6758,19 +7651,19 @@ var Share = {
|
|
|
6758
7651
|
if (remaining <= 0) break;
|
|
6759
7652
|
const now = Date.now();
|
|
6760
7653
|
if (now >= nextProgressLogTs) {
|
|
6761
|
-
|
|
7654
|
+
logger15.info(
|
|
6762
7655
|
`captureLink \u7B49\u5F85\u4E2D: remaining=${remaining}ms, domMutationCount=${stats.domMutationCount}, responseMatched=${stats.responseMatched}`
|
|
6763
7656
|
);
|
|
6764
7657
|
nextProgressLogTs = now + 5e3;
|
|
6765
7658
|
}
|
|
6766
|
-
await
|
|
7659
|
+
await delay3(Math.max(0, Math.min(DEFAULT_POLL_INTERVAL_MS, remaining)));
|
|
6767
7660
|
}
|
|
6768
7661
|
if (share.mode === "response" && stats.responseMatched === 0) {
|
|
6769
|
-
|
|
7662
|
+
logger15.warning(
|
|
6770
7663
|
`\u63A5\u53E3\u76D1\u542C\u672A\u547D\u4E2D: apiMatchers=${toJsonInline(apiMatchers, 220)}, \u54CD\u5E94\u6837\u672CURLs=${toJsonInline(stats.responseSampleUrls, 420)}`
|
|
6771
7664
|
);
|
|
6772
7665
|
}
|
|
6773
|
-
|
|
7666
|
+
logger15.warning(
|
|
6774
7667
|
`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"}`
|
|
6775
7668
|
);
|
|
6776
7669
|
return {
|
|
@@ -6782,7 +7675,7 @@ var Share = {
|
|
|
6782
7675
|
} finally {
|
|
6783
7676
|
if (share.mode === "response") {
|
|
6784
7677
|
page.off("response", onResponse);
|
|
6785
|
-
|
|
7678
|
+
logger15.info("response \u76D1\u542C\u5DF2\u5378\u8F7D");
|
|
6786
7679
|
}
|
|
6787
7680
|
await stopDomMonitor();
|
|
6788
7681
|
}
|
|
@@ -6834,7 +7727,7 @@ var Share = {
|
|
|
6834
7727
|
width: originalViewport.width,
|
|
6835
7728
|
height: targetHeight
|
|
6836
7729
|
});
|
|
6837
|
-
await
|
|
7730
|
+
await delay3(1e3);
|
|
6838
7731
|
const capturedAt = /* @__PURE__ */ new Date();
|
|
6839
7732
|
const rawBuffer = await capturePageScreenshot(page, {
|
|
6840
7733
|
fullPage: true,
|
|
@@ -6875,7 +7768,7 @@ var usePlaywrightToolKit = () => {
|
|
|
6875
7768
|
return {
|
|
6876
7769
|
ApifyKit,
|
|
6877
7770
|
AntiCheat,
|
|
6878
|
-
Humanize,
|
|
7771
|
+
Humanize: Humanize2,
|
|
6879
7772
|
Launch,
|
|
6880
7773
|
LiveView,
|
|
6881
7774
|
Constants: constants_exports,
|