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