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