@skrillex1224/playwright-toolkit 2.1.217 → 2.1.219
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/browser.js.map +1 -1
- package/dist/index.cjs +330 -27
- package/dist/index.cjs.map +3 -3
- package/dist/index.js +330 -27
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -415,6 +415,9 @@ function createInternalLogger(moduleName, explicitLogger) {
|
|
|
415
415
|
},
|
|
416
416
|
info(message) {
|
|
417
417
|
baseLogger.info(message);
|
|
418
|
+
},
|
|
419
|
+
error(message) {
|
|
420
|
+
baseLogger.error(message);
|
|
418
421
|
}
|
|
419
422
|
};
|
|
420
423
|
}
|
|
@@ -2947,13 +2950,45 @@ var LiveView = {
|
|
|
2947
2950
|
// src/captcha-monitor.js
|
|
2948
2951
|
import { v4 as uuidv4 } from "uuid";
|
|
2949
2952
|
var logger9 = createInternalLogger("Captcha");
|
|
2953
|
+
var DEFAULT_BYTEDANCE_CAPTCHA_TOKEN = "eKJvBfwfN0YRav0-VD_44E2VBSfm7l0YtddUQ7cFySI";
|
|
2954
|
+
var DEFAULT_BYTEDANCE_CAPTCHA_OPTIONS = Object.freeze({
|
|
2955
|
+
token: DEFAULT_BYTEDANCE_CAPTCHA_TOKEN,
|
|
2956
|
+
apiUrl: "https://api.jfbym.com/api/YmServer/customApi",
|
|
2957
|
+
apiType: "31234",
|
|
2958
|
+
maxRetries: 3,
|
|
2959
|
+
containerSelector: "#captcha_container",
|
|
2960
|
+
iframeSelector: 'iframe[src*="verifycenter"]',
|
|
2961
|
+
iframeFallbackSelector: "iframe",
|
|
2962
|
+
sourceImageSelector: "div.canvas-container",
|
|
2963
|
+
dropTargetContainerSelector: "#captcha_verify_image div",
|
|
2964
|
+
dropTargetTexts: ["\u62D6\u62FD\u5230\u8FD9\u91CC"],
|
|
2965
|
+
refreshTexts: ["\u5237\u65B0"],
|
|
2966
|
+
submitTexts: ["\u63D0\u4EA4"],
|
|
2967
|
+
recognitionSuccessCode: 1e4,
|
|
2968
|
+
containerVisibleTimeoutMs: 2e3,
|
|
2969
|
+
iframeVisibleTimeoutMs: 12e3,
|
|
2970
|
+
iframeFallbackVisibleTimeoutMs: 4e3,
|
|
2971
|
+
contentFrameResolveRetries: 5,
|
|
2972
|
+
contentFrameResolveDelayMs: 500,
|
|
2973
|
+
actionVisibleTimeoutMs: 1500,
|
|
2974
|
+
sourceImageVisibleTimeoutMs: 3e3,
|
|
2975
|
+
challengeReadyTimeoutMs: 15e3,
|
|
2976
|
+
challengeReadyPollMs: 300,
|
|
2977
|
+
loadingIndicatorVisibleTimeoutMs: 200,
|
|
2978
|
+
loadingTexts: ["\u52A0\u8F7D\u4E2D", "\u52A0\u8F7D\u4E2D..."],
|
|
2979
|
+
recognitionDelayMs: 2e3,
|
|
2980
|
+
refreshWaitMs: 3e3,
|
|
2981
|
+
submitWaitMs: 3e3,
|
|
2982
|
+
retryDelayBaseMs: 2e3,
|
|
2983
|
+
retryDelayStepMs: 1e3
|
|
2984
|
+
});
|
|
2950
2985
|
function useCaptchaMonitor(page, options) {
|
|
2951
2986
|
const { domSelector, urlPattern, onDetected } = options;
|
|
2952
2987
|
if (!domSelector && !urlPattern) {
|
|
2953
|
-
throw new Error("[CaptchaMonitor] \u5FC5\u987B\u63D0\u4F9B domSelector \u6216 urlPattern
|
|
2988
|
+
throw new Error("[CaptchaMonitor] \u5FC5\u987B\u63D0\u4F9B domSelector \u6216 urlPattern\u3002");
|
|
2954
2989
|
}
|
|
2955
2990
|
if (!onDetected || typeof onDetected !== "function") {
|
|
2956
|
-
throw new Error("[CaptchaMonitor] onDetected \u5FC5\u987B\u662F\
|
|
2991
|
+
throw new Error("[CaptchaMonitor] onDetected \u5FC5\u987B\u662F\u51FD\u6570\u3002");
|
|
2957
2992
|
}
|
|
2958
2993
|
let isStopped = false;
|
|
2959
2994
|
let isHandling = false;
|
|
@@ -2974,28 +3009,22 @@ function useCaptchaMonitor(page, options) {
|
|
|
2974
3009
|
const cleanerName = `__c_cleaner_${uuidv4().replace(/-/g, "_")}`;
|
|
2975
3010
|
page.exposeFunction(exposedFunctionName, triggerDetected).catch(() => {
|
|
2976
3011
|
});
|
|
2977
|
-
page.addInitScript(({ selector, callbackName, cleanerName:
|
|
3012
|
+
page.addInitScript(({ selector, callbackName, cleanerName: cleanupName }) => {
|
|
2978
3013
|
(() => {
|
|
2979
3014
|
let observer = null;
|
|
2980
3015
|
const checkAndReport = () => {
|
|
2981
3016
|
const element = document.querySelector(selector);
|
|
2982
|
-
if (element) {
|
|
2983
|
-
|
|
2984
|
-
window[callbackName]();
|
|
2985
|
-
}
|
|
2986
|
-
return true;
|
|
3017
|
+
if (!element) {
|
|
3018
|
+
return false;
|
|
2987
3019
|
}
|
|
2988
|
-
|
|
3020
|
+
if (window[callbackName]) {
|
|
3021
|
+
window[callbackName]();
|
|
3022
|
+
}
|
|
3023
|
+
return true;
|
|
2989
3024
|
};
|
|
2990
3025
|
checkAndReport();
|
|
2991
3026
|
observer = new MutationObserver((mutations) => {
|
|
2992
|
-
|
|
2993
|
-
for (const mutation of mutations) {
|
|
2994
|
-
if (mutation.addedNodes.length > 0) {
|
|
2995
|
-
shouldCheck = true;
|
|
2996
|
-
break;
|
|
2997
|
-
}
|
|
2998
|
-
}
|
|
3027
|
+
const shouldCheck = mutations.some((mutation) => mutation.addedNodes.length > 0);
|
|
2999
3028
|
if (shouldCheck && observer) {
|
|
3000
3029
|
checkAndReport();
|
|
3001
3030
|
}
|
|
@@ -3011,7 +3040,7 @@ function useCaptchaMonitor(page, options) {
|
|
|
3011
3040
|
} else {
|
|
3012
3041
|
mountObserver();
|
|
3013
3042
|
}
|
|
3014
|
-
window[
|
|
3043
|
+
window[cleanupName] = () => {
|
|
3015
3044
|
if (observer) {
|
|
3016
3045
|
observer.disconnect();
|
|
3017
3046
|
observer = null;
|
|
@@ -3019,7 +3048,7 @@ function useCaptchaMonitor(page, options) {
|
|
|
3019
3048
|
};
|
|
3020
3049
|
})();
|
|
3021
3050
|
}, { selector: domSelector, callbackName: exposedFunctionName, cleanerName });
|
|
3022
|
-
logger9.success("useCaptchaMonitor", `DOM \u76D1\u63A7\u5DF2\u542F\u7528
|
|
3051
|
+
logger9.success("useCaptchaMonitor", `DOM \u76D1\u63A7\u5DF2\u542F\u7528\uFF1A${domSelector}`);
|
|
3023
3052
|
cleanupFns.push(async () => {
|
|
3024
3053
|
try {
|
|
3025
3054
|
await page.evaluate((name) => {
|
|
@@ -3028,28 +3057,29 @@ function useCaptchaMonitor(page, options) {
|
|
|
3028
3057
|
delete window[name];
|
|
3029
3058
|
}
|
|
3030
3059
|
}, cleanerName);
|
|
3031
|
-
} catch
|
|
3060
|
+
} catch {
|
|
3032
3061
|
}
|
|
3033
3062
|
});
|
|
3034
3063
|
}
|
|
3035
3064
|
if (urlPattern) {
|
|
3036
3065
|
frameHandler = async (frame) => {
|
|
3037
|
-
if (frame
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3066
|
+
if (frame !== page.mainFrame()) {
|
|
3067
|
+
return;
|
|
3068
|
+
}
|
|
3069
|
+
const currentUrl = page.url();
|
|
3070
|
+
if (currentUrl.includes(urlPattern)) {
|
|
3071
|
+
await triggerDetected();
|
|
3042
3072
|
}
|
|
3043
3073
|
};
|
|
3044
3074
|
page.on("framenavigated", frameHandler);
|
|
3045
|
-
logger9.success("useCaptchaMonitor", `URL \u76D1\u63A7\u5DF2\u542F\u7528
|
|
3075
|
+
logger9.success("useCaptchaMonitor", `URL \u76D1\u63A7\u5DF2\u542F\u7528\uFF1A${urlPattern}`);
|
|
3046
3076
|
cleanupFns.push(async () => {
|
|
3047
3077
|
page.off("framenavigated", frameHandler);
|
|
3048
3078
|
});
|
|
3049
3079
|
}
|
|
3050
3080
|
return {
|
|
3051
3081
|
stop: async () => {
|
|
3052
|
-
logger9.info("
|
|
3082
|
+
logger9.info("\u6B63\u5728\u505C\u6B62\u9A8C\u8BC1\u7801\u76D1\u63A7...");
|
|
3053
3083
|
for (const fn of cleanupFns) {
|
|
3054
3084
|
await fn();
|
|
3055
3085
|
}
|
|
@@ -3057,8 +3087,281 @@ function useCaptchaMonitor(page, options) {
|
|
|
3057
3087
|
}
|
|
3058
3088
|
};
|
|
3059
3089
|
}
|
|
3090
|
+
var callCaptchaRecognitionApi = async (imageBase64, { apiUrl, apiType, token }) => {
|
|
3091
|
+
const response = await fetch(apiUrl, {
|
|
3092
|
+
method: "POST",
|
|
3093
|
+
headers: {
|
|
3094
|
+
"Content-Type": "application/json"
|
|
3095
|
+
},
|
|
3096
|
+
body: JSON.stringify({
|
|
3097
|
+
type: apiType,
|
|
3098
|
+
image: imageBase64,
|
|
3099
|
+
token
|
|
3100
|
+
})
|
|
3101
|
+
});
|
|
3102
|
+
if (!response.ok) {
|
|
3103
|
+
throw new Error(`Captcha API request failed with status ${response.status}`);
|
|
3104
|
+
}
|
|
3105
|
+
return await response.json();
|
|
3106
|
+
};
|
|
3107
|
+
var extractCaptchaSerialNumbers = (apiResponse) => {
|
|
3108
|
+
const serialNumbers = apiResponse?.data?.data?.serial_number;
|
|
3109
|
+
if (!Array.isArray(serialNumbers)) {
|
|
3110
|
+
return [];
|
|
3111
|
+
}
|
|
3112
|
+
return serialNumbers.map((value) => Number(value)).filter((value) => Number.isInteger(value) && value >= 0);
|
|
3113
|
+
};
|
|
3114
|
+
var waitForVisible = async (locator, timeout) => {
|
|
3115
|
+
try {
|
|
3116
|
+
await locator.waitFor({
|
|
3117
|
+
state: "visible",
|
|
3118
|
+
timeout
|
|
3119
|
+
});
|
|
3120
|
+
return true;
|
|
3121
|
+
} catch {
|
|
3122
|
+
return false;
|
|
3123
|
+
}
|
|
3124
|
+
};
|
|
3125
|
+
var isAnyCaptchaTextVisible = async (frame, texts, timeout) => {
|
|
3126
|
+
for (const text of texts || []) {
|
|
3127
|
+
if (!text) {
|
|
3128
|
+
continue;
|
|
3129
|
+
}
|
|
3130
|
+
const candidates = [
|
|
3131
|
+
frame.getByText(text, { exact: false }).first(),
|
|
3132
|
+
frame.locator(`text=${text}`).first()
|
|
3133
|
+
];
|
|
3134
|
+
for (const candidate of candidates) {
|
|
3135
|
+
const isVisible = await candidate.isVisible({ timeout }).catch(() => false);
|
|
3136
|
+
if (isVisible) {
|
|
3137
|
+
return true;
|
|
3138
|
+
}
|
|
3139
|
+
}
|
|
3140
|
+
}
|
|
3141
|
+
return false;
|
|
3142
|
+
};
|
|
3143
|
+
var waitForCaptchaChallengeReady = async (page, frame, options) => {
|
|
3144
|
+
const deadline = Date.now() + options.challengeReadyTimeoutMs;
|
|
3145
|
+
let hasSeenLoading = false;
|
|
3146
|
+
while (Date.now() < deadline) {
|
|
3147
|
+
const isLoadingVisible = await isAnyCaptchaTextVisible(
|
|
3148
|
+
frame,
|
|
3149
|
+
options.loadingTexts,
|
|
3150
|
+
options.loadingIndicatorVisibleTimeoutMs
|
|
3151
|
+
);
|
|
3152
|
+
hasSeenLoading = hasSeenLoading || isLoadingVisible;
|
|
3153
|
+
const sourceImages = frame.locator(options.sourceImageSelector);
|
|
3154
|
+
const imageCount = await sourceImages.count().catch(() => 0);
|
|
3155
|
+
const hasVisibleSourceImage = imageCount > 0 ? await sourceImages.first().isVisible({ timeout: options.loadingIndicatorVisibleTimeoutMs }).catch(() => false) : false;
|
|
3156
|
+
if (!isLoadingVisible && hasVisibleSourceImage) {
|
|
3157
|
+
logger9.info(hasSeenLoading ? "\u9A8C\u8BC1\u7801\u56FE\u7247\u5DF2\u52A0\u8F7D\u5B8C\u6210\u3002" : "\u9A8C\u8BC1\u7801\u56FE\u7247\u5DF2\u5C31\u7EEA\u3002");
|
|
3158
|
+
return;
|
|
3159
|
+
}
|
|
3160
|
+
await page.waitForTimeout(options.challengeReadyPollMs);
|
|
3161
|
+
}
|
|
3162
|
+
throw new Error("Captcha challenge is still loading and did not become ready in time.");
|
|
3163
|
+
};
|
|
3164
|
+
var resolveContentFrame = async (page, iframeLocator, options) => {
|
|
3165
|
+
for (let attempt = 1; attempt <= options.contentFrameResolveRetries; attempt++) {
|
|
3166
|
+
const iframeHandle = await iframeLocator.elementHandle();
|
|
3167
|
+
const frame = await iframeHandle?.contentFrame();
|
|
3168
|
+
if (frame) {
|
|
3169
|
+
return frame;
|
|
3170
|
+
}
|
|
3171
|
+
if (attempt < options.contentFrameResolveRetries) {
|
|
3172
|
+
await page.waitForTimeout(options.contentFrameResolveDelayMs);
|
|
3173
|
+
}
|
|
3174
|
+
}
|
|
3175
|
+
return null;
|
|
3176
|
+
};
|
|
3177
|
+
var getVerifycenterCaptchaContext = async (page, options) => {
|
|
3178
|
+
const captchaContainer = page.locator(options.containerSelector).first();
|
|
3179
|
+
const isContainerVisible = await waitForVisible(
|
|
3180
|
+
captchaContainer,
|
|
3181
|
+
options.containerVisibleTimeoutMs
|
|
3182
|
+
);
|
|
3183
|
+
if (!isContainerVisible) {
|
|
3184
|
+
return null;
|
|
3185
|
+
}
|
|
3186
|
+
logger9.info("\u68C0\u6D4B\u5230\u9A8C\u8BC1\u7801\u5BB9\u5668\uFF0C\u5F00\u59CB\u7B49\u5F85 iframe \u52A0\u8F7D\u3002");
|
|
3187
|
+
let iframeLocator = page.locator(options.iframeSelector).first();
|
|
3188
|
+
let isIframeVisible = await waitForVisible(
|
|
3189
|
+
iframeLocator,
|
|
3190
|
+
options.iframeVisibleTimeoutMs
|
|
3191
|
+
);
|
|
3192
|
+
if (!isIframeVisible) {
|
|
3193
|
+
logger9.warn("\u672A\u5728\u9884\u671F\u9009\u62E9\u5668\u4E2D\u627E\u5230 verifycenter iframe\uFF0C\u5C1D\u8BD5\u5BB9\u5668\u5185\u4EFB\u610F iframe\u3002");
|
|
3194
|
+
iframeLocator = captchaContainer.locator(options.iframeFallbackSelector).first();
|
|
3195
|
+
isIframeVisible = await waitForVisible(
|
|
3196
|
+
iframeLocator,
|
|
3197
|
+
options.iframeFallbackVisibleTimeoutMs
|
|
3198
|
+
);
|
|
3199
|
+
}
|
|
3200
|
+
if (!isIframeVisible) {
|
|
3201
|
+
throw new Error("verifycenter iframe not found inside captcha container.");
|
|
3202
|
+
}
|
|
3203
|
+
logger9.info("\u9A8C\u8BC1\u7801 iframe \u5DF2\u53EF\u89C1\uFF0C\u5F00\u59CB\u89E3\u6790\u5185\u5BB9 frame\u3002");
|
|
3204
|
+
const frame = await resolveContentFrame(page, iframeLocator, options);
|
|
3205
|
+
if (!frame) {
|
|
3206
|
+
throw new Error("Failed to resolve verifycenter iframe content frame.");
|
|
3207
|
+
}
|
|
3208
|
+
return { iframeLocator, frame };
|
|
3209
|
+
};
|
|
3210
|
+
var clickCaptchaAction = async (frame, texts, options) => {
|
|
3211
|
+
for (const text of texts) {
|
|
3212
|
+
const candidates = [
|
|
3213
|
+
frame.getByText(text, { exact: false }).first(),
|
|
3214
|
+
frame.locator(`text=${text}`).first()
|
|
3215
|
+
];
|
|
3216
|
+
for (const candidate of candidates) {
|
|
3217
|
+
const isVisible = await waitForVisible(candidate, options.actionVisibleTimeoutMs);
|
|
3218
|
+
if (!isVisible) {
|
|
3219
|
+
continue;
|
|
3220
|
+
}
|
|
3221
|
+
await candidate.click();
|
|
3222
|
+
return true;
|
|
3223
|
+
}
|
|
3224
|
+
}
|
|
3225
|
+
return false;
|
|
3226
|
+
};
|
|
3227
|
+
var findCaptchaDropTarget = async (frame, options) => {
|
|
3228
|
+
for (const text of options.dropTargetTexts) {
|
|
3229
|
+
const candidates = [
|
|
3230
|
+
frame.locator(options.dropTargetContainerSelector).filter({ hasText: text }).first(),
|
|
3231
|
+
frame.getByText(text, { exact: false }).first()
|
|
3232
|
+
];
|
|
3233
|
+
for (const candidate of candidates) {
|
|
3234
|
+
const isVisible = await waitForVisible(candidate, options.actionVisibleTimeoutMs);
|
|
3235
|
+
if (isVisible) {
|
|
3236
|
+
return candidate;
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3239
|
+
}
|
|
3240
|
+
return null;
|
|
3241
|
+
};
|
|
3242
|
+
var dragCaptchaWithMouse = async (page, sourceLocator, targetLocator) => {
|
|
3243
|
+
const sourceBox = await sourceLocator.boundingBox();
|
|
3244
|
+
const targetBox = await targetLocator.boundingBox();
|
|
3245
|
+
if (!sourceBox || !targetBox) {
|
|
3246
|
+
throw new Error("Unable to resolve captcha drag coordinates.");
|
|
3247
|
+
}
|
|
3248
|
+
const startX = sourceBox.x + sourceBox.width / 2;
|
|
3249
|
+
const startY = sourceBox.y + sourceBox.height / 2;
|
|
3250
|
+
const endX = targetBox.x + targetBox.width / 2;
|
|
3251
|
+
const endY = targetBox.y + targetBox.height / 2;
|
|
3252
|
+
const steps = 10;
|
|
3253
|
+
const liftOffsetX = Math.min(18, Math.max(8, sourceBox.width * 0.12));
|
|
3254
|
+
const liftOffsetY = Math.min(12, Math.max(4, sourceBox.height * 0.08));
|
|
3255
|
+
await page.mouse.move(startX, startY, { steps: 8 });
|
|
3256
|
+
await page.waitForTimeout(250);
|
|
3257
|
+
await page.mouse.down();
|
|
3258
|
+
await page.waitForTimeout(350);
|
|
3259
|
+
await page.mouse.move(startX + liftOffsetX, startY + liftOffsetY, { steps: 6 });
|
|
3260
|
+
await page.waitForTimeout(250);
|
|
3261
|
+
for (let step = 1; step <= steps; step++) {
|
|
3262
|
+
const progress = step / steps;
|
|
3263
|
+
const easedProgress = 1 - (1 - progress) * (1 - progress);
|
|
3264
|
+
const currentX = startX + liftOffsetX + (endX - startX - liftOffsetX) * easedProgress;
|
|
3265
|
+
const currentY = startY + liftOffsetY + (endY - startY - liftOffsetY) * easedProgress;
|
|
3266
|
+
await page.mouse.move(currentX, currentY, { steps: 2 });
|
|
3267
|
+
await page.waitForTimeout(90);
|
|
3268
|
+
}
|
|
3269
|
+
await page.waitForTimeout(100);
|
|
3270
|
+
await page.mouse.up();
|
|
3271
|
+
await page.waitForTimeout(100);
|
|
3272
|
+
};
|
|
3273
|
+
var refreshCaptcha = async (page, frame, options) => {
|
|
3274
|
+
const clicked = await clickCaptchaAction(frame, options.refreshTexts, options).catch(() => false);
|
|
3275
|
+
if (!clicked) {
|
|
3276
|
+
logger9.warn("Refresh button not found.");
|
|
3277
|
+
return false;
|
|
3278
|
+
}
|
|
3279
|
+
await page.waitForTimeout(options.refreshWaitMs);
|
|
3280
|
+
return true;
|
|
3281
|
+
};
|
|
3282
|
+
async function solveBytedanceCaptcha(page, options = {}) {
|
|
3283
|
+
const config = {
|
|
3284
|
+
...DEFAULT_BYTEDANCE_CAPTCHA_OPTIONS,
|
|
3285
|
+
...options
|
|
3286
|
+
};
|
|
3287
|
+
if (!config.token) {
|
|
3288
|
+
logger9.warn("\u7F3A\u5C11\u9A8C\u8BC1\u7801 token\uFF0C\u8DF3\u8FC7\u81EA\u52A8\u8BC6\u522B\u3002");
|
|
3289
|
+
return false;
|
|
3290
|
+
}
|
|
3291
|
+
for (let attempt = 1; attempt <= config.maxRetries; attempt++) {
|
|
3292
|
+
logger9.info(`\u5F00\u59CB\u7B2C ${attempt}/${config.maxRetries} \u6B21 verifycenter \u9A8C\u8BC1\u7801\u8BC6\u522B\u3002`);
|
|
3293
|
+
try {
|
|
3294
|
+
const captchaContext = await getVerifycenterCaptchaContext(page, config);
|
|
3295
|
+
if (!captchaContext) {
|
|
3296
|
+
logger9.info("Captcha container is not visible anymore.");
|
|
3297
|
+
return true;
|
|
3298
|
+
}
|
|
3299
|
+
const { iframeLocator, frame } = captchaContext;
|
|
3300
|
+
await waitForCaptchaChallengeReady(page, frame, config);
|
|
3301
|
+
await page.waitForTimeout(config.recognitionDelayMs);
|
|
3302
|
+
const screenshotBuffer = await iframeLocator.screenshot();
|
|
3303
|
+
const apiResponse = await callCaptchaRecognitionApi(
|
|
3304
|
+
screenshotBuffer.toString("base64"),
|
|
3305
|
+
config
|
|
3306
|
+
);
|
|
3307
|
+
const serialNumbers = extractCaptchaSerialNumbers(apiResponse);
|
|
3308
|
+
if (apiResponse?.code !== config.recognitionSuccessCode || serialNumbers.length === 0) {
|
|
3309
|
+
logger9.warn(
|
|
3310
|
+
`\u9A8C\u8BC1\u7801\u8BC6\u522B\u5931\u8D25\u3002code=${apiResponse?.code}, msg=${apiResponse?.msg || "unknown"}`
|
|
3311
|
+
);
|
|
3312
|
+
await refreshCaptcha(page, frame, config);
|
|
3313
|
+
continue;
|
|
3314
|
+
}
|
|
3315
|
+
logger9.info(`\u9A8C\u8BC1\u7801\u8BC6\u522B\u6210\u529F\uFF0C\u5E8F\u53F7\uFF1A${serialNumbers.join(", ")}`);
|
|
3316
|
+
const dropTarget = await findCaptchaDropTarget(frame, config);
|
|
3317
|
+
if (!dropTarget) {
|
|
3318
|
+
logger9.warn("\u672A\u627E\u5230\u9A8C\u8BC1\u7801\u62D6\u62FD\u76EE\u6807\u533A\u57DF\u3002");
|
|
3319
|
+
await refreshCaptcha(page, frame, config);
|
|
3320
|
+
continue;
|
|
3321
|
+
}
|
|
3322
|
+
const sourceImages = frame.locator(config.sourceImageSelector);
|
|
3323
|
+
const imageCount = await sourceImages.count();
|
|
3324
|
+
for (const rawIndex of serialNumbers) {
|
|
3325
|
+
let imageIndex = rawIndex;
|
|
3326
|
+
if (imageIndex >= imageCount && imageIndex > 0 && imageIndex - 1 < imageCount) {
|
|
3327
|
+
imageIndex -= 1;
|
|
3328
|
+
}
|
|
3329
|
+
if (imageIndex < 0 || imageIndex >= imageCount) {
|
|
3330
|
+
throw new Error(`Captcha image index ${rawIndex} is out of range. count=${imageCount}`);
|
|
3331
|
+
}
|
|
3332
|
+
const sourceImage = sourceImages.nth(imageIndex);
|
|
3333
|
+
await sourceImage.waitFor({
|
|
3334
|
+
state: "visible",
|
|
3335
|
+
timeout: config.sourceImageVisibleTimeoutMs
|
|
3336
|
+
});
|
|
3337
|
+
await dragCaptchaWithMouse(page, sourceImage, dropTarget);
|
|
3338
|
+
}
|
|
3339
|
+
const submitted = await clickCaptchaAction(frame, config.submitTexts, config).catch(() => false);
|
|
3340
|
+
if (!submitted) {
|
|
3341
|
+
logger9.warn("\u672A\u627E\u5230\u63D0\u4EA4\u6309\u94AE\uFF0C\u53EF\u80FD\u4F1A\u81EA\u52A8\u63D0\u4EA4\u3002");
|
|
3342
|
+
}
|
|
3343
|
+
await page.waitForTimeout(config.submitWaitMs);
|
|
3344
|
+
const stillVisible = await iframeLocator.isVisible({ timeout: config.containerVisibleTimeoutMs }).catch(() => false);
|
|
3345
|
+
if (!stillVisible) {
|
|
3346
|
+
logger9.info("\u9A8C\u8BC1\u7801\u8BC6\u522B\u5E76\u63D0\u4EA4\u6210\u529F\u3002");
|
|
3347
|
+
return true;
|
|
3348
|
+
}
|
|
3349
|
+
logger9.warn("\u63D0\u4EA4\u540E\u9A8C\u8BC1\u7801 iframe \u4ECD\u7136\u53EF\u89C1\uFF0C\u51C6\u5907\u5237\u65B0\u540E\u91CD\u8BD5\u3002");
|
|
3350
|
+
await page.waitForTimeout(2e3);
|
|
3351
|
+
await refreshCaptcha(page, frame, config);
|
|
3352
|
+
} catch (error) {
|
|
3353
|
+
logger9.error(`\u7B2C ${attempt}/${config.maxRetries} \u6B21\u9A8C\u8BC1\u7801\u8BC6\u522B\u5931\u8D25\uFF1A${error?.message || error}`);
|
|
3354
|
+
}
|
|
3355
|
+
if (attempt < config.maxRetries) {
|
|
3356
|
+
await page.waitForTimeout(config.retryDelayBaseMs + attempt * config.retryDelayStepMs);
|
|
3357
|
+
}
|
|
3358
|
+
}
|
|
3359
|
+
logger9.error(`\u91CD\u8BD5 ${config.maxRetries} \u6B21\u540E\uFF0C\u9A8C\u8BC1\u7801\u4ECD\u672A\u8BC6\u522B\u6210\u529F\u3002`);
|
|
3360
|
+
return false;
|
|
3361
|
+
}
|
|
3060
3362
|
var Captcha = {
|
|
3061
|
-
useCaptchaMonitor
|
|
3363
|
+
useCaptchaMonitor,
|
|
3364
|
+
solveBytedanceCaptcha
|
|
3062
3365
|
};
|
|
3063
3366
|
|
|
3064
3367
|
// src/mutation.js
|