capskip 1.1.0 → 1.3.0
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/CHANGELOG.md +72 -0
- package/README.md +248 -24
- package/docs/API_REFERENCE.md +391 -1
- package/examples/altcha.js +103 -0
- package/examples/captchafox.js +102 -0
- package/examples/capy.js +101 -0
- package/examples/friendly_captcha.js +115 -0
- package/package.json +23 -2
- package/src/apiParams.js +201 -0
- package/src/solver.js +339 -1
- package/types/index.d.ts +175 -5
package/src/solver.js
CHANGED
|
@@ -83,6 +83,13 @@ function applyPollResult(result, polled) {
|
|
|
83
83
|
if (userAgent) {
|
|
84
84
|
result.userAgent = userAgent;
|
|
85
85
|
}
|
|
86
|
+
// ALTCHA's createTask-shaped `solution` object. Carried through so
|
|
87
|
+
// applyAltchaSolution can read the counter the server already worked out,
|
|
88
|
+
// which is the only reliable source for a proof-of-work v2 answer; that
|
|
89
|
+
// function deletes it, so it never reaches the caller.
|
|
90
|
+
if (polled.solution !== null && typeof polled.solution === 'object') {
|
|
91
|
+
result.solution = polled.solution;
|
|
92
|
+
}
|
|
86
93
|
} else {
|
|
87
94
|
result.code = polled;
|
|
88
95
|
}
|
|
@@ -126,6 +133,159 @@ function applyGeetestSolution(result) {
|
|
|
126
133
|
return result;
|
|
127
134
|
}
|
|
128
135
|
|
|
136
|
+
// ALTCHA answers come back as a base64 payload: the challenge document with the
|
|
137
|
+
// winning counter added. That payload is what the site's own `altcha` form field
|
|
138
|
+
// carries, so it is posted back verbatim.
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Expose the answer as `token`, and the winning counter as `number`.
|
|
142
|
+
*
|
|
143
|
+
* `code` keeps the raw answer so callers that forward it verbatim (or that were
|
|
144
|
+
* written against another solver's API) keep working; `token` is the same string,
|
|
145
|
+
* named for the form field it goes into. If the payload does not decode, the
|
|
146
|
+
* result is returned untouched rather than masking the server's reply.
|
|
147
|
+
*/
|
|
148
|
+
/**
|
|
149
|
+
* Dig the winning counter out of a token, whichever scheme produced it.
|
|
150
|
+
*
|
|
151
|
+
* The two ALTCHA generations nest it differently: a legacy payload is the
|
|
152
|
+
* challenge document with a top-level `number` added, while a proof-of-work v2
|
|
153
|
+
* payload is `{ challenge: {...}, solution: { counter: N, ... } }` and has no
|
|
154
|
+
* `number` at all. Returns undefined if the payload does not decode.
|
|
155
|
+
*/
|
|
156
|
+
function tokenCounter(code) {
|
|
157
|
+
let payload;
|
|
158
|
+
try {
|
|
159
|
+
const decoded = Buffer.from(code, 'base64');
|
|
160
|
+
// Buffer.from is lenient: it drops invalid characters instead of throwing,
|
|
161
|
+
// so round-trip to confirm the input really was base64 before trusting it.
|
|
162
|
+
if (decoded.toString('base64').replace(/=+$/, '') !== code.replace(/=+$/, '')) {
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
payload = JSON.parse(decoded.toString('utf8'));
|
|
166
|
+
} catch (err) {
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (payload === null || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
171
|
+
return undefined;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (payload.number !== undefined) {
|
|
175
|
+
return payload.number;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (payload.solution !== null && typeof payload.solution === 'object') {
|
|
179
|
+
return payload.solution.counter;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Expose the answer as `token`, and the winning counter as `number`.
|
|
187
|
+
*
|
|
188
|
+
* `code` keeps the raw answer so callers that forward it verbatim (or that were
|
|
189
|
+
* written against another solver's API) keep working; `token` is the same
|
|
190
|
+
* string, named for the form field it goes into.
|
|
191
|
+
*
|
|
192
|
+
* The counter comes from the server's own `solution` object when the poll
|
|
193
|
+
* carried one, because that is the single field both ALTCHA generations report
|
|
194
|
+
* the same way. Only if it is absent — a plain-text poll — is it dug out of the
|
|
195
|
+
* token, which is shaped differently per scheme. If neither yields one, the
|
|
196
|
+
* result keeps its token and simply has no `number`, rather than masking the
|
|
197
|
+
* server's reply.
|
|
198
|
+
*/
|
|
199
|
+
function applyAltchaSolution(result) {
|
|
200
|
+
const code = result.code || '';
|
|
201
|
+
result.token = code;
|
|
202
|
+
|
|
203
|
+
const { solution } = result;
|
|
204
|
+
delete result.solution;
|
|
205
|
+
|
|
206
|
+
let number = solution !== null && typeof solution === 'object'
|
|
207
|
+
? solution.number
|
|
208
|
+
: undefined;
|
|
209
|
+
if (number === undefined) {
|
|
210
|
+
number = tokenCounter(code);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (number !== undefined) {
|
|
214
|
+
result.number = number;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// A Capy solution is not a token. It is three values that together go into the
|
|
221
|
+
// target form, under the capy_ prefixed names the widget would have filled in.
|
|
222
|
+
const CAPY_FIELDS = ['captchakey', 'challengekey', 'answer', 'respKey'];
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Expand the Capy answer into `captchakey` / `challengekey` / `answer`.
|
|
226
|
+
*
|
|
227
|
+
* `code` keeps the raw answer -- an object when polled with json=1, where the
|
|
228
|
+
* server puts it straight into `request`, or the JSON string it sends after
|
|
229
|
+
* `OK|` in plain-text mode -- so callers that forward it verbatim (or that were
|
|
230
|
+
* written against another solver's API) keep working.
|
|
231
|
+
*
|
|
232
|
+
* If the answer does not parse, the result is returned untouched rather than
|
|
233
|
+
* masking the server's reply.
|
|
234
|
+
*/
|
|
235
|
+
function applyCapySolution(result) {
|
|
236
|
+
let payload = result.solution;
|
|
237
|
+
delete result.solution;
|
|
238
|
+
|
|
239
|
+
if (payload === null || typeof payload !== 'object') {
|
|
240
|
+
const { code } = result;
|
|
241
|
+
if (code !== null && typeof code === 'object') {
|
|
242
|
+
payload = code;
|
|
243
|
+
} else {
|
|
244
|
+
try {
|
|
245
|
+
payload = JSON.parse(code || '');
|
|
246
|
+
} catch (error) {
|
|
247
|
+
return result;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (payload === null || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
253
|
+
return result;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
for (const field of CAPY_FIELDS) {
|
|
257
|
+
if (Object.prototype.hasOwnProperty.call(payload, field)) {
|
|
258
|
+
result[field] = payload[field];
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return result;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Expose a single-token answer as `token`, named for the form field it fills.
|
|
267
|
+
*
|
|
268
|
+
* `code` keeps the raw answer so callers that forward it verbatim keep working;
|
|
269
|
+
* `token` is the same string. The server's createTask-shaped `solution` object
|
|
270
|
+
* carries that same string, so it is consumed here rather than handed back as a
|
|
271
|
+
* second copy -- but it is read first when `request` came through empty, so a
|
|
272
|
+
* client is never left without the token the poll actually carried.
|
|
273
|
+
*/
|
|
274
|
+
function applyTokenSolution(result) {
|
|
275
|
+
const { solution } = result;
|
|
276
|
+
delete result.solution;
|
|
277
|
+
|
|
278
|
+
let code = result.code;
|
|
279
|
+
|
|
280
|
+
if (!code && solution !== null && typeof solution === 'object') {
|
|
281
|
+
code = solution.token || '';
|
|
282
|
+
result.code = code;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
result.token = code || '';
|
|
286
|
+
return result;
|
|
287
|
+
}
|
|
288
|
+
|
|
129
289
|
// CapSkip's in.php returns OK|<id> by default, or {"status":1,"request":"<id>"}
|
|
130
290
|
// when the submit carried json=1. Accept both so submitting with json=1 works.
|
|
131
291
|
function parseSubmitResponse(response) {
|
|
@@ -149,7 +309,7 @@ function parseSubmitResponse(response) {
|
|
|
149
309
|
throw new ApiException(`cannot recognize response ${response}`);
|
|
150
310
|
}
|
|
151
311
|
|
|
152
|
-
/** Client for the CapSkip local captcha solver (image, reCAPTCHA, Turnstile, GeeTest v3). */
|
|
312
|
+
/** Client for the CapSkip local captcha solver (image, reCAPTCHA, Turnstile, GeeTest v3, ALTCHA). */
|
|
153
313
|
class CapSkip {
|
|
154
314
|
constructor({
|
|
155
315
|
apiKey = 'capskip',
|
|
@@ -230,6 +390,169 @@ class CapSkip {
|
|
|
230
390
|
return applyGeetestSolution(result);
|
|
231
391
|
}
|
|
232
392
|
|
|
393
|
+
/**
|
|
394
|
+
* Solve an ALTCHA proof-of-work challenge.
|
|
395
|
+
*
|
|
396
|
+
* Pass `challengeUrl` for CapSkip to fetch the challenge itself, or
|
|
397
|
+
* `challengeJson` with the document you already have (a JSON string, or an
|
|
398
|
+
* object which is serialized for you). Sending both is allowed -- the inline
|
|
399
|
+
* document wins. A proxy applies only to the `challengeUrl` fetch.
|
|
400
|
+
*
|
|
401
|
+
* Challenges expire fast -- some sites inside two minutes -- and an expired one
|
|
402
|
+
* is refused with a bare "verification failed" that looks exactly like a wrong
|
|
403
|
+
* answer. Fetch the challenge immediately before calling, and post the token
|
|
404
|
+
* promptly.
|
|
405
|
+
*
|
|
406
|
+
* The result carries the raw answer as `code`, the same string as `token`
|
|
407
|
+
* (what the site's `altcha` form field expects, verbatim), and the counter
|
|
408
|
+
* that solved it as `number`.
|
|
409
|
+
*/
|
|
410
|
+
async altcha(url, options = {}) {
|
|
411
|
+
// An unset challenge param is dropped rather than sent as undefined, so
|
|
412
|
+
// `altcha(url, { challengeUrl, challengeJson })` works with either left out.
|
|
413
|
+
const given = {};
|
|
414
|
+
for (const [key, value] of Object.entries(options)) {
|
|
415
|
+
if (value !== undefined && value !== null) {
|
|
416
|
+
given[key] = value;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Unlike GeeTest and reCAPTCHA this is CPU proof-of-work measured in
|
|
421
|
+
// milliseconds, not a browser solve, so it keeps the default timeout.
|
|
422
|
+
const result = await this.solve({
|
|
423
|
+
url,
|
|
424
|
+
...given,
|
|
425
|
+
method: 'altcha',
|
|
426
|
+
poll_json: 1,
|
|
427
|
+
});
|
|
428
|
+
return applyAltchaSolution(result);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Solve a Capy Puzzle captcha.
|
|
433
|
+
*
|
|
434
|
+
* `sitekey` is the site's public Capy key, conventionally prefixed `PUZZLE_`;
|
|
435
|
+
* it is sent as the `captchakey` the API documents. Pass `api_server` when the
|
|
436
|
+
* widget script points somewhere other than `https://jp.api.capy.me`.
|
|
437
|
+
*
|
|
438
|
+
* The result is not a token. It carries `captchakey`, `challengekey` and
|
|
439
|
+
* `answer`, which go into the target form's `capy_captchakey`,
|
|
440
|
+
* `capy_challengekey` and `capy_answer` fields, plus the raw answer as `code`.
|
|
441
|
+
* Submit `answer` verbatim -- it is the drag path the widget would have
|
|
442
|
+
* recorded, so trimming or re-encoding it invalidates the solve.
|
|
443
|
+
*
|
|
444
|
+
* The challenge key is single-use and short-lived, so submit promptly rather
|
|
445
|
+
* than caching the three values for a later request.
|
|
446
|
+
*/
|
|
447
|
+
async capy(sitekey, url, options = {}) {
|
|
448
|
+
// An unset optional is dropped rather than sent as undefined, so
|
|
449
|
+
// `capy(key, url, { apiServer: undefined })` behaves as if it were omitted.
|
|
450
|
+
const given = {};
|
|
451
|
+
for (const [key, value] of Object.entries(options)) {
|
|
452
|
+
if (value !== undefined && value !== null) {
|
|
453
|
+
given[key] = value;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// A Capy solve is one HTTP fetch plus pixel math, not a browser session, so
|
|
458
|
+
// it keeps the default timeout. It is held back to roughly two seconds
|
|
459
|
+
// before the answer is released -- Capy refuses answers that arrive faster
|
|
460
|
+
// than a human could have produced them -- which the default absorbs.
|
|
461
|
+
const result = await this.solve({
|
|
462
|
+
captchakey: sitekey,
|
|
463
|
+
url,
|
|
464
|
+
...given,
|
|
465
|
+
method: 'capy',
|
|
466
|
+
poll_json: 1,
|
|
467
|
+
});
|
|
468
|
+
return applyCapySolution(result);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Solve a CaptchaFox challenge.
|
|
473
|
+
*
|
|
474
|
+
* `sitekey` is the public key the widget renders with, conventionally prefixed
|
|
475
|
+
* `sk_`, and `url` has to be the page the widget actually runs on: CaptchaFox
|
|
476
|
+
* checks it against the domains the key is registered for and refuses a
|
|
477
|
+
* mismatch permanently rather than intermittently.
|
|
478
|
+
*
|
|
479
|
+
* Pass `api_server` only when the target page does not load the default
|
|
480
|
+
* widget. A page loading the MAM package expects a `MAM_` prefixed token, and
|
|
481
|
+
* sending the wrong source still succeeds -- it just returns a token in a
|
|
482
|
+
* format the site will not accept, which reads as a silent verification
|
|
483
|
+
* failure rather than an error.
|
|
484
|
+
*
|
|
485
|
+
* The result carries the token as both `code` and `token`, for the form's
|
|
486
|
+
* `cf-captcha-response` field, and `userAgent` when the solve reported one.
|
|
487
|
+
* That User-Agent is the browser's own, not any you sent, so submit the token
|
|
488
|
+
* under it.
|
|
489
|
+
*/
|
|
490
|
+
async captchafox(sitekey, url, options = {}) {
|
|
491
|
+
const given = {};
|
|
492
|
+
for (const [key, value] of Object.entries(options)) {
|
|
493
|
+
if (value !== undefined && value !== null) {
|
|
494
|
+
given[key] = value;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// A real browser session, like reCAPTCHA and GeeTest, and longer again when
|
|
499
|
+
// an interactive challenge is drawn -- so it gets the longer of the two
|
|
500
|
+
// timeouts unless the caller asked for a specific one.
|
|
501
|
+
const result = await this.solve({
|
|
502
|
+
timeout: this.recaptchaTimeout,
|
|
503
|
+
sitekey,
|
|
504
|
+
url,
|
|
505
|
+
...given,
|
|
506
|
+
method: 'captchafox',
|
|
507
|
+
poll_json: 1,
|
|
508
|
+
});
|
|
509
|
+
return applyTokenSolution(result);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Solve a Friendly Captcha proof-of-work challenge.
|
|
514
|
+
*
|
|
515
|
+
* Two different protocols ship under this name and a sitekey does not tell you
|
|
516
|
+
* which one a site uses, so say which: pass `version: 'v1'` or `version: 'v2'`,
|
|
517
|
+
* or pass `moduleScript` with the src of the widget's `type="module"` script
|
|
518
|
+
* tag and let CapSkip read the version off the build the site actually loads.
|
|
519
|
+
* With neither, v1 is assumed. Solving the wrong version returns a well-formed
|
|
520
|
+
* token the target site rejects, with nothing to indicate the version was the
|
|
521
|
+
* problem.
|
|
522
|
+
*
|
|
523
|
+
* Pass `api_server: 'eu'` for a sitekey on the EU data-residency tenant; both
|
|
524
|
+
* tenants mint a token for the same sitekey, so the wrong one is only caught
|
|
525
|
+
* by the site's own verification.
|
|
526
|
+
*
|
|
527
|
+
* The result carries the token as both `code` and `token`. It goes into
|
|
528
|
+
* `frc-captcha-solution` on v1 and `frc-captcha-response` on v2 -- the field
|
|
529
|
+
* names differ, which is what catches an integration moved from one to the
|
|
530
|
+
* other. A v2 token is roughly six kilobytes, so size whatever carries it
|
|
531
|
+
* accordingly.
|
|
532
|
+
*/
|
|
533
|
+
async friendlyCaptcha(sitekey, url, options = {}) {
|
|
534
|
+
const given = {};
|
|
535
|
+
for (const [key, value] of Object.entries(options)) {
|
|
536
|
+
if (value !== undefined && value !== null) {
|
|
537
|
+
given[key] = value;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// Proof-of-work, but not the millisecond kind ALTCHA does: the service sets
|
|
542
|
+
// the difficulty per request and raises it for addresses it has seen a lot
|
|
543
|
+
// of, and v2 always solves in a browser. Both make solve time variable
|
|
544
|
+
// enough to want the longer timeout.
|
|
545
|
+
const result = await this.solve({
|
|
546
|
+
timeout: this.recaptchaTimeout,
|
|
547
|
+
sitekey,
|
|
548
|
+
url,
|
|
549
|
+
...given,
|
|
550
|
+
method: 'friendly_captcha',
|
|
551
|
+
poll_json: 1,
|
|
552
|
+
});
|
|
553
|
+
return applyTokenSolution(result);
|
|
554
|
+
}
|
|
555
|
+
|
|
233
556
|
async solve(options = {}) {
|
|
234
557
|
const {
|
|
235
558
|
timeout = 0,
|
|
@@ -318,6 +641,18 @@ class CapSkip {
|
|
|
318
641
|
if (method === 'geetest') {
|
|
319
642
|
return prepareSubmitParams(params, 'geetest');
|
|
320
643
|
}
|
|
644
|
+
if (method === 'altcha') {
|
|
645
|
+
return prepareSubmitParams(params, 'altcha');
|
|
646
|
+
}
|
|
647
|
+
if (method === 'capy') {
|
|
648
|
+
return prepareSubmitParams(params, 'capy');
|
|
649
|
+
}
|
|
650
|
+
if (method === 'captchafox') {
|
|
651
|
+
return prepareSubmitParams(params, 'captchafox');
|
|
652
|
+
}
|
|
653
|
+
if (method === 'friendly_captcha') {
|
|
654
|
+
return prepareSubmitParams(params, 'friendly_captcha');
|
|
655
|
+
}
|
|
321
656
|
return applyProxy(applyParamAliases(params));
|
|
322
657
|
}
|
|
323
658
|
}
|
|
@@ -330,4 +665,7 @@ module.exports = {
|
|
|
330
665
|
parseSubmitResponse,
|
|
331
666
|
applyPollResult,
|
|
332
667
|
applyGeetestSolution,
|
|
668
|
+
applyAltchaSolution,
|
|
669
|
+
applyCapySolution,
|
|
670
|
+
applyTokenSolution,
|
|
333
671
|
};
|
package/types/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Type definitions for the CapSkip Node.js SDK.
|
|
2
2
|
|
|
3
|
-
/** Proxy passed to
|
|
3
|
+
/** Proxy passed to any solve except image captcha. */
|
|
4
4
|
export interface Proxy {
|
|
5
5
|
/** Proxy type: `HTTP`, `HTTPS`, `SOCKS5`, or `SOCKS5H`. */
|
|
6
6
|
type: string;
|
|
@@ -18,7 +18,11 @@ export interface CapSkipOptions {
|
|
|
18
18
|
port?: number;
|
|
19
19
|
/** Seconds to poll an image captcha before timing out. Default `120`. */
|
|
20
20
|
defaultTimeout?: number;
|
|
21
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Seconds to poll reCAPTCHA / Turnstile / GeeTest / CaptchaFox / Friendly
|
|
23
|
+
* Captcha before timing out. Default `300`. ALTCHA and Capy use
|
|
24
|
+
* `defaultTimeout` instead — neither is a browser solve.
|
|
25
|
+
*/
|
|
22
26
|
recaptchaTimeout?: number;
|
|
23
27
|
/** Max seconds between polls; starts at `0.25` and backs off to this. Default `5`. */
|
|
24
28
|
pollingInterval?: number;
|
|
@@ -31,10 +35,16 @@ export interface SolveResult {
|
|
|
31
35
|
/**
|
|
32
36
|
* The solution: recognized text for images, a token otherwise. For GeeTest
|
|
33
37
|
* this is the raw JSON string CapSkip returns — prefer the parsed
|
|
34
|
-
* `challenge` / `validate` / `seccode` fields below.
|
|
38
|
+
* `challenge` / `validate` / `seccode` fields below. For ALTCHA it is the
|
|
39
|
+
* base64 token, also exposed as `token`. For Capy it is the raw answer
|
|
40
|
+
* object, expanded into the three fields below.
|
|
41
|
+
*/
|
|
42
|
+
code: string | Record<string, unknown>;
|
|
43
|
+
/**
|
|
44
|
+
* Turnstile and CaptchaFox — the User-Agent to use when submitting the token.
|
|
45
|
+
* For CaptchaFox this is the browser's own, not one you sent, and it is absent
|
|
46
|
+
* when the solve captured none rather than guessed.
|
|
35
47
|
*/
|
|
36
|
-
code: string;
|
|
37
|
-
/** Turnstile only — the User-Agent to use when submitting the token. */
|
|
38
48
|
userAgent?: string;
|
|
39
49
|
/** GeeTest only — the `geetest_challenge` value to post back. */
|
|
40
50
|
challenge?: string;
|
|
@@ -42,6 +52,27 @@ export interface SolveResult {
|
|
|
42
52
|
validate?: string;
|
|
43
53
|
/** GeeTest only — the `geetest_seccode` value to post back. */
|
|
44
54
|
seccode?: string;
|
|
55
|
+
/**
|
|
56
|
+
* ALTCHA only — the base64 payload to post back in the site's `altcha` form
|
|
57
|
+
* field. The same string as `code`, named for where it goes.
|
|
58
|
+
*/
|
|
59
|
+
token?: string;
|
|
60
|
+
/** ALTCHA only — the counter that solved the challenge. */
|
|
61
|
+
number?: number;
|
|
62
|
+
/** Capy only — the value for the target form's `capy_captchakey` field. */
|
|
63
|
+
captchakey?: string;
|
|
64
|
+
/** Capy only — the value for the target form's `capy_challengekey` field. */
|
|
65
|
+
challengekey?: string;
|
|
66
|
+
/**
|
|
67
|
+
* Capy only — the value for the target form's `capy_answer` field. The drag
|
|
68
|
+
* path the widget would have recorded; submit it verbatim.
|
|
69
|
+
*/
|
|
70
|
+
answer?: string;
|
|
71
|
+
/**
|
|
72
|
+
* Capy only — empty for a puzzle solve. Present for shape compatibility with
|
|
73
|
+
* 2Captcha's documented response; it carries nothing.
|
|
74
|
+
*/
|
|
75
|
+
respKey?: string;
|
|
45
76
|
}
|
|
46
77
|
|
|
47
78
|
/** Extra options for {@link CapSkip.normal}. */
|
|
@@ -111,6 +142,103 @@ export interface GeetestOptions {
|
|
|
111
142
|
[key: string]: unknown;
|
|
112
143
|
}
|
|
113
144
|
|
|
145
|
+
/** Extra options for {@link CapSkip.altcha}. */
|
|
146
|
+
export interface AltchaOptions {
|
|
147
|
+
/** Endpoint CapSkip fetches the challenge from. */
|
|
148
|
+
challengeUrl?: string;
|
|
149
|
+
/** Endpoint CapSkip fetches the challenge from. */
|
|
150
|
+
challenge_url?: string;
|
|
151
|
+
/**
|
|
152
|
+
* The challenge document itself. An object is serialized for you; a string is
|
|
153
|
+
* sent as-is.
|
|
154
|
+
*/
|
|
155
|
+
challengeJson?: string | Record<string, unknown>;
|
|
156
|
+
/** The challenge document itself. */
|
|
157
|
+
challenge_json?: string | Record<string, unknown>;
|
|
158
|
+
/** `1` to request the raw JSON response from CapSkip. */
|
|
159
|
+
json?: number;
|
|
160
|
+
/** Proxy — used only for the `challengeUrl` fetch, never for the solve. */
|
|
161
|
+
proxy?: Proxy | string;
|
|
162
|
+
/** Proxy type when `proxy` is a bare string. */
|
|
163
|
+
proxytype?: string;
|
|
164
|
+
[key: string]: unknown;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Extra options for {@link CapSkip.capy}. */
|
|
168
|
+
export interface CapyOptions {
|
|
169
|
+
/** Root of the Capy API the key lives behind (alias for `api_server`). */
|
|
170
|
+
apiServer?: string;
|
|
171
|
+
/** Root of the Capy API the key lives behind. Default `https://jp.api.capy.me`. */
|
|
172
|
+
api_server?: string;
|
|
173
|
+
/**
|
|
174
|
+
* The challenge family. Only `"puzzle"` (the default) is solved — `"avatar"`
|
|
175
|
+
* is refused before the request is made.
|
|
176
|
+
*/
|
|
177
|
+
version?: string;
|
|
178
|
+
/** User-Agent to send with the puzzle request (alias for `useragent`). */
|
|
179
|
+
userAgent?: string;
|
|
180
|
+
/** User-Agent to send with the puzzle request. */
|
|
181
|
+
useragent?: string;
|
|
182
|
+
/** `1` to request the raw JSON response from CapSkip. */
|
|
183
|
+
json?: number;
|
|
184
|
+
/** Proxy — used for the puzzle-image fetch, the only request a Capy solve makes. */
|
|
185
|
+
proxy?: Proxy | string;
|
|
186
|
+
/** Proxy type when `proxy` is a bare string. */
|
|
187
|
+
proxytype?: string;
|
|
188
|
+
[key: string]: unknown;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** Extra options for {@link CapSkip.captchafox}. */
|
|
192
|
+
export interface CaptchaFoxOptions {
|
|
193
|
+
/** Widget entry point (alias for `api_server`). */
|
|
194
|
+
apiServer?: string;
|
|
195
|
+
/**
|
|
196
|
+
* Widget entry point. Default `https://cdn.captchafox.com/`. The MAM package
|
|
197
|
+
* path returns a `MAM_` prefixed token instead.
|
|
198
|
+
*/
|
|
199
|
+
api_server?: string;
|
|
200
|
+
/** Accepted for compatibility and not applied (alias for `useragent`). */
|
|
201
|
+
userAgent?: string;
|
|
202
|
+
/** Accepted for compatibility and not applied — CapSkip uses its own browser. */
|
|
203
|
+
useragent?: string;
|
|
204
|
+
/** `1` to request the raw JSON response from CapSkip. */
|
|
205
|
+
json?: number;
|
|
206
|
+
/** Proxy used for solving. */
|
|
207
|
+
proxy?: Proxy | string;
|
|
208
|
+
/** Proxy type when `proxy` is a bare string. */
|
|
209
|
+
proxytype?: string;
|
|
210
|
+
[key: string]: unknown;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** Extra options for {@link CapSkip.friendlyCaptcha}. */
|
|
214
|
+
export interface FriendlyCaptchaOptions {
|
|
215
|
+
/** Protocol version: `"v1"` (default) or `"v2"`; a bare `1` or `2` works too. */
|
|
216
|
+
version?: string | number;
|
|
217
|
+
/** `src` of the widget script tag carrying `type="module"` (alias). */
|
|
218
|
+
moduleScript?: string;
|
|
219
|
+
/** `src` of the widget script tag carrying `type="module"`. */
|
|
220
|
+
module_script?: string;
|
|
221
|
+
/** `src` of the widget script tag carrying `nomodule` (alias). */
|
|
222
|
+
nomoduleScript?: string;
|
|
223
|
+
/** `src` of the widget script tag carrying `nomodule`. */
|
|
224
|
+
nomodule_script?: string;
|
|
225
|
+
/** Data residency tenant (alias for `api_server`). */
|
|
226
|
+
apiServer?: string;
|
|
227
|
+
/** Data residency tenant: `"global"` (default), `"eu"`, or a full URL. */
|
|
228
|
+
api_server?: string;
|
|
229
|
+
/** User-Agent to send with the request (alias for `useragent`). */
|
|
230
|
+
userAgent?: string;
|
|
231
|
+
/** User-Agent to send with the request. */
|
|
232
|
+
useragent?: string;
|
|
233
|
+
/** `1` to request the raw JSON response from CapSkip. */
|
|
234
|
+
json?: number;
|
|
235
|
+
/** Proxy used for solving. */
|
|
236
|
+
proxy?: Proxy | string;
|
|
237
|
+
/** Proxy type when `proxy` is a bare string. */
|
|
238
|
+
proxytype?: string;
|
|
239
|
+
[key: string]: unknown;
|
|
240
|
+
}
|
|
241
|
+
|
|
114
242
|
/** Options for the {@link CapSkip.solve} manual workflow. */
|
|
115
243
|
export interface SolveOptions {
|
|
116
244
|
/** Poll timeout in seconds (falls back to the configured default). */
|
|
@@ -150,6 +278,48 @@ export class CapSkip {
|
|
|
150
278
|
url: string,
|
|
151
279
|
options?: GeetestOptions,
|
|
152
280
|
): Promise<SolveResult>;
|
|
281
|
+
/**
|
|
282
|
+
* Solve an ALTCHA proof-of-work challenge.
|
|
283
|
+
*
|
|
284
|
+
* Pass `challengeUrl` for CapSkip to fetch the challenge, or `challengeJson`
|
|
285
|
+
* with the document itself. Sending both is allowed — the inline document
|
|
286
|
+
* wins. Challenges expire fast, so fetch one immediately before calling.
|
|
287
|
+
*/
|
|
288
|
+
altcha(url: string, options?: AltchaOptions): Promise<SolveResult>;
|
|
289
|
+
/**
|
|
290
|
+
* Solve a Capy Puzzle captcha.
|
|
291
|
+
*
|
|
292
|
+
* `sitekey` is the site's public Capy key, conventionally prefixed `PUZZLE_`,
|
|
293
|
+
* and is sent as the `captchakey` the API documents. The result is not a
|
|
294
|
+
* token: it carries `captchakey`, `challengekey` and `answer`, all three of
|
|
295
|
+
* which go into the target form.
|
|
296
|
+
*/
|
|
297
|
+
capy(sitekey: string, url: string, options?: CapyOptions): Promise<SolveResult>;
|
|
298
|
+
/**
|
|
299
|
+
* Solve a CaptchaFox challenge.
|
|
300
|
+
*
|
|
301
|
+
* `url` has to be the page the widget actually runs on — CaptchaFox checks it
|
|
302
|
+
* against the domains the key is registered for. The result carries `token`
|
|
303
|
+
* for the form's `cf-captcha-response` field.
|
|
304
|
+
*/
|
|
305
|
+
captchafox(
|
|
306
|
+
sitekey: string,
|
|
307
|
+
url: string,
|
|
308
|
+
options?: CaptchaFoxOptions,
|
|
309
|
+
): Promise<SolveResult>;
|
|
310
|
+
/**
|
|
311
|
+
* Solve a Friendly Captcha proof-of-work challenge.
|
|
312
|
+
*
|
|
313
|
+
* Two different protocols ship under this name and a sitekey does not say
|
|
314
|
+
* which, so pass `version`, or pass `moduleScript` and let CapSkip read it off
|
|
315
|
+
* the build the site loads. The token goes into `frc-captcha-solution` on v1
|
|
316
|
+
* or `frc-captcha-response` on v2.
|
|
317
|
+
*/
|
|
318
|
+
friendlyCaptcha(
|
|
319
|
+
sitekey: string,
|
|
320
|
+
url: string,
|
|
321
|
+
options?: FriendlyCaptchaOptions,
|
|
322
|
+
): Promise<SolveResult>;
|
|
153
323
|
/** Submit then poll to completion. Used by the higher-level solve methods. */
|
|
154
324
|
solve(options?: SolveOptions): Promise<SolveResult>;
|
|
155
325
|
/** Submit a captcha without polling; resolves to the captcha id. */
|