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