@skrillex1224/playwright-toolkit 2.1.245 → 2.1.246
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +477 -330
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +477 -330
- package/dist/index.js.map +4 -4
- package/index.d.ts +7 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -133,10 +133,10 @@ var createActorInfo = (info) => {
|
|
|
133
133
|
xurl
|
|
134
134
|
};
|
|
135
135
|
};
|
|
136
|
-
const buildLandingUrl = ({ protocol: protocol2, domain: domain2, path:
|
|
136
|
+
const buildLandingUrl = ({ protocol: protocol2, domain: domain2, path: path4 }) => {
|
|
137
137
|
const safeProtocol = String(protocol2).trim();
|
|
138
138
|
const safeDomain = normalizeDomain(domain2);
|
|
139
|
-
const safePath = normalizePath(
|
|
139
|
+
const safePath = normalizePath(path4);
|
|
140
140
|
return `${safeProtocol}://${safeDomain}${safePath}`;
|
|
141
141
|
};
|
|
142
142
|
const buildIcon = ({ key }) => {
|
|
@@ -144,14 +144,14 @@ var createActorInfo = (info) => {
|
|
|
144
144
|
};
|
|
145
145
|
const protocol = info.protocol || "https";
|
|
146
146
|
const domain = normalizeDomain(info.domain);
|
|
147
|
-
const
|
|
147
|
+
const path3 = normalizePath(info.path);
|
|
148
148
|
const share = normalizeShare2(info.share);
|
|
149
149
|
const device = normalizeDevice(info.device);
|
|
150
150
|
return {
|
|
151
151
|
...info,
|
|
152
152
|
protocol,
|
|
153
153
|
domain,
|
|
154
|
-
path:
|
|
154
|
+
path: path3,
|
|
155
155
|
share,
|
|
156
156
|
device,
|
|
157
157
|
get icon() {
|
|
@@ -1512,7 +1512,7 @@ var normalizeCookies = (value) => {
|
|
|
1512
1512
|
if (!name || !cookieValue || cookieValue === "<nil>") return null;
|
|
1513
1513
|
const domain = String(raw.domain || "").trim();
|
|
1514
1514
|
const url = normalizeHttpUrl(raw.url);
|
|
1515
|
-
const
|
|
1515
|
+
const path3 = String(raw.path || "").trim() || "/";
|
|
1516
1516
|
const sameSite = normalizeCookieSameSite(raw.sameSite);
|
|
1517
1517
|
const expires = normalizeCookieExpires(raw);
|
|
1518
1518
|
const secure = Boolean(raw.secure);
|
|
@@ -1521,7 +1521,7 @@ var normalizeCookies = (value) => {
|
|
|
1521
1521
|
const normalized = {
|
|
1522
1522
|
name,
|
|
1523
1523
|
value: cookieValue,
|
|
1524
|
-
path:
|
|
1524
|
+
path: path3,
|
|
1525
1525
|
...domain ? { domain } : {},
|
|
1526
1526
|
...!domain && url ? { url } : {},
|
|
1527
1527
|
...sameSite ? { sameSite } : {},
|
|
@@ -2592,6 +2592,10 @@ var assertPoint = (point) => {
|
|
|
2592
2592
|
throw new Error(`Invalid input point: ${JSON.stringify(point)}`);
|
|
2593
2593
|
}
|
|
2594
2594
|
};
|
|
2595
|
+
var toFiniteNumber = (value, fallback = 0) => {
|
|
2596
|
+
const number = Number(value);
|
|
2597
|
+
return Number.isFinite(number) ? number : fallback;
|
|
2598
|
+
};
|
|
2595
2599
|
var dispatchMouseMove = (page, point, options = {}) => page.mouse.move(point.x, point.y, options);
|
|
2596
2600
|
var dispatchMouseStart = (page, options = {}) => page.mouse.down(options);
|
|
2597
2601
|
var dispatchMouseEnd = (page, options = {}) => page.mouse.up(options);
|
|
@@ -2611,6 +2615,11 @@ var dragWithMouse = async (page, points, options = {}) => {
|
|
|
2611
2615
|
}, { steps: 2 });
|
|
2612
2616
|
await waitFor(page, options.stepDelayMs ?? 90);
|
|
2613
2617
|
}
|
|
2618
|
+
const finalMoveRepeats = Math.max(0, Math.floor(toFiniteNumber(options.finalMoveRepeats)));
|
|
2619
|
+
for (let repeat = 0; repeat < finalMoveRepeats; repeat += 1) {
|
|
2620
|
+
await dispatchMouseMove(page, points.end, { steps: 1 });
|
|
2621
|
+
await waitFor(page, options.finalMoveDelayMs ?? 35);
|
|
2622
|
+
}
|
|
2614
2623
|
await waitFor(page, options.beforeReleaseDelayMs ?? 100);
|
|
2615
2624
|
await dispatchMouseEnd(page);
|
|
2616
2625
|
await waitFor(page, options.afterReleaseDelayMs ?? 100);
|
|
@@ -2620,6 +2629,7 @@ var dragWithTouch = async (page, points, options = {}) => {
|
|
|
2620
2629
|
let client = null;
|
|
2621
2630
|
try {
|
|
2622
2631
|
client = await page.context().newCDPSession(page);
|
|
2632
|
+
await waitFor(page, options.initialDelayMs ?? 250);
|
|
2623
2633
|
await client.send("Input.dispatchTouchEvent", {
|
|
2624
2634
|
type: "touchStart",
|
|
2625
2635
|
touchPoints: [{ x: points.start.x, y: points.start.y, id: 1 }]
|
|
@@ -2641,6 +2651,14 @@ var dragWithTouch = async (page, points, options = {}) => {
|
|
|
2641
2651
|
});
|
|
2642
2652
|
await waitFor(page, options.stepDelayMs ?? 90);
|
|
2643
2653
|
}
|
|
2654
|
+
const finalMoveRepeats = Math.max(0, Math.floor(toFiniteNumber(options.finalMoveRepeats)));
|
|
2655
|
+
for (let repeat = 0; repeat < finalMoveRepeats; repeat += 1) {
|
|
2656
|
+
await client.send("Input.dispatchTouchEvent", {
|
|
2657
|
+
type: "touchMove",
|
|
2658
|
+
touchPoints: [{ x: points.end.x, y: points.end.y, id: 1 }]
|
|
2659
|
+
});
|
|
2660
|
+
await waitFor(page, options.finalMoveDelayMs ?? 35);
|
|
2661
|
+
}
|
|
2644
2662
|
await waitFor(page, options.beforeReleaseDelayMs ?? 100);
|
|
2645
2663
|
await client.send("Input.dispatchTouchEvent", {
|
|
2646
2664
|
type: "touchEnd",
|
|
@@ -2658,7 +2676,7 @@ var dragWithTouch = async (page, points, options = {}) => {
|
|
|
2658
2676
|
var clickTargetWithDevice = async (page, target, options = {}) => {
|
|
2659
2677
|
const normalizedOptions = normalizeSelectorOptions(options);
|
|
2660
2678
|
const resolvedDevice = resolveDeviceFromPage(page);
|
|
2661
|
-
if (target && resolvedDevice === Device.Mobile && !normalizedOptions.forceClick) {
|
|
2679
|
+
if (target && resolvedDevice === Device.Mobile && !normalizedOptions.forceClick && !normalizedOptions.forceMouse) {
|
|
2662
2680
|
if (typeof target.tap === "function") {
|
|
2663
2681
|
await target.tap(normalizedOptions.tapOptions);
|
|
2664
2682
|
return true;
|
|
@@ -2858,20 +2876,26 @@ var DeviceInput = {
|
|
|
2858
2876
|
throw new Error("Unable to resolve drag coordinates.");
|
|
2859
2877
|
}
|
|
2860
2878
|
const steps = options.steps || 10;
|
|
2879
|
+
const sourceOffsetX = toFiniteNumber(options.sourceOffsetX);
|
|
2880
|
+
const sourceOffsetY = toFiniteNumber(options.sourceOffsetY);
|
|
2881
|
+
const targetOffsetX = toFiniteNumber(options.targetOffsetX);
|
|
2882
|
+
const targetOffsetY = toFiniteNumber(options.targetOffsetY);
|
|
2883
|
+
const sourceCenterX = sourceBox.x + sourceBox.width / 2 + sourceOffsetX;
|
|
2884
|
+
const sourceCenterY = sourceBox.y + sourceBox.height / 2 + sourceOffsetY;
|
|
2861
2885
|
const liftOffsetX = Math.min(18, Math.max(8, sourceBox.width * 0.12));
|
|
2862
2886
|
const liftOffsetY = Math.min(12, Math.max(4, sourceBox.height * 0.08));
|
|
2863
2887
|
const points = {
|
|
2864
2888
|
start: {
|
|
2865
|
-
x:
|
|
2866
|
-
y:
|
|
2889
|
+
x: sourceCenterX,
|
|
2890
|
+
y: sourceCenterY
|
|
2867
2891
|
},
|
|
2868
2892
|
lift: {
|
|
2869
|
-
x:
|
|
2870
|
-
y:
|
|
2893
|
+
x: sourceCenterX + liftOffsetX,
|
|
2894
|
+
y: sourceCenterY + liftOffsetY
|
|
2871
2895
|
},
|
|
2872
2896
|
end: {
|
|
2873
|
-
x: targetBox.x + targetBox.width / 2,
|
|
2874
|
-
y: targetBox.y + targetBox.height / 2
|
|
2897
|
+
x: targetBox.x + targetBox.width / 2 + targetOffsetX,
|
|
2898
|
+
y: targetBox.y + targetBox.height / 2 + targetOffsetY
|
|
2875
2899
|
},
|
|
2876
2900
|
steps
|
|
2877
2901
|
};
|
|
@@ -3439,11 +3463,6 @@ var DEFAULT_ACTIVATE_FALLBACK_TIMEOUT_MS = 900;
|
|
|
3439
3463
|
var INTERACTIVE_SELECTOR = [
|
|
3440
3464
|
"button",
|
|
3441
3465
|
'[role="button"]',
|
|
3442
|
-
'[role="link"]',
|
|
3443
|
-
'[role="menuitem"]',
|
|
3444
|
-
'[role="tab"]',
|
|
3445
|
-
'[role="switch"]',
|
|
3446
|
-
'[role="checkbox"]',
|
|
3447
3466
|
"a[href]",
|
|
3448
3467
|
"label",
|
|
3449
3468
|
"input",
|
|
@@ -3458,34 +3477,6 @@ var EDITABLE_SELECTOR = [
|
|
|
3458
3477
|
"textarea",
|
|
3459
3478
|
'[contenteditable="true"]'
|
|
3460
3479
|
].join(",");
|
|
3461
|
-
var INTERACTIVE_HINT_PATTERN = [
|
|
3462
|
-
"btn",
|
|
3463
|
-
"button",
|
|
3464
|
-
"send",
|
|
3465
|
-
"submit",
|
|
3466
|
-
"confirm",
|
|
3467
|
-
"cancel",
|
|
3468
|
-
"retry",
|
|
3469
|
-
"reload",
|
|
3470
|
-
"search",
|
|
3471
|
-
"copy",
|
|
3472
|
-
"share",
|
|
3473
|
-
"close",
|
|
3474
|
-
"more",
|
|
3475
|
-
"\u53D1\u9001",
|
|
3476
|
-
"\u63D0\u4EA4",
|
|
3477
|
-
"\u786E\u5B9A",
|
|
3478
|
-
"\u786E\u8BA4",
|
|
3479
|
-
"\u53D6\u6D88",
|
|
3480
|
-
"\u91CD\u8BD5",
|
|
3481
|
-
"\u641C\u7D22",
|
|
3482
|
-
"\u590D\u5236",
|
|
3483
|
-
"\u5206\u4EAB",
|
|
3484
|
-
"\u5173\u95ED",
|
|
3485
|
-
"\u66F4\u591A",
|
|
3486
|
-
"\u5C55\u5F00",
|
|
3487
|
-
"\u6536\u8D77"
|
|
3488
|
-
].join("|");
|
|
3489
3480
|
var clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
3490
3481
|
var resolveViewport = (page) => page?.viewportSize?.() || { width: 390, height: 844 };
|
|
3491
3482
|
var describeTarget = (target) => {
|
|
@@ -3525,47 +3516,7 @@ var withTimeout = async (operation, timeoutMs, label) => {
|
|
|
3525
3516
|
}
|
|
3526
3517
|
};
|
|
3527
3518
|
var checkElementVisibility = async (element) => {
|
|
3528
|
-
return element.evaluate((el,
|
|
3529
|
-
const interactiveHintRe = new RegExp(interactiveHintPattern, "i");
|
|
3530
|
-
const nodeClassName = (node) => {
|
|
3531
|
-
if (!node) return "";
|
|
3532
|
-
if (typeof node.className === "string") return node.className;
|
|
3533
|
-
if (node.className && typeof node.className.baseVal === "string") return node.className.baseVal;
|
|
3534
|
-
return "";
|
|
3535
|
-
};
|
|
3536
|
-
const interactiveScore = (node) => {
|
|
3537
|
-
if (!node || node.nodeType !== Node.ELEMENT_NODE) return 0;
|
|
3538
|
-
let score = 0;
|
|
3539
|
-
if (typeof node.matches === "function" && node.matches(interactiveSelector)) score += 8;
|
|
3540
|
-
const hints = [
|
|
3541
|
-
node.id || "",
|
|
3542
|
-
nodeClassName(node),
|
|
3543
|
-
node.getAttribute?.("aria-label") || "",
|
|
3544
|
-
node.getAttribute?.("title") || "",
|
|
3545
|
-
node.getAttribute?.("data-testid") || "",
|
|
3546
|
-
node.getAttribute?.("data-test") || "",
|
|
3547
|
-
node.getAttribute?.("data-click") || "",
|
|
3548
|
-
node.getAttribute?.("data-action") || "",
|
|
3549
|
-
node.getAttribute?.("onclick") || "",
|
|
3550
|
-
String(node.textContent || "").trim().slice(0, 24)
|
|
3551
|
-
].join(" ");
|
|
3552
|
-
if (interactiveHintRe.test(hints)) score += 4;
|
|
3553
|
-
const style = window.getComputedStyle(node);
|
|
3554
|
-
if (style?.cursor === "pointer") score += 2;
|
|
3555
|
-
return score;
|
|
3556
|
-
};
|
|
3557
|
-
const closestInteractive = (node) => {
|
|
3558
|
-
let best = null;
|
|
3559
|
-
let bestScore = 0;
|
|
3560
|
-
for (let current = node; current && current !== document.body; current = current.parentElement) {
|
|
3561
|
-
const score = interactiveScore(current);
|
|
3562
|
-
if (score > bestScore || score > 0 && score === bestScore) {
|
|
3563
|
-
best = current;
|
|
3564
|
-
bestScore = score;
|
|
3565
|
-
}
|
|
3566
|
-
}
|
|
3567
|
-
return best;
|
|
3568
|
-
};
|
|
3519
|
+
return element.evaluate((el, interactiveSelector) => {
|
|
3569
3520
|
const targetStyle = window.getComputedStyle(el);
|
|
3570
3521
|
if (!targetStyle || targetStyle.display === "none" || targetStyle.visibility === "hidden" || targetStyle.visibility === "collapse") {
|
|
3571
3522
|
return { code: "NOT_INTERACTABLE", reason: "\u5143\u7D20\u4E0D\u53EF\u89C1", direction: "down" };
|
|
@@ -3627,13 +3578,16 @@ var checkElementVisibility = async (element) => {
|
|
|
3627
3578
|
positioning
|
|
3628
3579
|
};
|
|
3629
3580
|
}
|
|
3630
|
-
const targetInteractive =
|
|
3581
|
+
const targetInteractive = typeof el.closest === "function" ? el.closest(interactiveSelector) : null;
|
|
3631
3582
|
const sameInteractiveTarget = (pointElement) => {
|
|
3632
3583
|
if (!pointElement) return false;
|
|
3633
3584
|
if (pointElement === el || el.contains(pointElement) || pointElement.contains(el)) {
|
|
3634
3585
|
return true;
|
|
3635
3586
|
}
|
|
3636
|
-
|
|
3587
|
+
if (!targetInteractive || typeof pointElement.closest !== "function") {
|
|
3588
|
+
return false;
|
|
3589
|
+
}
|
|
3590
|
+
return pointElement.closest(interactiveSelector) === targetInteractive;
|
|
3637
3591
|
};
|
|
3638
3592
|
const describeElement = (node) => {
|
|
3639
3593
|
if (!node) return null;
|
|
@@ -3680,53 +3634,10 @@ var checkElementVisibility = async (element) => {
|
|
|
3680
3634
|
};
|
|
3681
3635
|
}
|
|
3682
3636
|
return { code: "VISIBLE", isFixed, positioning };
|
|
3683
|
-
},
|
|
3684
|
-
interactiveSelector: INTERACTIVE_SELECTOR,
|
|
3685
|
-
interactiveHintPattern: INTERACTIVE_HINT_PATTERN
|
|
3686
|
-
});
|
|
3637
|
+
}, INTERACTIVE_SELECTOR);
|
|
3687
3638
|
};
|
|
3688
3639
|
var resolveSafeTapPoint = async (element) => {
|
|
3689
|
-
return element.evaluate((el,
|
|
3690
|
-
const interactiveHintRe = new RegExp(interactiveHintPattern, "i");
|
|
3691
|
-
const nodeClassName = (node) => {
|
|
3692
|
-
if (!node) return "";
|
|
3693
|
-
if (typeof node.className === "string") return node.className;
|
|
3694
|
-
if (node.className && typeof node.className.baseVal === "string") return node.className.baseVal;
|
|
3695
|
-
return "";
|
|
3696
|
-
};
|
|
3697
|
-
const interactiveScore = (node) => {
|
|
3698
|
-
if (!node || node.nodeType !== Node.ELEMENT_NODE) return 0;
|
|
3699
|
-
let score = 0;
|
|
3700
|
-
if (typeof node.matches === "function" && node.matches(interactiveSelector)) score += 8;
|
|
3701
|
-
const hints = [
|
|
3702
|
-
node.id || "",
|
|
3703
|
-
nodeClassName(node),
|
|
3704
|
-
node.getAttribute?.("aria-label") || "",
|
|
3705
|
-
node.getAttribute?.("title") || "",
|
|
3706
|
-
node.getAttribute?.("data-testid") || "",
|
|
3707
|
-
node.getAttribute?.("data-test") || "",
|
|
3708
|
-
node.getAttribute?.("data-click") || "",
|
|
3709
|
-
node.getAttribute?.("data-action") || "",
|
|
3710
|
-
node.getAttribute?.("onclick") || "",
|
|
3711
|
-
String(node.textContent || "").trim().slice(0, 24)
|
|
3712
|
-
].join(" ");
|
|
3713
|
-
if (interactiveHintRe.test(hints)) score += 4;
|
|
3714
|
-
const style = window.getComputedStyle(node);
|
|
3715
|
-
if (style?.cursor === "pointer") score += 2;
|
|
3716
|
-
return score;
|
|
3717
|
-
};
|
|
3718
|
-
const closestInteractive = (node) => {
|
|
3719
|
-
let best = null;
|
|
3720
|
-
let bestScore = 0;
|
|
3721
|
-
for (let current = node; current && current !== document.body; current = current.parentElement) {
|
|
3722
|
-
const score = interactiveScore(current);
|
|
3723
|
-
if (score > bestScore || score > 0 && score === bestScore) {
|
|
3724
|
-
best = current;
|
|
3725
|
-
bestScore = score;
|
|
3726
|
-
}
|
|
3727
|
-
}
|
|
3728
|
-
return best;
|
|
3729
|
-
};
|
|
3640
|
+
return element.evaluate((el, interactiveSelector) => {
|
|
3730
3641
|
const rect = el.getBoundingClientRect();
|
|
3731
3642
|
if (!rect || rect.width <= 0 || rect.height <= 0) {
|
|
3732
3643
|
return null;
|
|
@@ -3763,13 +3674,16 @@ var resolveSafeTapPoint = async (element) => {
|
|
|
3763
3674
|
if (visibleWidth <= 1 || visibleHeight <= 1) {
|
|
3764
3675
|
return null;
|
|
3765
3676
|
}
|
|
3766
|
-
const targetInteractive =
|
|
3677
|
+
const targetInteractive = typeof el.closest === "function" ? el.closest(interactiveSelector) : null;
|
|
3767
3678
|
const sameInteractiveTarget = (pointElement) => {
|
|
3768
3679
|
if (!pointElement) return false;
|
|
3769
3680
|
if (pointElement === el || el.contains(pointElement) || pointElement.contains(el)) {
|
|
3770
3681
|
return true;
|
|
3771
3682
|
}
|
|
3772
|
-
|
|
3683
|
+
if (!targetInteractive || typeof pointElement.closest !== "function") {
|
|
3684
|
+
return false;
|
|
3685
|
+
}
|
|
3686
|
+
return pointElement.closest(interactiveSelector) === targetInteractive;
|
|
3773
3687
|
};
|
|
3774
3688
|
const cx = visibleLeft + visibleWidth / 2;
|
|
3775
3689
|
const cy = visibleTop + visibleHeight / 2;
|
|
@@ -3797,53 +3711,14 @@ var resolveSafeTapPoint = async (element) => {
|
|
|
3797
3711
|
x: chosen.x,
|
|
3798
3712
|
y: chosen.y
|
|
3799
3713
|
};
|
|
3800
|
-
},
|
|
3801
|
-
interactiveSelector: INTERACTIVE_SELECTOR,
|
|
3802
|
-
interactiveHintPattern: INTERACTIVE_HINT_PATTERN
|
|
3803
|
-
});
|
|
3714
|
+
}, INTERACTIVE_SELECTOR);
|
|
3804
3715
|
};
|
|
3805
3716
|
var activateElementFallback = async (element, point = null, options = {}) => {
|
|
3806
|
-
return element.evaluate((el, { innerPoint, innerOptions, interactiveSelector, editableSelector
|
|
3807
|
-
const interactiveHintRe = new RegExp(interactiveHintPattern, "i");
|
|
3808
|
-
const nodeClassName = (node) => {
|
|
3809
|
-
if (!node) return "";
|
|
3810
|
-
if (typeof node.className === "string") return node.className;
|
|
3811
|
-
if (node.className && typeof node.className.baseVal === "string") return node.className.baseVal;
|
|
3812
|
-
return "";
|
|
3813
|
-
};
|
|
3814
|
-
const interactiveScore = (node) => {
|
|
3815
|
-
if (!node || node.nodeType !== Node.ELEMENT_NODE) return 0;
|
|
3816
|
-
let score = 0;
|
|
3817
|
-
if (typeof node.matches === "function" && node.matches(interactiveSelector)) score += 8;
|
|
3818
|
-
const hints = [
|
|
3819
|
-
node.id || "",
|
|
3820
|
-
nodeClassName(node),
|
|
3821
|
-
node.getAttribute?.("aria-label") || "",
|
|
3822
|
-
node.getAttribute?.("title") || "",
|
|
3823
|
-
node.getAttribute?.("data-testid") || "",
|
|
3824
|
-
node.getAttribute?.("data-test") || "",
|
|
3825
|
-
node.getAttribute?.("data-click") || "",
|
|
3826
|
-
node.getAttribute?.("data-action") || "",
|
|
3827
|
-
node.getAttribute?.("onclick") || "",
|
|
3828
|
-
String(node.textContent || "").trim().slice(0, 24)
|
|
3829
|
-
].join(" ");
|
|
3830
|
-
if (interactiveHintRe.test(hints)) score += 4;
|
|
3831
|
-
const style = window.getComputedStyle(node);
|
|
3832
|
-
if (style?.cursor === "pointer") score += 2;
|
|
3833
|
-
return score;
|
|
3834
|
-
};
|
|
3717
|
+
return element.evaluate((el, { innerPoint, innerOptions, interactiveSelector, editableSelector }) => {
|
|
3835
3718
|
const pointElement = innerPoint ? document.elementFromPoint(innerPoint.x, innerPoint.y) : null;
|
|
3836
3719
|
const nearestInteractive = (node) => {
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
for (let current = node; current && current !== document.body; current = current.parentElement) {
|
|
3840
|
-
const score = interactiveScore(current);
|
|
3841
|
-
if (score > bestScore || score > 0 && score === bestScore) {
|
|
3842
|
-
best = current;
|
|
3843
|
-
bestScore = score;
|
|
3844
|
-
}
|
|
3845
|
-
}
|
|
3846
|
-
return best;
|
|
3720
|
+
if (!node || typeof node.closest !== "function") return null;
|
|
3721
|
+
return node.closest(interactiveSelector);
|
|
3847
3722
|
};
|
|
3848
3723
|
const targetInteractive = nearestInteractive(el);
|
|
3849
3724
|
const pointInteractive = nearestInteractive(pointElement);
|
|
@@ -3875,8 +3750,7 @@ var activateElementFallback = async (element, point = null, options = {}) => {
|
|
|
3875
3750
|
innerPoint: point,
|
|
3876
3751
|
innerOptions: options || {},
|
|
3877
3752
|
interactiveSelector: INTERACTIVE_SELECTOR,
|
|
3878
|
-
editableSelector: EDITABLE_SELECTOR
|
|
3879
|
-
interactiveHintPattern: INTERACTIVE_HINT_PATTERN
|
|
3753
|
+
editableSelector: EDITABLE_SELECTOR
|
|
3880
3754
|
});
|
|
3881
3755
|
};
|
|
3882
3756
|
var getScrollableRect = async (element) => {
|
|
@@ -3980,47 +3854,6 @@ var scrollAwayFromObstruction = async (element, status) => {
|
|
|
3980
3854
|
};
|
|
3981
3855
|
}, status);
|
|
3982
3856
|
};
|
|
3983
|
-
var getElementViewportSnapshot = async (element) => {
|
|
3984
|
-
return element.evaluate((el) => {
|
|
3985
|
-
const rect = el.getBoundingClientRect();
|
|
3986
|
-
return {
|
|
3987
|
-
top: rect.top,
|
|
3988
|
-
bottom: rect.bottom,
|
|
3989
|
-
left: rect.left,
|
|
3990
|
-
right: rect.right,
|
|
3991
|
-
width: rect.width,
|
|
3992
|
-
height: rect.height,
|
|
3993
|
-
scrollX: window.scrollX,
|
|
3994
|
-
scrollY: window.scrollY
|
|
3995
|
-
};
|
|
3996
|
-
});
|
|
3997
|
-
};
|
|
3998
|
-
var isTargetImmobileAfterScroll = (before, after) => {
|
|
3999
|
-
if (!before || !after) return false;
|
|
4000
|
-
const rectDeltaY = Number(after.top || 0) - Number(before.top || 0);
|
|
4001
|
-
const rectDeltaX = Number(after.left || 0) - Number(before.left || 0);
|
|
4002
|
-
const scrollDeltaY = Number(after.scrollY || 0) - Number(before.scrollY || 0);
|
|
4003
|
-
const scrollDeltaX = Number(after.scrollX || 0) - Number(before.scrollX || 0);
|
|
4004
|
-
const rectMoved = Math.abs(rectDeltaY) > 3 || Math.abs(rectDeltaX) > 3;
|
|
4005
|
-
const pageMoved = Math.abs(scrollDeltaY) > 3 || Math.abs(scrollDeltaX) > 3;
|
|
4006
|
-
if (!rectMoved && !pageMoved) return true;
|
|
4007
|
-
if (pageMoved && !rectMoved) return true;
|
|
4008
|
-
if (Math.abs(scrollDeltaY) > 12 && Math.abs(rectDeltaY) < Math.min(12, Math.abs(scrollDeltaY) * 0.2)) {
|
|
4009
|
-
return true;
|
|
4010
|
-
}
|
|
4011
|
-
return false;
|
|
4012
|
-
};
|
|
4013
|
-
var restoreWindowFromSnapshot = async (page, before, after) => {
|
|
4014
|
-
if (!before || !after) return;
|
|
4015
|
-
if (Math.abs(Number(after.scrollX || 0) - Number(before.scrollX || 0)) <= 2 && Math.abs(Number(after.scrollY || 0) - Number(before.scrollY || 0)) <= 2) {
|
|
4016
|
-
return;
|
|
4017
|
-
}
|
|
4018
|
-
await page.evaluate(
|
|
4019
|
-
(state) => window.scrollTo(state.x, state.y),
|
|
4020
|
-
{ x: Number(before.scrollX || 0), y: Number(before.scrollY || 0) }
|
|
4021
|
-
).catch(() => {
|
|
4022
|
-
});
|
|
4023
|
-
};
|
|
4024
3857
|
var dispatchTouchSwipe = async (page, deltaY, options = {}) => {
|
|
4025
3858
|
const viewport = resolveViewport(page);
|
|
4026
3859
|
const rawRect = options.rect || null;
|
|
@@ -4201,11 +4034,11 @@ var MobileHumanize = {
|
|
|
4201
4034
|
const scrollRect = await getScrollableRect(element);
|
|
4202
4035
|
if (!scrollRect && status.isFixed && status.code === "OUT_OF_VIEWPORT") {
|
|
4203
4036
|
logger7.warn(`humanScroll | fixed/sticky \u76EE\u6807\u4E0D\u5728\u89C6\u53E3\u5185\uFF0C\u9875\u9762\u6EDA\u52A8\u65E0\u6CD5\u6539\u53D8\u5176\u4F4D\u7F6E (direction=${status.direction || "unknown"})`);
|
|
4204
|
-
return { element, didScroll, restore: null
|
|
4037
|
+
return { element, didScroll, restore: null };
|
|
4205
4038
|
}
|
|
4206
4039
|
if (!scrollRect && status.isFixed && status.code === "OBSTRUCTED") {
|
|
4207
4040
|
logger7.warn(`humanScroll | fixed/sticky \u76EE\u6807\u88AB\u906E\u6321\uFF0C\u6EDA\u52A8\u65E0\u6CD5\u89E3\u9664 (${status.obstruction?.tag || "unknown"})`);
|
|
4208
|
-
return { element, didScroll, restore: null
|
|
4041
|
+
return { element, didScroll, restore: null };
|
|
4209
4042
|
}
|
|
4210
4043
|
if (scrollRect && status.code === "OBSTRUCTED" && status.obstruction?.isFixed) {
|
|
4211
4044
|
const moved = await scrollAwayFromObstruction(element, status);
|
|
@@ -4236,7 +4069,6 @@ var MobileHumanize = {
|
|
|
4236
4069
|
}
|
|
4237
4070
|
}
|
|
4238
4071
|
const beforeWindowState = scrollRect ? await page.evaluate(() => ({ x: window.scrollX, y: window.scrollY })) : null;
|
|
4239
|
-
const beforeElementSnapshot = await getElementViewportSnapshot(element).catch(() => null);
|
|
4240
4072
|
const beforeState = scrollRect ? await element.evaluate((el) => {
|
|
4241
4073
|
const isScrollable = (node) => {
|
|
4242
4074
|
const style = window.getComputedStyle(node);
|
|
@@ -4266,21 +4098,6 @@ var MobileHumanize = {
|
|
|
4266
4098
|
logger7.debug(`humanScroll | \u7A97\u53E3\u6EDA\u52A8\u56DE\u6536 from=${Math.round(afterWindowState.y)} to=${Math.round(beforeWindowState.y)}`);
|
|
4267
4099
|
}
|
|
4268
4100
|
}
|
|
4269
|
-
let afterElementSnapshot = null;
|
|
4270
|
-
const readAfterElementSnapshot = async () => {
|
|
4271
|
-
if (!afterElementSnapshot) {
|
|
4272
|
-
afterElementSnapshot = await getElementViewportSnapshot(element).catch(() => null);
|
|
4273
|
-
}
|
|
4274
|
-
return afterElementSnapshot;
|
|
4275
|
-
};
|
|
4276
|
-
if (!scrollRect && beforeElementSnapshot) {
|
|
4277
|
-
const afterSnapshot = await readAfterElementSnapshot();
|
|
4278
|
-
if (isTargetImmobileAfterScroll(beforeElementSnapshot, afterSnapshot)) {
|
|
4279
|
-
await restoreWindowFromSnapshot(page, beforeElementSnapshot, afterSnapshot);
|
|
4280
|
-
logger7.warn(`humanScroll | \u76EE\u6807\u4E0D\u968F\u9875\u9762\u6EDA\u52A8\u79FB\u52A8\uFF0C\u9875\u9762\u6EDA\u52A8\u65E0\u6CD5\u6539\u53D8\u5176\u4F4D\u7F6E (status=${status.code}, direction=${status.direction || "unknown"})`);
|
|
4281
|
-
return { element, didScroll, restore: null, unscrollable: true };
|
|
4282
|
-
}
|
|
4283
|
-
}
|
|
4284
4101
|
if (scrollRect && beforeState) {
|
|
4285
4102
|
const afterState = await element.evaluate((el) => {
|
|
4286
4103
|
const isScrollable = (node) => {
|
|
@@ -4318,14 +4135,6 @@ var MobileHumanize = {
|
|
|
4318
4135
|
}
|
|
4319
4136
|
}
|
|
4320
4137
|
}
|
|
4321
|
-
if (scrollRect && beforeElementSnapshot) {
|
|
4322
|
-
const afterSnapshot = await getElementViewportSnapshot(element).catch(() => null);
|
|
4323
|
-
if (isTargetImmobileAfterScroll(beforeElementSnapshot, afterSnapshot)) {
|
|
4324
|
-
await restoreWindowFromSnapshot(page, beforeElementSnapshot, afterSnapshot);
|
|
4325
|
-
logger7.warn(`humanScroll | \u76EE\u6807\u4E0D\u968F\u6EDA\u52A8\u5BB9\u5668\u79FB\u52A8\uFF0C\u6EDA\u52A8\u65E0\u6CD5\u6539\u53D8\u5176\u4F4D\u7F6E (status=${status.code}, direction=${status.direction || "unknown"})`);
|
|
4326
|
-
return { element, didScroll, restore: null, unscrollable: true };
|
|
4327
|
-
}
|
|
4328
|
-
}
|
|
4329
4138
|
didScroll = true;
|
|
4330
4139
|
}
|
|
4331
4140
|
try {
|
|
@@ -4375,28 +4184,21 @@ var MobileHumanize = {
|
|
|
4375
4184
|
logger7.warn(`humanClick: \u5143\u7D20\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7\u70B9\u51FB ${targetDesc}`);
|
|
4376
4185
|
return false;
|
|
4377
4186
|
}
|
|
4378
|
-
|
|
4187
|
+
if (scrollIfNeeded) {
|
|
4188
|
+
await MobileHumanize.humanScroll(page, element, { throwOnMissing });
|
|
4189
|
+
}
|
|
4379
4190
|
const status = await checkElementVisibility(element).catch(() => null);
|
|
4380
4191
|
if (status && status.code !== "VISIBLE") {
|
|
4381
|
-
if (fallbackDomClick &&
|
|
4382
|
-
|
|
4192
|
+
if (fallbackDomClick && status.isFixed && status.code === "OUT_OF_VIEWPORT") {
|
|
4193
|
+
const fallback = await withTimeout(
|
|
4383
4194
|
() => activateElementFallback(element, null, {
|
|
4384
4195
|
editableOnly: true
|
|
4385
4196
|
}),
|
|
4386
4197
|
activateFallbackTimeoutMs,
|
|
4387
4198
|
"focus fallback"
|
|
4388
4199
|
).catch(() => null);
|
|
4389
|
-
if (!fallback?.activated) {
|
|
4390
|
-
fallback = await withTimeout(
|
|
4391
|
-
() => activateElementFallback(element, null, {
|
|
4392
|
-
editableOnly: false
|
|
4393
|
-
}),
|
|
4394
|
-
activateFallbackTimeoutMs,
|
|
4395
|
-
"activation fallback"
|
|
4396
|
-
).catch(() => null);
|
|
4397
|
-
}
|
|
4398
4200
|
if (fallback?.activated) {
|
|
4399
|
-
logger7.warn(`humanClick: \
|
|
4201
|
+
logger7.warn(`humanClick: fixed/sticky \u76EE\u6807\u4E0D\u5728\u89C6\u53E3\u5185\uFF0C\u5DF2\u7528 ${fallback.method} \u6FC0\u6D3B`);
|
|
4400
4202
|
return true;
|
|
4401
4203
|
}
|
|
4402
4204
|
}
|
|
@@ -4513,20 +4315,6 @@ var MobileHumanize = {
|
|
|
4513
4315
|
const locator = page.locator(selector);
|
|
4514
4316
|
await MobileHumanize.humanClick(page, locator, { scrollIfNeeded: true });
|
|
4515
4317
|
await waitJitter(160, 0.4);
|
|
4516
|
-
const readValue = async () => {
|
|
4517
|
-
try {
|
|
4518
|
-
return await locator.inputValue({ timeout: 600 });
|
|
4519
|
-
} catch {
|
|
4520
|
-
return await locator.evaluate((el) => "value" in el ? String(el.value || "") : String(el.textContent || "")).catch(() => "");
|
|
4521
|
-
}
|
|
4522
|
-
};
|
|
4523
|
-
const currentValue = await readValue();
|
|
4524
|
-
if (!currentValue) return;
|
|
4525
|
-
await page.keyboard.press("ControlOrMeta+A");
|
|
4526
|
-
await waitJitter(90, 0.35);
|
|
4527
|
-
await page.keyboard.press("Backspace");
|
|
4528
|
-
await waitJitter(120, 0.35);
|
|
4529
|
-
if (!await readValue()) return;
|
|
4530
4318
|
await locator.evaluate((el) => {
|
|
4531
4319
|
if ("value" in el) {
|
|
4532
4320
|
el.value = "";
|
|
@@ -5116,6 +4904,10 @@ var LiveView = {
|
|
|
5116
4904
|
// src/chaptcha.js
|
|
5117
4905
|
var import_uuid = require("uuid");
|
|
5118
4906
|
|
|
4907
|
+
// src/internals/captcha/bytedance.js
|
|
4908
|
+
var import_promises = require("fs/promises");
|
|
4909
|
+
var import_path2 = __toESM(require("path"), 1);
|
|
4910
|
+
|
|
5119
4911
|
// src/internals/captcha/shared.js
|
|
5120
4912
|
var waitForVisible = async (locator, timeout) => {
|
|
5121
4913
|
try {
|
|
@@ -5133,38 +4925,71 @@ var isAnyCaptchaTextVisible = async (frame, texts, timeout) => {
|
|
|
5133
4925
|
if (!text) {
|
|
5134
4926
|
continue;
|
|
5135
4927
|
}
|
|
5136
|
-
const
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
4928
|
+
const textLocator = frame.getByText(text, { exact: false });
|
|
4929
|
+
const locatorText = frame.locator(`text=${text}`);
|
|
4930
|
+
const candidateGroups = [textLocator, locatorText];
|
|
4931
|
+
for (const candidateGroup of candidateGroups) {
|
|
4932
|
+
const candidateCount = await candidateGroup.count().catch(() => 0);
|
|
4933
|
+
for (let index = 0; index < candidateCount; index += 1) {
|
|
4934
|
+
const candidate = candidateGroup.nth(index);
|
|
4935
|
+
const isVisible = await candidate.isVisible({ timeout }).catch(() => false);
|
|
4936
|
+
if (isVisible) {
|
|
4937
|
+
return true;
|
|
4938
|
+
}
|
|
5144
4939
|
}
|
|
5145
4940
|
}
|
|
5146
4941
|
}
|
|
5147
4942
|
return false;
|
|
5148
4943
|
};
|
|
4944
|
+
var collectVisibleCandidateIndexes = async (candidateGroup, count, timeout) => {
|
|
4945
|
+
const visibleIndexes = [];
|
|
4946
|
+
for (let index = 0; index < count; index += 1) {
|
|
4947
|
+
const candidate = candidateGroup.nth(index);
|
|
4948
|
+
const isVisible = await candidate.isVisible({ timeout }).catch(() => false);
|
|
4949
|
+
if (isVisible) {
|
|
4950
|
+
visibleIndexes.push(index);
|
|
4951
|
+
}
|
|
4952
|
+
}
|
|
4953
|
+
return visibleIndexes;
|
|
4954
|
+
};
|
|
5149
4955
|
var clickCaptchaAction = async (frame, texts, options) => {
|
|
5150
4956
|
for (const text of texts || []) {
|
|
5151
|
-
const
|
|
5152
|
-
|
|
5153
|
-
|
|
4957
|
+
const textLocator = frame.getByText(text, { exact: false });
|
|
4958
|
+
const locatorText = frame.locator(`text=${text}`);
|
|
4959
|
+
const [getByTextCount, locatorTextCount] = await Promise.all([
|
|
4960
|
+
textLocator.count().catch(() => 0),
|
|
4961
|
+
locatorText.count().catch(() => 0)
|
|
4962
|
+
]);
|
|
4963
|
+
const [getByTextVisibleIndexes, locatorTextVisibleIndexes] = await Promise.all([
|
|
4964
|
+
collectVisibleCandidateIndexes(textLocator, getByTextCount, options.actionVisibleTimeoutMs),
|
|
4965
|
+
collectVisibleCandidateIndexes(locatorText, locatorTextCount, options.actionVisibleTimeoutMs)
|
|
4966
|
+
]);
|
|
4967
|
+
options.logger?.info(
|
|
4968
|
+
`[CaptchaAction] \u6587\u672C "${text}" \u547D\u4E2D\u6570\u91CF\uFF1AgetByText=${getByTextCount} (visible=${getByTextVisibleIndexes.length}), locator=${locatorTextCount} (visible=${locatorTextVisibleIndexes.length})`
|
|
4969
|
+
);
|
|
4970
|
+
const candidateGroups = [
|
|
4971
|
+
{ label: "getByText", locator: textLocator, count: getByTextCount },
|
|
4972
|
+
{ label: "locator", locator: locatorText, count: locatorTextCount }
|
|
5154
4973
|
];
|
|
5155
|
-
for (const
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
4974
|
+
for (const candidateGroup of candidateGroups) {
|
|
4975
|
+
for (let index = 0; index < candidateGroup.count; index += 1) {
|
|
4976
|
+
const candidate = candidateGroup.locator.nth(index);
|
|
4977
|
+
const isVisible = await waitForVisible(candidate, options.actionVisibleTimeoutMs);
|
|
4978
|
+
if (!isVisible) {
|
|
4979
|
+
continue;
|
|
4980
|
+
}
|
|
4981
|
+
options.logger?.info(
|
|
4982
|
+
`[CaptchaAction] \u6587\u672C "${text}" \u9009\u62E9 ${candidateGroup.label}[${index}] \u4F5C\u4E3A\u7B2C\u4E00\u4E2A\u53EF\u89C1\u8282\u70B9\u6267\u884C\u70B9\u51FB\u3002`
|
|
4983
|
+
);
|
|
4984
|
+
await DeviceInput.click(options.page, candidate, options);
|
|
4985
|
+
return true;
|
|
5159
4986
|
}
|
|
5160
|
-
await DeviceInput.click(options.page, candidate);
|
|
5161
|
-
return true;
|
|
5162
4987
|
}
|
|
5163
4988
|
}
|
|
5164
4989
|
return false;
|
|
5165
4990
|
};
|
|
5166
|
-
var dragCaptchaAction = async (page, sourceLocator, targetLocator) => {
|
|
5167
|
-
await DeviceInput.drag(page, sourceLocator, targetLocator);
|
|
4991
|
+
var dragCaptchaAction = async (page, sourceLocator, targetLocator, options = {}) => {
|
|
4992
|
+
await DeviceInput.drag(page, sourceLocator, targetLocator, options);
|
|
5168
4993
|
};
|
|
5169
4994
|
|
|
5170
4995
|
// src/internals/captcha/bytedance.js
|
|
@@ -5175,11 +5000,12 @@ var DEFAULT_BYTEDANCE_CAPTCHA_OPTIONS = Object.freeze({
|
|
|
5175
5000
|
containerSelector: "#captcha_container",
|
|
5176
5001
|
iframeSelector: 'iframe[src*="verifycenter"]',
|
|
5177
5002
|
iframeFallbackSelector: "iframe",
|
|
5178
|
-
sourceImageSelector: "
|
|
5179
|
-
dropTargetContainerSelector: "
|
|
5003
|
+
sourceImageSelector: ".img-container .canvas-container",
|
|
5004
|
+
dropTargetContainerSelector: ".drag-area",
|
|
5180
5005
|
dropTargetTexts: ["\u62D6\u62FD\u5230\u8FD9\u91CC"],
|
|
5181
5006
|
refreshTexts: ["\u5237\u65B0"],
|
|
5182
5007
|
submitTexts: ["\u63D0\u4EA4"],
|
|
5008
|
+
guideMaskSelector: ".play-guide-mask",
|
|
5183
5009
|
recognitionSuccessCode: 1e4,
|
|
5184
5010
|
containerVisibleTimeoutMs: 2e3,
|
|
5185
5011
|
iframeVisibleTimeoutMs: 12e3,
|
|
@@ -5202,10 +5028,111 @@ var DEFAULT_BYTEDANCE_CAPTCHA_OPTIONS = Object.freeze({
|
|
|
5202
5028
|
],
|
|
5203
5029
|
recognitionDelayMs: 2e3,
|
|
5204
5030
|
refreshWaitMs: 3e3,
|
|
5205
|
-
submitWaitMs:
|
|
5031
|
+
submitWaitMs: 5e3,
|
|
5032
|
+
submitReadyTimeoutMs: 2500,
|
|
5206
5033
|
retryDelayBaseMs: 2e3,
|
|
5207
|
-
retryDelayStepMs: 1e3
|
|
5034
|
+
retryDelayStepMs: 1e3,
|
|
5035
|
+
sourceImageRowTolerancePx: 24,
|
|
5036
|
+
dragBetweenWaitMs: 250,
|
|
5037
|
+
promptBadgeCountSelector: ".drag-area .photo-badge .badge span",
|
|
5038
|
+
promptSubmitButtonSelector: ".vc-captcha-verify-mobile-button",
|
|
5039
|
+
promptSelectedSourceSelector: ".img-container .canvas-container.selected",
|
|
5040
|
+
promptActiveSourceSelector: ".img-container .canvas-container.active",
|
|
5041
|
+
promptDragMoveSteps: 16,
|
|
5042
|
+
promptDragStepDelayMs: 55,
|
|
5043
|
+
promptDragHoldDelayMs: 240,
|
|
5044
|
+
promptDragBeforeReleaseDelayMs: 180,
|
|
5045
|
+
promptDragAfterReleaseDelayMs: 240,
|
|
5046
|
+
promptDragFinalMoveRepeats: 3,
|
|
5047
|
+
promptDragRetryDelayMs: 250,
|
|
5048
|
+
debugArtifacts: false
|
|
5208
5049
|
});
|
|
5050
|
+
var PROMPT_CAPTCHA_DRAG_PLANS = Object.freeze([
|
|
5051
|
+
{ name: "lower-middle", endXRatio: 0.5, endYRatio: 0.72 },
|
|
5052
|
+
{ name: "center-middle", endXRatio: 0.5, endYRatio: 0.56 },
|
|
5053
|
+
{ name: "upper-middle", endXRatio: 0.5, endYRatio: 0.4 }
|
|
5054
|
+
]);
|
|
5055
|
+
var resolveCaptchaDebugDir = () => import_path2.default.resolve(process.cwd(), "storage", "captcha-debug");
|
|
5056
|
+
var rectOf = (rect) => {
|
|
5057
|
+
if (!rect) {
|
|
5058
|
+
return null;
|
|
5059
|
+
}
|
|
5060
|
+
return {
|
|
5061
|
+
x: Number(rect.x.toFixed(2)),
|
|
5062
|
+
y: Number(rect.y.toFixed(2)),
|
|
5063
|
+
width: Number(rect.width.toFixed(2)),
|
|
5064
|
+
height: Number(rect.height.toFixed(2))
|
|
5065
|
+
};
|
|
5066
|
+
};
|
|
5067
|
+
var collectCaptchaDebugInfo = async (page, frame, iframeLocator, attempt, phase, extra = null) => {
|
|
5068
|
+
const timestamp = Date.now();
|
|
5069
|
+
const debugDir = resolveCaptchaDebugDir();
|
|
5070
|
+
await (0, import_promises.mkdir)(debugDir, { recursive: true });
|
|
5071
|
+
const baseName = `bytedance-${timestamp}-attempt${attempt}-${phase}`;
|
|
5072
|
+
const iframeScreenshotPath = import_path2.default.join(debugDir, `${baseName}-iframe.png`);
|
|
5073
|
+
const pageScreenshotPath = import_path2.default.join(debugDir, `${baseName}-page.png`);
|
|
5074
|
+
const htmlPath = import_path2.default.join(debugDir, `${baseName}-iframe.html`);
|
|
5075
|
+
const infoPath = import_path2.default.join(debugDir, `${baseName}-info.json`);
|
|
5076
|
+
await iframeLocator.screenshot({ path: iframeScreenshotPath }).catch(() => {
|
|
5077
|
+
});
|
|
5078
|
+
await page.screenshot({ path: pageScreenshotPath, fullPage: true }).catch(() => {
|
|
5079
|
+
});
|
|
5080
|
+
const html = await frame.evaluate(() => document.documentElement.outerHTML).catch(() => "");
|
|
5081
|
+
if (html) {
|
|
5082
|
+
await (0, import_promises.writeFile)(htmlPath, html, "utf8");
|
|
5083
|
+
}
|
|
5084
|
+
const info = await frame.evaluate(() => {
|
|
5085
|
+
const toRect = (element) => {
|
|
5086
|
+
const rect = element.getBoundingClientRect();
|
|
5087
|
+
return {
|
|
5088
|
+
x: Number(rect.x.toFixed(2)),
|
|
5089
|
+
y: Number(rect.y.toFixed(2)),
|
|
5090
|
+
width: Number(rect.width.toFixed(2)),
|
|
5091
|
+
height: Number(rect.height.toFixed(2))
|
|
5092
|
+
};
|
|
5093
|
+
};
|
|
5094
|
+
const toItem = (element, index) => ({
|
|
5095
|
+
index,
|
|
5096
|
+
tag: element.tagName,
|
|
5097
|
+
id: element.id || "",
|
|
5098
|
+
className: typeof element.className === "string" ? element.className : "",
|
|
5099
|
+
text: String(element.textContent || "").trim(),
|
|
5100
|
+
rect: toRect(element)
|
|
5101
|
+
});
|
|
5102
|
+
const visibleNodes = Array.from(document.querySelectorAll("body *")).map(toItem).filter((item) => item.rect.width > 0 && item.rect.height > 0);
|
|
5103
|
+
return {
|
|
5104
|
+
title: document.title,
|
|
5105
|
+
bodyText: String(document.body?.innerText || "").trim(),
|
|
5106
|
+
canvasContainers: visibleNodes.filter((item) => item.className.includes("canvas-container")),
|
|
5107
|
+
canvasNodes: visibleNodes.filter((item) => item.tag === "CANVAS"),
|
|
5108
|
+
captchaNodes: visibleNodes.filter((item) => item.id.startsWith("captcha_") || item.className.includes("captcha") || item.className.includes("verify") || /拖拽到这里|刷新|提交/.test(item.text)),
|
|
5109
|
+
visibleTextNodes: visibleNodes.filter((item) => item.text).slice(0, 300)
|
|
5110
|
+
};
|
|
5111
|
+
}).catch(() => null);
|
|
5112
|
+
if (info) {
|
|
5113
|
+
const payload = {
|
|
5114
|
+
capturedAt: new Date(timestamp).toISOString(),
|
|
5115
|
+
pageUrl: page.url(),
|
|
5116
|
+
attempt,
|
|
5117
|
+
phase,
|
|
5118
|
+
iframeScreenshotPath,
|
|
5119
|
+
pageScreenshotPath,
|
|
5120
|
+
htmlPath,
|
|
5121
|
+
info
|
|
5122
|
+
};
|
|
5123
|
+
if (extra != null) {
|
|
5124
|
+
payload.extra = extra;
|
|
5125
|
+
}
|
|
5126
|
+
await (0, import_promises.writeFile)(infoPath, JSON.stringify(payload, null, 2), "utf8");
|
|
5127
|
+
}
|
|
5128
|
+
logger10.info(`\u5DF2\u5199\u51FA\u9A8C\u8BC1\u7801\u8C03\u8BD5\u4EA7\u7269\uFF1A${debugDir}`);
|
|
5129
|
+
};
|
|
5130
|
+
var maybeCollectCaptchaDebugInfo = async (page, frame, iframeLocator, attempt, phase, options, extra = null) => {
|
|
5131
|
+
if (!options.debugArtifacts) {
|
|
5132
|
+
return;
|
|
5133
|
+
}
|
|
5134
|
+
await collectCaptchaDebugInfo(page, frame, iframeLocator, attempt, phase, extra);
|
|
5135
|
+
};
|
|
5209
5136
|
var extractCaptchaSerialNumbers = (apiResponse) => {
|
|
5210
5137
|
const serialNumbers = apiResponse?.data?.data?.serial_number;
|
|
5211
5138
|
if (!Array.isArray(serialNumbers)) {
|
|
@@ -5214,7 +5141,7 @@ var extractCaptchaSerialNumbers = (apiResponse) => {
|
|
|
5214
5141
|
return serialNumbers.map((value) => Number(value)).filter((value) => Number.isInteger(value) && value >= 0);
|
|
5215
5142
|
};
|
|
5216
5143
|
var resolveContentFrame = async (page, iframeLocator, options) => {
|
|
5217
|
-
for (let attempt = 1; attempt <= options.contentFrameResolveRetries; attempt
|
|
5144
|
+
for (let attempt = 1; attempt <= options.contentFrameResolveRetries; attempt += 1) {
|
|
5218
5145
|
const iframeHandle = await iframeLocator.elementHandle();
|
|
5219
5146
|
const frame = await iframeHandle?.contentFrame();
|
|
5220
5147
|
if (frame) {
|
|
@@ -5259,20 +5186,15 @@ var getVerifycenterCaptchaContext = async (page, options) => {
|
|
|
5259
5186
|
}
|
|
5260
5187
|
return { iframeLocator, frame };
|
|
5261
5188
|
};
|
|
5262
|
-
var refreshCaptcha = async (page, frame, options) => {
|
|
5263
|
-
const clicked = await clickCaptchaAction(frame, options.refreshTexts, { ...options, page }).catch(() => false);
|
|
5264
|
-
if (!clicked) {
|
|
5265
|
-
logger10.warn("Refresh button not found.");
|
|
5266
|
-
return false;
|
|
5267
|
-
}
|
|
5268
|
-
await page.waitForTimeout(options.refreshWaitMs);
|
|
5269
|
-
return true;
|
|
5270
|
-
};
|
|
5271
5189
|
var findCaptchaDropTarget = async (frame, options) => {
|
|
5190
|
+
const directTarget = frame.locator(options.dropTargetContainerSelector).first();
|
|
5191
|
+
if (await waitForVisible(directTarget, options.actionVisibleTimeoutMs)) {
|
|
5192
|
+
return directTarget;
|
|
5193
|
+
}
|
|
5272
5194
|
for (const text of options.dropTargetTexts) {
|
|
5273
5195
|
const candidates = [
|
|
5274
|
-
frame.
|
|
5275
|
-
frame.
|
|
5196
|
+
frame.getByText(text, { exact: false }).first(),
|
|
5197
|
+
frame.locator(`text=${text}`).first()
|
|
5276
5198
|
];
|
|
5277
5199
|
for (const candidate of candidates) {
|
|
5278
5200
|
const isVisible = await waitForVisible(candidate, options.actionVisibleTimeoutMs);
|
|
@@ -5283,10 +5205,112 @@ var findCaptchaDropTarget = async (frame, options) => {
|
|
|
5283
5205
|
}
|
|
5284
5206
|
return null;
|
|
5285
5207
|
};
|
|
5208
|
+
var readPromptCaptchaState = async (frame, options) => frame.evaluate((selectors) => {
|
|
5209
|
+
const toRect = (element) => {
|
|
5210
|
+
if (!element) {
|
|
5211
|
+
return null;
|
|
5212
|
+
}
|
|
5213
|
+
const rect = element.getBoundingClientRect();
|
|
5214
|
+
return {
|
|
5215
|
+
x: Number(rect.x.toFixed(2)),
|
|
5216
|
+
y: Number(rect.y.toFixed(2)),
|
|
5217
|
+
width: Number(rect.width.toFixed(2)),
|
|
5218
|
+
height: Number(rect.height.toFixed(2))
|
|
5219
|
+
};
|
|
5220
|
+
};
|
|
5221
|
+
const badgeNode = document.querySelector(selectors.badgeCountSelector);
|
|
5222
|
+
const submitButton = document.querySelector(selectors.submitButtonSelector);
|
|
5223
|
+
const dragArea = document.querySelector(selectors.dragAreaSelector);
|
|
5224
|
+
const badgeCount = badgeNode ? Number.parseInt(String(badgeNode.textContent || "").trim(), 10) : 0;
|
|
5225
|
+
return {
|
|
5226
|
+
badgeCount: Number.isFinite(badgeCount) ? badgeCount : 0,
|
|
5227
|
+
selectedCount: document.querySelectorAll(selectors.selectedSourceSelector).length,
|
|
5228
|
+
activeCount: document.querySelectorAll(selectors.activeSourceSelector).length,
|
|
5229
|
+
submitDisabled: submitButton ? submitButton.classList.contains("disable") : null,
|
|
5230
|
+
dragAreaActive: dragArea ? dragArea.classList.contains("active") : false,
|
|
5231
|
+
dragAreaRect: toRect(dragArea)
|
|
5232
|
+
};
|
|
5233
|
+
}, {
|
|
5234
|
+
badgeCountSelector: options.promptBadgeCountSelector,
|
|
5235
|
+
submitButtonSelector: options.promptSubmitButtonSelector,
|
|
5236
|
+
selectedSourceSelector: options.promptSelectedSourceSelector,
|
|
5237
|
+
activeSourceSelector: options.promptActiveSourceSelector,
|
|
5238
|
+
dragAreaSelector: options.dropTargetContainerSelector
|
|
5239
|
+
}).catch(() => ({
|
|
5240
|
+
badgeCount: 0,
|
|
5241
|
+
selectedCount: 0,
|
|
5242
|
+
activeCount: 0,
|
|
5243
|
+
submitDisabled: null,
|
|
5244
|
+
dragAreaActive: false,
|
|
5245
|
+
dragAreaRect: null
|
|
5246
|
+
}));
|
|
5247
|
+
var normalizeCaptchaImageIndexes = (serialNumbers, imageCount) => {
|
|
5248
|
+
if (!Array.isArray(serialNumbers) || imageCount <= 0) {
|
|
5249
|
+
return [];
|
|
5250
|
+
}
|
|
5251
|
+
const areAllOneBased = serialNumbers.every((value) => value >= 1 && value <= imageCount);
|
|
5252
|
+
if (areAllOneBased) {
|
|
5253
|
+
return serialNumbers.map((value) => value - 1);
|
|
5254
|
+
}
|
|
5255
|
+
const areAllZeroBased = serialNumbers.every((value) => value >= 0 && value < imageCount);
|
|
5256
|
+
if (areAllZeroBased) {
|
|
5257
|
+
return [...serialNumbers];
|
|
5258
|
+
}
|
|
5259
|
+
return serialNumbers.map((value) => {
|
|
5260
|
+
if (value >= 1 && value <= imageCount) {
|
|
5261
|
+
return value - 1;
|
|
5262
|
+
}
|
|
5263
|
+
if (value >= 0 && value < imageCount) {
|
|
5264
|
+
return value;
|
|
5265
|
+
}
|
|
5266
|
+
return null;
|
|
5267
|
+
}).filter((value) => Number.isInteger(value));
|
|
5268
|
+
};
|
|
5269
|
+
var resolveCaptchaSourceImagesInVisualOrder = async (frame, options) => {
|
|
5270
|
+
const sourceImages = frame.locator(options.sourceImageSelector);
|
|
5271
|
+
const imageCount = await sourceImages.count().catch(() => 0);
|
|
5272
|
+
const sources = [];
|
|
5273
|
+
for (let domIndex = 0; domIndex < imageCount; domIndex += 1) {
|
|
5274
|
+
const locator = sourceImages.nth(domIndex);
|
|
5275
|
+
const box = await locator.boundingBox().catch(() => null);
|
|
5276
|
+
if (!box || box.width <= 0 || box.height <= 0) {
|
|
5277
|
+
continue;
|
|
5278
|
+
}
|
|
5279
|
+
sources.push({
|
|
5280
|
+
domIndex,
|
|
5281
|
+
locator,
|
|
5282
|
+
box
|
|
5283
|
+
});
|
|
5284
|
+
}
|
|
5285
|
+
sources.sort((left, right) => {
|
|
5286
|
+
const deltaY = left.box.y - right.box.y;
|
|
5287
|
+
if (Math.abs(deltaY) > options.sourceImageRowTolerancePx) {
|
|
5288
|
+
return deltaY;
|
|
5289
|
+
}
|
|
5290
|
+
return left.box.x - right.box.x;
|
|
5291
|
+
});
|
|
5292
|
+
return sources;
|
|
5293
|
+
};
|
|
5294
|
+
var refreshCaptcha = async (page, frame, options) => {
|
|
5295
|
+
const clicked = await clickCaptchaAction(frame, options.refreshTexts, {
|
|
5296
|
+
...options,
|
|
5297
|
+
page,
|
|
5298
|
+
logger: logger10,
|
|
5299
|
+
forceMouse: true
|
|
5300
|
+
}).catch(() => false);
|
|
5301
|
+
if (!clicked) {
|
|
5302
|
+
logger10.warn("Refresh button not found.");
|
|
5303
|
+
return false;
|
|
5304
|
+
}
|
|
5305
|
+
await page.waitForTimeout(options.refreshWaitMs);
|
|
5306
|
+
return true;
|
|
5307
|
+
};
|
|
5286
5308
|
var waitForCaptchaChallengeReady = async (page, frame, options) => {
|
|
5287
5309
|
const deadline = Date.now() + options.challengeReadyTimeoutMs;
|
|
5288
5310
|
let refreshDeadline = Date.now() + options.challengeReadyRefreshTimeoutMs;
|
|
5289
5311
|
let hasSeenLoading = false;
|
|
5312
|
+
let hasSeenGuideMask = false;
|
|
5313
|
+
let hasLoggedGuideMask = false;
|
|
5290
5314
|
while (Date.now() < deadline) {
|
|
5291
5315
|
const isLoadingVisible = await isAnyCaptchaTextVisible(
|
|
5292
5316
|
frame,
|
|
@@ -5302,8 +5326,17 @@ var waitForCaptchaChallengeReady = async (page, frame, options) => {
|
|
|
5302
5326
|
const sourceImages = frame.locator(options.sourceImageSelector);
|
|
5303
5327
|
const imageCount = await sourceImages.count().catch(() => 0);
|
|
5304
5328
|
const hasVisibleSourceImage = imageCount > 0 ? await sourceImages.first().isVisible({ timeout: options.loadingIndicatorVisibleTimeoutMs }).catch(() => false) : false;
|
|
5305
|
-
|
|
5306
|
-
|
|
5329
|
+
const hasVisibleDropTarget = await frame.locator(options.dropTargetContainerSelector).first().isVisible({ timeout: options.loadingIndicatorVisibleTimeoutMs }).catch(() => false);
|
|
5330
|
+
const hasGuideMaskVisible = options.guideMaskSelector ? await frame.locator(options.guideMaskSelector).first().isVisible({ timeout: options.loadingIndicatorVisibleTimeoutMs }).catch(() => false) : false;
|
|
5331
|
+
hasSeenGuideMask = hasSeenGuideMask || hasGuideMaskVisible;
|
|
5332
|
+
if (hasGuideMaskVisible && !hasLoggedGuideMask) {
|
|
5333
|
+
logger10.info("\u68C0\u6D4B\u5230\u9A8C\u8BC1\u7801\u64CD\u4F5C\u5F15\u5BFC\u5C42\uFF0C\u7B49\u5F85\u5176\u6D88\u5931\u540E\u518D\u5F00\u59CB\u8BC6\u522B\u3002");
|
|
5334
|
+
hasLoggedGuideMask = true;
|
|
5335
|
+
}
|
|
5336
|
+
if (!isLoadingVisible && hasVisibleSourceImage && hasVisibleDropTarget && !hasGuideMaskVisible) {
|
|
5337
|
+
logger10.info(
|
|
5338
|
+
hasSeenGuideMask ? "\u9A8C\u8BC1\u7801\u56FE\u7247\u548C\u62D6\u62FD\u533A\u57DF\u5DF2\u5C31\u7EEA\uFF0C\u5F15\u5BFC\u5C42\u5DF2\u6D88\u5931\u3002" : hasSeenLoading ? "\u9A8C\u8BC1\u7801\u56FE\u7247\u5DF2\u52A0\u8F7D\u5B8C\u6210\u3002" : "\u9A8C\u8BC1\u7801\u56FE\u7247\u5DF2\u5C31\u7EEA\u3002"
|
|
5339
|
+
);
|
|
5307
5340
|
return;
|
|
5308
5341
|
}
|
|
5309
5342
|
if (hasErrorTextVisible) {
|
|
@@ -5313,8 +5346,8 @@ var waitForCaptchaChallengeReady = async (page, frame, options) => {
|
|
|
5313
5346
|
hasSeenLoading = false;
|
|
5314
5347
|
continue;
|
|
5315
5348
|
}
|
|
5316
|
-
if (!hasVisibleSourceImage && Date.now() >= refreshDeadline) {
|
|
5317
|
-
logger10.warn(`\u9A8C\u8BC1\u7801\
|
|
5349
|
+
if ((!hasVisibleSourceImage || !hasVisibleDropTarget) && Date.now() >= refreshDeadline) {
|
|
5350
|
+
logger10.warn(`\u9A8C\u8BC1\u7801\u9898\u76EE\u8D85\u8FC7 ${options.challengeReadyRefreshTimeoutMs}ms \u4ECD\u672A\u51C6\u5907\u597D\uFF0C\u5C1D\u8BD5\u5237\u65B0\u9898\u76EE\u3002`);
|
|
5318
5351
|
await refreshCaptcha(page, frame, options);
|
|
5319
5352
|
refreshDeadline = Date.now() + options.challengeReadyRefreshTimeoutMs;
|
|
5320
5353
|
hasSeenLoading = false;
|
|
@@ -5324,6 +5357,69 @@ var waitForCaptchaChallengeReady = async (page, frame, options) => {
|
|
|
5324
5357
|
}
|
|
5325
5358
|
throw new Error("Captcha challenge is still loading and did not become ready in time.");
|
|
5326
5359
|
};
|
|
5360
|
+
var dragPromptCaptchaImage = async (page, frame, iframeLocator, sourceLocator, dropTarget, options, {
|
|
5361
|
+
attempt,
|
|
5362
|
+
visualIndex
|
|
5363
|
+
}) => {
|
|
5364
|
+
const baselineState = await readPromptCaptchaState(frame, options);
|
|
5365
|
+
const dragAttempts = [];
|
|
5366
|
+
for (const plan of PROMPT_CAPTCHA_DRAG_PLANS) {
|
|
5367
|
+
const sourceBox = await sourceLocator.boundingBox().catch(() => null);
|
|
5368
|
+
const targetBox = await dropTarget.boundingBox().catch(() => null);
|
|
5369
|
+
if (!sourceBox || !targetBox) {
|
|
5370
|
+
throw new Error("Unable to resolve prompt captcha drag coordinates.");
|
|
5371
|
+
}
|
|
5372
|
+
const targetOffsetX = (plan.endXRatio - 0.5) * targetBox.width;
|
|
5373
|
+
const targetOffsetY = (plan.endYRatio - 0.5) * targetBox.height;
|
|
5374
|
+
await dragCaptchaAction(page, sourceLocator, dropTarget, {
|
|
5375
|
+
forceMouse: true,
|
|
5376
|
+
targetOffsetX,
|
|
5377
|
+
targetOffsetY,
|
|
5378
|
+
steps: options.promptDragMoveSteps,
|
|
5379
|
+
holdDelayMs: options.promptDragHoldDelayMs,
|
|
5380
|
+
stepDelayMs: options.promptDragStepDelayMs,
|
|
5381
|
+
beforeReleaseDelayMs: options.promptDragBeforeReleaseDelayMs,
|
|
5382
|
+
afterReleaseDelayMs: options.promptDragAfterReleaseDelayMs,
|
|
5383
|
+
finalMoveRepeats: options.promptDragFinalMoveRepeats
|
|
5384
|
+
});
|
|
5385
|
+
const afterState = await readPromptCaptchaState(frame, options);
|
|
5386
|
+
const accepted = afterState.badgeCount > baselineState.badgeCount || afterState.selectedCount > baselineState.selectedCount;
|
|
5387
|
+
const attemptInfo = {
|
|
5388
|
+
planName: plan.name,
|
|
5389
|
+
sourceRect: rectOf(sourceBox),
|
|
5390
|
+
targetRect: rectOf(targetBox),
|
|
5391
|
+
targetOffsetX: Number(targetOffsetX.toFixed(2)),
|
|
5392
|
+
targetOffsetY: Number(targetOffsetY.toFixed(2)),
|
|
5393
|
+
beforeState: baselineState,
|
|
5394
|
+
afterState,
|
|
5395
|
+
accepted
|
|
5396
|
+
};
|
|
5397
|
+
dragAttempts.push(attemptInfo);
|
|
5398
|
+
logger10.info(
|
|
5399
|
+
`\u9A8C\u8BC1\u7801\u62D6\u62FD\u7B2C ${visualIndex + 1} \u5F20\uFF0C\u65B9\u6848 ${plan.name}\uFF0Cbadge ${baselineState.badgeCount} -> ${afterState.badgeCount}\uFF0Cselected ${baselineState.selectedCount} -> ${afterState.selectedCount}`
|
|
5400
|
+
);
|
|
5401
|
+
if (accepted) {
|
|
5402
|
+
return {
|
|
5403
|
+
accepted: true,
|
|
5404
|
+
dragAttempts
|
|
5405
|
+
};
|
|
5406
|
+
}
|
|
5407
|
+
if (options.promptDragRetryDelayMs > 0) {
|
|
5408
|
+
await page.waitForTimeout(options.promptDragRetryDelayMs);
|
|
5409
|
+
}
|
|
5410
|
+
}
|
|
5411
|
+
await maybeCollectCaptchaDebugInfo(page, frame, iframeLocator, attempt, `drag-${visualIndex + 1}-failed`, options, {
|
|
5412
|
+
visualIndex,
|
|
5413
|
+
dragAttempts,
|
|
5414
|
+
finalState: await readPromptCaptchaState(frame, options)
|
|
5415
|
+
}).catch((error) => {
|
|
5416
|
+
logger10.warn(`\u9A8C\u8BC1\u7801\u62D6\u62FD\u5931\u8D25\u8C03\u8BD5\u6293\u53D6\u5931\u8D25\uFF1A${error?.message || error}`);
|
|
5417
|
+
});
|
|
5418
|
+
return {
|
|
5419
|
+
accepted: false,
|
|
5420
|
+
dragAttempts
|
|
5421
|
+
};
|
|
5422
|
+
};
|
|
5327
5423
|
async function solveCaptcha(page, options = {}, dependencies = {}) {
|
|
5328
5424
|
const { callCaptchaRecognitionApi: callCaptchaRecognitionApi2 } = dependencies;
|
|
5329
5425
|
if (typeof callCaptchaRecognitionApi2 !== "function") {
|
|
@@ -5338,7 +5434,7 @@ async function solveCaptcha(page, options = {}, dependencies = {}) {
|
|
|
5338
5434
|
return false;
|
|
5339
5435
|
}
|
|
5340
5436
|
logger10.info("\u5F53\u524D\u4F7F\u7528\u672Ctool\u2014\u2014\u6D4B\u8BD5\u7248\u672C");
|
|
5341
|
-
for (let attempt = 1; attempt <= config.maxRetries; attempt
|
|
5437
|
+
for (let attempt = 1; attempt <= config.maxRetries; attempt += 1) {
|
|
5342
5438
|
logger10.info(`\u5F00\u59CB\u7B2C ${attempt}/${config.maxRetries} \u6B21 verifycenter \u9A8C\u8BC1\u7801\u8BC6\u522B\u3002`);
|
|
5343
5439
|
try {
|
|
5344
5440
|
const captchaContext = await getVerifycenterCaptchaContext(page, config);
|
|
@@ -5348,6 +5444,16 @@ async function solveCaptcha(page, options = {}, dependencies = {}) {
|
|
|
5348
5444
|
}
|
|
5349
5445
|
const { iframeLocator, frame } = captchaContext;
|
|
5350
5446
|
await waitForCaptchaChallengeReady(page, frame, config);
|
|
5447
|
+
await maybeCollectCaptchaDebugInfo(
|
|
5448
|
+
page,
|
|
5449
|
+
frame,
|
|
5450
|
+
iframeLocator,
|
|
5451
|
+
attempt,
|
|
5452
|
+
"ready",
|
|
5453
|
+
config
|
|
5454
|
+
).catch((error) => {
|
|
5455
|
+
logger10.warn(`\u9A8C\u8BC1\u7801\u8C03\u8BD5\u6293\u53D6\u5931\u8D25\uFF1A${error?.message || error}`);
|
|
5456
|
+
});
|
|
5351
5457
|
await page.waitForTimeout(config.recognitionDelayMs);
|
|
5352
5458
|
const screenshotBuffer = await iframeLocator.screenshot();
|
|
5353
5459
|
const apiResponse = await callCaptchaRecognitionApi2({
|
|
@@ -5371,33 +5477,74 @@ async function solveCaptcha(page, options = {}, dependencies = {}) {
|
|
|
5371
5477
|
await refreshCaptcha(page, frame, config);
|
|
5372
5478
|
continue;
|
|
5373
5479
|
}
|
|
5374
|
-
const
|
|
5375
|
-
const
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5480
|
+
const orderedSourceImages = await resolveCaptchaSourceImagesInVisualOrder(frame, config);
|
|
5481
|
+
const normalizedIndexes = normalizeCaptchaImageIndexes(serialNumbers, orderedSourceImages.length);
|
|
5482
|
+
if (normalizedIndexes.length !== serialNumbers.length) {
|
|
5483
|
+
throw new Error(
|
|
5484
|
+
`Captcha image indexes could not be normalized. raw=${serialNumbers.join(", ")}, count=${orderedSourceImages.length}`
|
|
5485
|
+
);
|
|
5486
|
+
}
|
|
5487
|
+
logger10.info(`\u9A8C\u8BC1\u7801\u89C6\u89C9\u4F4D\u5E8F\u6620\u5C04\uFF1A${normalizedIndexes.map((index) => index + 1).join(", ")}`);
|
|
5488
|
+
for (const imageIndex of normalizedIndexes) {
|
|
5489
|
+
if (imageIndex < 0 || imageIndex >= orderedSourceImages.length) {
|
|
5490
|
+
throw new Error(
|
|
5491
|
+
`Captcha image index ${imageIndex} is out of range. count=${orderedSourceImages.length}`
|
|
5492
|
+
);
|
|
5383
5493
|
}
|
|
5384
|
-
const sourceImage =
|
|
5494
|
+
const sourceImage = orderedSourceImages[imageIndex].locator;
|
|
5385
5495
|
await sourceImage.waitFor({
|
|
5386
5496
|
state: "visible",
|
|
5387
5497
|
timeout: config.sourceImageVisibleTimeoutMs
|
|
5388
5498
|
});
|
|
5389
|
-
await
|
|
5499
|
+
const dragResult = await dragPromptCaptchaImage(
|
|
5500
|
+
page,
|
|
5501
|
+
frame,
|
|
5502
|
+
iframeLocator,
|
|
5503
|
+
sourceImage,
|
|
5504
|
+
dropTarget,
|
|
5505
|
+
config,
|
|
5506
|
+
{
|
|
5507
|
+
attempt,
|
|
5508
|
+
visualIndex: imageIndex
|
|
5509
|
+
}
|
|
5510
|
+
);
|
|
5511
|
+
if (!dragResult.accepted) {
|
|
5512
|
+
throw new Error(`Captcha prompt drag was not accepted for visual index ${imageIndex + 1}.`);
|
|
5513
|
+
}
|
|
5514
|
+
if (config.dragBetweenWaitMs > 0) {
|
|
5515
|
+
await page.waitForTimeout(config.dragBetweenWaitMs);
|
|
5516
|
+
}
|
|
5390
5517
|
}
|
|
5391
|
-
const
|
|
5518
|
+
const beforeSubmitState = await readPromptCaptchaState(frame, config);
|
|
5519
|
+
logger10.info(
|
|
5520
|
+
`\u63D0\u4EA4\u524D\u9A8C\u8BC1\u7801\u72B6\u6001\uFF1Abadge=${beforeSubmitState.badgeCount}, selected=${beforeSubmitState.selectedCount}, submitDisabled=${beforeSubmitState.submitDisabled}`
|
|
5521
|
+
);
|
|
5522
|
+
const submitted = await clickCaptchaAction(frame, config.submitTexts, {
|
|
5523
|
+
...config,
|
|
5524
|
+
page,
|
|
5525
|
+
logger: logger10,
|
|
5526
|
+
forceMouse: true,
|
|
5527
|
+
actionVisibleTimeoutMs: config.submitReadyTimeoutMs
|
|
5528
|
+
}).catch(() => false);
|
|
5392
5529
|
if (!submitted) {
|
|
5393
5530
|
logger10.warn("\u672A\u627E\u5230\u63D0\u4EA4\u6309\u94AE\uFF0C\u53EF\u80FD\u4F1A\u81EA\u52A8\u63D0\u4EA4\u3002");
|
|
5394
5531
|
}
|
|
5395
5532
|
await page.waitForTimeout(config.submitWaitMs);
|
|
5533
|
+
const afterSubmitState = await readPromptCaptchaState(frame, config);
|
|
5534
|
+
logger10.info(
|
|
5535
|
+
`\u63D0\u4EA4\u540E\u9A8C\u8BC1\u7801\u72B6\u6001\uFF1Abadge=${afterSubmitState.badgeCount}, selected=${afterSubmitState.selectedCount}, submitDisabled=${afterSubmitState.submitDisabled}`
|
|
5536
|
+
);
|
|
5396
5537
|
const stillVisible = await iframeLocator.isVisible({ timeout: config.containerVisibleTimeoutMs }).catch(() => false);
|
|
5397
5538
|
if (!stillVisible) {
|
|
5398
5539
|
logger10.info("\u9A8C\u8BC1\u7801\u8BC6\u522B\u5E76\u63D0\u4EA4\u6210\u529F\u3002");
|
|
5399
5540
|
return true;
|
|
5400
5541
|
}
|
|
5542
|
+
await maybeCollectCaptchaDebugInfo(page, frame, iframeLocator, attempt, "submit-still-visible", config, {
|
|
5543
|
+
beforeSubmitState,
|
|
5544
|
+
afterSubmitState
|
|
5545
|
+
}).catch((error) => {
|
|
5546
|
+
logger10.warn(`\u63D0\u4EA4\u540E\u9A8C\u8BC1\u7801\u8C03\u8BD5\u6293\u53D6\u5931\u8D25\uFF1A${error?.message || error}`);
|
|
5547
|
+
});
|
|
5401
5548
|
logger10.warn("\u63D0\u4EA4\u540E\u9A8C\u8BC1\u7801 iframe \u4ECD\u7136\u53EF\u89C1\uFF0C\u51C6\u5907\u5237\u65B0\u540E\u91CD\u8BD5\u3002");
|
|
5402
5549
|
await page.waitForTimeout(2e3);
|
|
5403
5550
|
await refreshCaptcha(page, frame, config);
|
|
@@ -5840,14 +5987,14 @@ var Mutation = {
|
|
|
5840
5987
|
const isFrameElement = tagName === "IFRAME" || tagName === "FRAME";
|
|
5841
5988
|
const nodeName = descriptor?.id || descriptor?.name || "no-id";
|
|
5842
5989
|
let source = "main";
|
|
5843
|
-
let
|
|
5990
|
+
let path3 = `${selector}[${index}]`;
|
|
5844
5991
|
let text = "";
|
|
5845
5992
|
let html = "";
|
|
5846
5993
|
let frameUrl = "";
|
|
5847
5994
|
let readyState = "";
|
|
5848
5995
|
if (isFrameElement) {
|
|
5849
5996
|
source = "iframe";
|
|
5850
|
-
|
|
5997
|
+
path3 = `${selector}[${index}]::iframe(${nodeName})`;
|
|
5851
5998
|
const frame = await handle.contentFrame();
|
|
5852
5999
|
if (frame) {
|
|
5853
6000
|
try {
|
|
@@ -5877,7 +6024,7 @@ var Mutation = {
|
|
|
5877
6024
|
items.push({
|
|
5878
6025
|
selector,
|
|
5879
6026
|
source,
|
|
5880
|
-
path:
|
|
6027
|
+
path: path3,
|
|
5881
6028
|
text,
|
|
5882
6029
|
html,
|
|
5883
6030
|
frameUrl,
|