fiptcha 0.1.0 → 0.1.1
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/README.md +278 -137
- package/package.json +1 -1
- package/src/captcha-local-solver.js +58 -38
package/README.md
CHANGED
|
@@ -1,139 +1,233 @@
|
|
|
1
1
|
# Fiptcha
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/fiptcha)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Local CAPTCHA solving for browser automation.
|
|
6
6
|
|
|
7
|
-
Fiptcha
|
|
7
|
+
Fiptcha is the lightweight CAPTCHA runtime used by Figranium, packaged as a standalone Apache-2.0 Node.js library so it can be embedded in other browser automation projects.
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Install
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
```bash
|
|
12
|
+
npm install fiptcha
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
CommonJS:
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
const { solveLocalCaptcha } = require('fiptcha');
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Fiptcha does not launch a browser for you. Your application owns the browser lifecycle and passes an active Playwright-compatible `page` into the solver.
|
|
22
|
+
|
|
23
|
+
## Quick start
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
const { chromium } = require('playwright');
|
|
27
|
+
const { solveLocalCaptcha } = require('fiptcha');
|
|
28
|
+
|
|
29
|
+
const browser = await chromium.launch({ headless: false });
|
|
30
|
+
const page = await browser.newPage();
|
|
12
31
|
|
|
13
|
-
|
|
32
|
+
await page.goto('https://example.com');
|
|
14
33
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
34
|
+
const logs = [];
|
|
35
|
+
const result = await solveLocalCaptcha(page, {
|
|
36
|
+
captchaType: 'recaptcha_v2',
|
|
37
|
+
timeout: 60_000,
|
|
38
|
+
logs
|
|
39
|
+
});
|
|
21
40
|
|
|
22
|
-
|
|
41
|
+
console.log(result.token);
|
|
42
|
+
console.log(result.provider); // "local"
|
|
43
|
+
console.log(result.model); // present when a vision model was used
|
|
44
|
+
console.log(result.device); // "browser", "cpu", or the active inference device
|
|
23
45
|
|
|
24
|
-
|
|
46
|
+
await browser.close();
|
|
47
|
+
```
|
|
25
48
|
|
|
26
|
-
|
|
27
|
-
- hCaptcha image challenges;
|
|
28
|
-
- Cloudflare Turnstile/browser challenge helpers.
|
|
49
|
+
## Supported challenge types
|
|
29
50
|
|
|
30
|
-
|
|
51
|
+
Pass one of these values as `captchaType`:
|
|
31
52
|
|
|
32
|
-
|
|
53
|
+
| Value | Behavior |
|
|
54
|
+
| --- | --- |
|
|
55
|
+
| `recaptcha_v2` | Interacts with the checkbox and solves supported image-grid challenges locally when required. |
|
|
56
|
+
| `hcaptcha` | Interacts with the widget and solves supported image-grid challenges locally when required. |
|
|
57
|
+
| `turnstile` | Performs active-browser interaction and waits for a token. It does not use the image-grid vision path. |
|
|
33
58
|
|
|
34
|
-
|
|
35
|
-
- Resource-aware model selection.
|
|
36
|
-
- Verified, on-demand model downloads and model lifecycle management.
|
|
37
|
-
- Image-grid classification and challenge handling.
|
|
38
|
-
- Browser challenge interception helpers.
|
|
39
|
-
- Companion-process client and runtime support.
|
|
40
|
-
- Optional MLX acceleration for compatible Apple Silicon environments.
|
|
41
|
-
- Container/resource probing utilities.
|
|
42
|
-
- CAPTCHA benchmarking helpers.
|
|
43
|
-
- Environment-variable configuration through the `CAPTCHA_*` settings used by the runtime.
|
|
44
|
-
- Apache-2.0 licensing for reuse in other applications.
|
|
59
|
+
Fiptcha cannot guarantee that every challenge will be solvable. CAPTCHA providers can change their UI, challenge format, and risk checks independently of the visible widget.
|
|
45
60
|
|
|
46
|
-
##
|
|
61
|
+
## `solveLocalCaptcha(page, options)`
|
|
47
62
|
|
|
48
|
-
|
|
63
|
+
This is the main integration API.
|
|
49
64
|
|
|
50
|
-
|
|
65
|
+
```js
|
|
66
|
+
const result = await solveLocalCaptcha(page, {
|
|
67
|
+
captchaType: 'hcaptcha',
|
|
68
|
+
timeout: 60_000,
|
|
69
|
+
logs: []
|
|
70
|
+
});
|
|
71
|
+
```
|
|
51
72
|
|
|
52
|
-
|
|
73
|
+
### Arguments
|
|
53
74
|
|
|
54
|
-
|
|
75
|
+
#### `page`
|
|
55
76
|
|
|
56
|
-
|
|
57
|
-
|
|
77
|
+
An active Playwright-compatible page. Fiptcha uses page/frame APIs, locators, mouse interaction, evaluation, and timeouts against this object.
|
|
78
|
+
|
|
79
|
+
#### `options.captchaType`
|
|
80
|
+
|
|
81
|
+
Required. One of:
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
recaptcha_v2
|
|
85
|
+
hcaptcha
|
|
86
|
+
turnstile
|
|
58
87
|
```
|
|
59
88
|
|
|
60
|
-
|
|
89
|
+
#### `options.timeout`
|
|
90
|
+
|
|
91
|
+
Optional. Maximum solve time in milliseconds.
|
|
92
|
+
|
|
93
|
+
Default:
|
|
61
94
|
|
|
62
95
|
```js
|
|
63
|
-
|
|
96
|
+
60_000
|
|
64
97
|
```
|
|
65
98
|
|
|
66
|
-
|
|
99
|
+
#### `options.logs`
|
|
67
100
|
|
|
68
|
-
|
|
101
|
+
Optional array. Fiptcha appends diagnostic messages to it while solving.
|
|
69
102
|
|
|
70
103
|
```js
|
|
71
|
-
const
|
|
104
|
+
const logs = [];
|
|
72
105
|
|
|
73
|
-
|
|
106
|
+
await solveLocalCaptcha(page, {
|
|
74
107
|
captchaType: 'recaptcha_v2',
|
|
75
|
-
|
|
108
|
+
logs
|
|
76
109
|
});
|
|
77
110
|
|
|
78
|
-
console.log(
|
|
111
|
+
console.log(logs);
|
|
79
112
|
```
|
|
80
113
|
|
|
81
|
-
|
|
114
|
+
### Return value
|
|
115
|
+
|
|
116
|
+
A successful solve returns an object similar to:
|
|
82
117
|
|
|
83
|
-
|
|
118
|
+
```js
|
|
119
|
+
{
|
|
120
|
+
token: '...',
|
|
121
|
+
provider: 'local',
|
|
122
|
+
model: 'owlvit', // only when a vision backend was used
|
|
123
|
+
device: 'cpu'
|
|
124
|
+
}
|
|
125
|
+
```
|
|
84
126
|
|
|
85
|
-
|
|
127
|
+
`model` is omitted when no vision model was needed.
|
|
86
128
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
129
|
+
### Errors
|
|
130
|
+
|
|
131
|
+
`solveLocalCaptcha` throws when:
|
|
132
|
+
|
|
133
|
+
- the requested CAPTCHA type is unsupported;
|
|
134
|
+
- a challenge never exposes a usable token or image challenge;
|
|
135
|
+
- a required vision backend cannot be prepared;
|
|
136
|
+
- the challenge ends without producing a token;
|
|
137
|
+
- Turnstile does not issue a token after browser interaction.
|
|
138
|
+
|
|
139
|
+
Wrap solves in your application's normal error handling:
|
|
140
|
+
|
|
141
|
+
```js
|
|
142
|
+
try {
|
|
143
|
+
const result = await solveLocalCaptcha(page, {
|
|
144
|
+
captchaType: 'recaptcha_v2',
|
|
145
|
+
timeout: 60_000
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
console.log(result.token);
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.error('CAPTCHA solve failed:', error.message);
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Browser integration pattern
|
|
155
|
+
|
|
156
|
+
A typical integration should keep orchestration outside Fiptcha:
|
|
157
|
+
|
|
158
|
+
```js
|
|
159
|
+
const { solveLocalCaptcha } = require('fiptcha');
|
|
160
|
+
|
|
161
|
+
async function handleCaptcha(page, captchaType) {
|
|
162
|
+
const logs = [];
|
|
163
|
+
|
|
164
|
+
const result = await solveLocalCaptcha(page, {
|
|
165
|
+
captchaType,
|
|
166
|
+
timeout: 60_000,
|
|
167
|
+
logs
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
token: result.token,
|
|
172
|
+
diagnostics: logs
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Your application should remain responsible for:
|
|
178
|
+
|
|
179
|
+
- launching and closing browsers;
|
|
180
|
+
- choosing browser contexts and sessions;
|
|
181
|
+
- navigation;
|
|
182
|
+
- deciding when CAPTCHA handling should run;
|
|
183
|
+
- retries at the workflow level;
|
|
184
|
+
- proxies and network policy;
|
|
185
|
+
- persistence;
|
|
186
|
+
- translating Fiptcha errors/results into your own execution model.
|
|
187
|
+
|
|
188
|
+
Fiptcha is responsible for the CAPTCHA-specific runtime: token detection, widget interaction, grid solving, model selection, model downloads, resource checks, and optional companion runtimes.
|
|
189
|
+
|
|
190
|
+
## Exported helpers
|
|
191
|
+
|
|
192
|
+
The package root re-exports the public exports from these modules:
|
|
193
|
+
|
|
194
|
+
| Module | Purpose |
|
|
195
|
+
| --- | --- |
|
|
196
|
+
| `captcha-local-solver` | High-level widget/token handling and `solveLocalCaptcha`. |
|
|
197
|
+
| `captcha-grid-solver` | Image-grid solving and provider adapters. |
|
|
198
|
+
| `captcha-interceptor` | Challenge interception helpers. |
|
|
199
|
+
| `captcha-resources` | Host/container memory and resource detection. |
|
|
200
|
+
| `captcha-model-manager` | Model selection, readiness, and lifecycle state. |
|
|
201
|
+
| `captcha-model-downloader` | Model download and verification. |
|
|
202
|
+
| `captcha-model-manifest` | Model metadata and requirements. |
|
|
203
|
+
| `captcha-companion-client` | Client for the optional companion process. |
|
|
204
|
+
| `captcha-mlx-runtime` | Apple Silicon MLX runtime support. |
|
|
205
|
+
| `captcha-benchmark` | Benchmarking helpers. |
|
|
206
|
+
|
|
207
|
+
For example, lower-level token helpers are also available:
|
|
123
208
|
|
|
124
209
|
```js
|
|
125
210
|
const {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
211
|
+
readToken,
|
|
212
|
+
waitForToken,
|
|
213
|
+
clickCheckbox,
|
|
214
|
+
solveLocalCaptcha
|
|
129
215
|
} = require('fiptcha');
|
|
130
216
|
```
|
|
131
217
|
|
|
132
|
-
|
|
218
|
+
The high-level solver is the recommended integration boundary. Lower-level exports are useful for custom integrations, but their API should be treated as pre-stable until Fiptcha reaches a stable standalone API release.
|
|
219
|
+
|
|
220
|
+
## Models and memory requirements
|
|
221
|
+
|
|
222
|
+
Fiptcha does not bundle large model weights into the npm tarball. Required assets are acquired through the model-management layer when needed.
|
|
223
|
+
|
|
224
|
+
The runtime chooses an appropriate backend based on available resources. In particular, a provider-advertised **2 GB** machine is interpreted using decimal sizing (2,000,000,000 bytes, about 1907 MiB) rather than incorrectly requiring 2048 MiB.
|
|
225
|
+
|
|
226
|
+
If your application runs in an ephemeral container, persist the model/cache directory used by your deployment so models do not need to be downloaded again on every container recreation.
|
|
133
227
|
|
|
134
228
|
## Companion runtime
|
|
135
229
|
|
|
136
|
-
Fiptcha includes
|
|
230
|
+
Fiptcha includes an optional companion process for environments that need it.
|
|
137
231
|
|
|
138
232
|
Install companion requirements:
|
|
139
233
|
|
|
@@ -141,103 +235,150 @@ Install companion requirements:
|
|
|
141
235
|
npm run companion:install
|
|
142
236
|
```
|
|
143
237
|
|
|
144
|
-
Start
|
|
238
|
+
Start it:
|
|
145
239
|
|
|
146
240
|
```bash
|
|
147
241
|
npm run companion:start
|
|
148
242
|
```
|
|
149
243
|
|
|
150
|
-
|
|
244
|
+
Start the Docker-oriented path:
|
|
151
245
|
|
|
152
246
|
```bash
|
|
153
247
|
npm run companion:start:docker
|
|
154
248
|
```
|
|
155
249
|
|
|
156
|
-
The companion is optional.
|
|
250
|
+
The companion is optional. Ordinary Node.js integrations do not need to make it part of their architecture unless they use that runtime path.
|
|
251
|
+
|
|
252
|
+
## Apple Silicon / MLX
|
|
157
253
|
|
|
158
|
-
|
|
254
|
+
Fiptcha contains the MLX worker and runtime integration used for compatible Apple Silicon environments.
|
|
159
255
|
|
|
160
|
-
The
|
|
256
|
+
The MLX path is optional. Consumers that do not use MLX can ignore the bundled worker and companion requirements.
|
|
257
|
+
|
|
258
|
+
## Inspect the runtime environment
|
|
259
|
+
|
|
260
|
+
Run the included resource probe:
|
|
161
261
|
|
|
162
262
|
```bash
|
|
163
263
|
npm run probe
|
|
164
264
|
```
|
|
165
265
|
|
|
166
|
-
This is useful
|
|
167
|
-
|
|
168
|
-
Resource detection is kept inside Fiptcha so applications embedding it do not need to reproduce Figranium-specific hardware checks.
|
|
266
|
+
This is useful in containers and VMs where cgroup/container limits may differ from the physical host's reported memory.
|
|
169
267
|
|
|
170
|
-
##
|
|
268
|
+
## Test Fiptcha
|
|
171
269
|
|
|
172
|
-
|
|
270
|
+
```bash
|
|
271
|
+
npm test
|
|
272
|
+
```
|
|
173
273
|
|
|
174
|
-
The
|
|
274
|
+
The smoke suite checks the extracted runtime and includes regression coverage for resource selection, including the 2 GB memory threshold.
|
|
175
275
|
|
|
176
|
-
##
|
|
276
|
+
## Check what npm will publish
|
|
177
277
|
|
|
178
|
-
|
|
278
|
+
Before publishing a release:
|
|
179
279
|
|
|
180
|
-
|
|
280
|
+
```bash
|
|
281
|
+
npm pack --dry-run
|
|
282
|
+
```
|
|
181
283
|
|
|
182
|
-
|
|
284
|
+
The package intentionally publishes the runtime source, entry point, companion scripts, README, and license—not downloaded model weights.
|
|
183
285
|
|
|
184
|
-
|
|
286
|
+
## Release process
|
|
185
287
|
|
|
186
|
-
|
|
288
|
+
Fiptcha publishes to npm as:
|
|
187
289
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
4. allow Fiptcha to manage its local model/runtime concerns;
|
|
192
|
-
5. translate the returned result into the host application's own success/error model.
|
|
290
|
+
```text
|
|
291
|
+
fiptcha
|
|
292
|
+
```
|
|
193
293
|
|
|
194
|
-
|
|
294
|
+
The repository uses npm Trusted Publishing through GitHub Actions. Releases are published without a long-lived npm token.
|
|
195
295
|
|
|
196
|
-
|
|
296
|
+
To release a new version:
|
|
197
297
|
|
|
198
|
-
|
|
298
|
+
1. Update `version` in `package.json`.
|
|
299
|
+
2. Merge the change to `main`.
|
|
300
|
+
3. Create and publish a GitHub Release whose tag matches the package version, for example `v0.2.0`.
|
|
301
|
+
4. The release workflow runs the tests and publishes the package to npm through OIDC Trusted Publishing.
|
|
199
302
|
|
|
200
|
-
|
|
303
|
+
Do not manually add an `NPM_TOKEN` secret for the normal release path.
|
|
201
304
|
|
|
202
|
-
##
|
|
305
|
+
## Using Fiptcha from another project
|
|
203
306
|
|
|
204
|
-
|
|
307
|
+
Install it normally:
|
|
205
308
|
|
|
206
309
|
```bash
|
|
207
|
-
npm
|
|
310
|
+
npm install fiptcha
|
|
208
311
|
```
|
|
209
312
|
|
|
210
|
-
|
|
313
|
+
Then keep the integration at your browser/runtime boundary:
|
|
314
|
+
|
|
315
|
+
```js
|
|
316
|
+
const { solveLocalCaptcha } = require('fiptcha');
|
|
211
317
|
|
|
212
|
-
|
|
318
|
+
async function solve(page) {
|
|
319
|
+
return solveLocalCaptcha(page, {
|
|
320
|
+
captchaType: 'recaptcha_v2',
|
|
321
|
+
timeout: 45_000
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
```
|
|
213
325
|
|
|
214
|
-
|
|
326
|
+
No Figranium task schema, server, UI, database, scheduler, authentication system, or storage layer is required.
|
|
215
327
|
|
|
216
|
-
|
|
328
|
+
## Using Fiptcha in Figranium
|
|
217
329
|
|
|
218
|
-
|
|
330
|
+
Figranium consumes the published `fiptcha` package and keeps only Figranium-specific integration code in the main application.
|
|
219
331
|
|
|
220
|
-
|
|
332
|
+
The standalone Fiptcha repository is the canonical implementation of the local CAPTCHA runtime. Fixes to model handling, resource detection, challenge interaction, or companion behavior should be made here rather than copied back into Figranium.
|
|
221
333
|
|
|
222
|
-
|
|
334
|
+
## Troubleshooting
|
|
223
335
|
|
|
224
|
-
|
|
336
|
+
### `Local solver does not support ...`
|
|
225
337
|
|
|
226
|
-
|
|
338
|
+
Check that `captchaType` is exactly one of:
|
|
227
339
|
|
|
228
|
-
|
|
340
|
+
```text
|
|
341
|
+
recaptcha_v2
|
|
342
|
+
hcaptcha
|
|
343
|
+
turnstile
|
|
344
|
+
```
|
|
229
345
|
|
|
230
|
-
|
|
346
|
+
### `vision backend unavailable`
|
|
231
347
|
|
|
232
|
-
-
|
|
233
|
-
- Node.js version;
|
|
234
|
-
- container/runtime details if applicable;
|
|
235
|
-
- available memory;
|
|
236
|
-
- CAPTCHA provider/challenge family;
|
|
237
|
-
- whether the ordinary runtime or companion/MLX path was used.
|
|
348
|
+
The challenge requires the image-solving path, but Fiptcha could not prepare a supported vision backend. Check available memory, model availability/downloads, and the output of:
|
|
238
349
|
|
|
239
|
-
|
|
350
|
+
```bash
|
|
351
|
+
npm run probe
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### Challenge appears but no token is returned
|
|
355
|
+
|
|
356
|
+
Increase the solve timeout and collect the `logs` array. Also verify that the challenge family still matches the provider structure expected by the current Fiptcha version.
|
|
357
|
+
|
|
358
|
+
### Models download repeatedly in containers
|
|
359
|
+
|
|
360
|
+
Persist the model/cache storage used by the runtime instead of recreating it with every ephemeral container.
|
|
361
|
+
|
|
362
|
+
### A lower-level helper changed between releases
|
|
363
|
+
|
|
364
|
+
Prefer `solveLocalCaptcha` for application integrations. Until the low-level API is declared stable, pin an exact Fiptcha version if you depend directly on internal/model/grid helpers.
|
|
365
|
+
|
|
366
|
+
## Package contents
|
|
367
|
+
|
|
368
|
+
The npm package contains:
|
|
369
|
+
|
|
370
|
+
```text
|
|
371
|
+
index.js
|
|
372
|
+
src/
|
|
373
|
+
scripts/captcha-mlx-worker.py
|
|
374
|
+
scripts/captcha-companion-requirements.txt
|
|
375
|
+
scripts/captcha-container-probe.js
|
|
376
|
+
scripts/install-captcha-companion.js
|
|
377
|
+
scripts/captcha-companion.js
|
|
378
|
+
README.md
|
|
379
|
+
LICENSE
|
|
380
|
+
```
|
|
240
381
|
|
|
241
382
|
## License
|
|
242
383
|
|
|
243
|
-
|
|
384
|
+
Apache-2.0.
|
package/package.json
CHANGED
|
@@ -7,6 +7,21 @@ const TOKEN_SELECTORS = Object.freeze({
|
|
|
7
7
|
turnstile: ['input[name="cf-turnstile-response"]', 'textarea[name="cf-turnstile-response"]']
|
|
8
8
|
});
|
|
9
9
|
|
|
10
|
+
const WIDGET_INTERACTIONS = Object.freeze({
|
|
11
|
+
recaptcha_v2: Object.freeze({
|
|
12
|
+
framePatterns: ['/recaptcha/api2/anchor', '/recaptcha/enterprise/anchor'],
|
|
13
|
+
selectors: ['#recaptcha-anchor', '[role="checkbox"]']
|
|
14
|
+
}),
|
|
15
|
+
hcaptcha: Object.freeze({
|
|
16
|
+
framePatterns: ['hcaptcha.com/captcha', 'newassets.hcaptcha.com/captcha'],
|
|
17
|
+
selectors: ['#checkbox', '[role="checkbox"]', 'input[type="checkbox"]', 'label']
|
|
18
|
+
}),
|
|
19
|
+
turnstile: Object.freeze({
|
|
20
|
+
framePatterns: ['challenges.cloudflare.com'],
|
|
21
|
+
selectors: ['[role="checkbox"]', 'input[type="checkbox"]', 'label', 'button']
|
|
22
|
+
})
|
|
23
|
+
});
|
|
24
|
+
|
|
10
25
|
async function readToken(page, captchaType) {
|
|
11
26
|
return page.evaluate(({ type, selectors }) => {
|
|
12
27
|
for (const selector of selectors[type] || []) {
|
|
@@ -57,43 +72,40 @@ async function waitForTokenOrChallenge(page, captchaType, timeout) {
|
|
|
57
72
|
return null;
|
|
58
73
|
}
|
|
59
74
|
|
|
60
|
-
async function clickCheckbox(page, captchaType) {
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
await page.mouse.move(x, y, { steps: 8 });
|
|
89
|
-
await page.mouse.down();
|
|
90
|
-
await page.waitForTimeout(45);
|
|
91
|
-
await page.mouse.up();
|
|
92
|
-
} else {
|
|
93
|
-
await checkbox.click({ timeout: 2000 });
|
|
75
|
+
async function clickCheckbox(page, captchaType, timeout = 10_000) {
|
|
76
|
+
const interaction = WIDGET_INTERACTIONS[captchaType];
|
|
77
|
+
if (!interaction) return false;
|
|
78
|
+
const deadline = Date.now() + Math.max(0, timeout);
|
|
79
|
+
|
|
80
|
+
do {
|
|
81
|
+
for (const frame of page.frames?.() || []) {
|
|
82
|
+
const frameUrl = String(frame.url?.() || '');
|
|
83
|
+
if (!interaction.framePatterns.some((pattern) => frameUrl.includes(pattern))) continue;
|
|
84
|
+
|
|
85
|
+
for (const selector of interaction.selectors) {
|
|
86
|
+
const matches = frame.locator(selector);
|
|
87
|
+
const count = await matches.count().catch(() => 0);
|
|
88
|
+
for (let index = 0; index < count; index += 1) {
|
|
89
|
+
const checkbox = matches.nth(index);
|
|
90
|
+
if (!await checkbox.isVisible({ timeout: 100 }).catch(() => false)) continue;
|
|
91
|
+
if (!await checkbox.isEnabled({ timeout: 100 }).catch(() => true)) continue;
|
|
92
|
+
const pointerReady = await checkbox.evaluate((element) => {
|
|
93
|
+
const style = getComputedStyle(element);
|
|
94
|
+
return style.pointerEvents !== 'none' && element.getAttribute('aria-disabled') !== 'true';
|
|
95
|
+
}).catch(() => true);
|
|
96
|
+
if (!pointerReady) continue;
|
|
97
|
+
|
|
98
|
+
const clickTimeout = Math.max(1, Math.min(2000, deadline - Date.now()));
|
|
99
|
+
const clicked = await checkbox.click({ timeout: clickTimeout }).then(() => true, () => false);
|
|
100
|
+
if (clicked) return true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
94
103
|
}
|
|
95
|
-
|
|
96
|
-
|
|
104
|
+
|
|
105
|
+
if (Date.now() >= deadline) break;
|
|
106
|
+
await page.waitForTimeout(Math.min(100, Math.max(1, deadline - Date.now())));
|
|
107
|
+
} while (Date.now() <= deadline);
|
|
108
|
+
|
|
97
109
|
return false;
|
|
98
110
|
}
|
|
99
111
|
|
|
@@ -121,7 +133,15 @@ async function solveLocalCaptcha(page, { captchaType, timeout = 60_000, logs = [
|
|
|
121
133
|
: null;
|
|
122
134
|
let token = await readToken(page, captchaType);
|
|
123
135
|
if (!token) {
|
|
124
|
-
|
|
136
|
+
const clickTimeout = Math.min(10_000, Math.max(0, deadline - Date.now()));
|
|
137
|
+
const clicked = await clickCheckbox(page, captchaType, clickTimeout)
|
|
138
|
+
.catch((error) => {
|
|
139
|
+
logs.push(`Local ${captchaType} checkbox click failed: ${error.message}`);
|
|
140
|
+
return false;
|
|
141
|
+
});
|
|
142
|
+
logs.push(clicked
|
|
143
|
+
? `Local ${captchaType} checkbox clicked`
|
|
144
|
+
: `Local ${captchaType} checkbox was not found before the interaction timeout`);
|
|
125
145
|
const initialWait = captchaType === 'hcaptcha' ? 1500 : 7000;
|
|
126
146
|
token = await waitForTokenOrChallenge(page, captchaType, Math.min(initialWait, Math.max(0, deadline - Date.now())));
|
|
127
147
|
if (!token && captchaType === 'hcaptcha' && Date.now() < deadline) {
|
|
@@ -165,4 +185,4 @@ async function solveLocalCaptcha(page, { captchaType, timeout = 60_000, logs = [
|
|
|
165
185
|
};
|
|
166
186
|
}
|
|
167
187
|
|
|
168
|
-
module.exports = { TOKEN_SELECTORS, readToken, waitForToken, waitForTokenOrChallenge, clickCheckbox, executeHcaptchaWidget, solveLocalCaptcha };
|
|
188
|
+
module.exports = { TOKEN_SELECTORS, WIDGET_INTERACTIONS, readToken, waitForToken, waitForTokenOrChallenge, clickCheckbox, executeHcaptchaWidget, solveLocalCaptcha };
|