@itd-api/captcha 0.1.0 → 0.2.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/README.md +71 -71
- package/dist/index.cjs +47 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -15
- package/dist/index.d.ts +49 -15
- package/dist/index.js +47 -21
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @itd-api/captcha
|
|
2
2
|
|
|
3
|
-
Получает токены собственной капчи ИТД и Cloudflare Turnstile для входа через [`itd-api`](https://github.com/KiowDev/itd-api).
|
|
3
|
+
Получает токены собственной капчи ИТД и Cloudflare Turnstile для входа через [`itd-api`](https://github.com/KiowDev/itd-api). Сервер принимает обе: виджет выбираете вы либо он сам.
|
|
4
4
|
|
|
5
5
|
[Руководство](https://kiowdev.github.io/itd-api/packages/captcha) ·
|
|
6
6
|
[API из TSDoc](https://kiowdev.github.io/itd-api/api/generated/captcha/)
|
|
@@ -9,18 +9,19 @@
|
|
|
9
9
|
|
|
10
10
|
## Когда пакет не нужен
|
|
11
11
|
|
|
12
|
-
Капча участвует
|
|
13
|
-
ни событийные соединения её не требуют. Пакет
|
|
12
|
+
Капча участвует во входе по паролю, регистрации, сбросе пароля и подтверждении QR-входа —
|
|
13
|
+
ни продление сессии, ни обычные запросы, ни событийные соединения её не требуют. Пакет
|
|
14
|
+
незачем ставить, если:
|
|
14
15
|
|
|
15
16
|
- сессия уже сохранена в `FileTokenStorage` — клиент продлевает её сам;
|
|
16
17
|
- токены можно [скопировать из браузера](https://kiowdev.github.io/itd-api/authentication/#токены-из-браузера), где вы уже вошли: DevTools отдают и access token, и cookie `refresh_token`;
|
|
17
18
|
- access token выдаёт серверное приложение или хранилище секретов — тогда подойдёт `auth: { getToken }`;
|
|
18
|
-
- токен капчи приходит из своего источника — `
|
|
19
|
+
- токен капчи приходит из своего источника — опция клиента `captcha` принимает любую функцию.
|
|
19
20
|
|
|
20
21
|
## Установка
|
|
21
22
|
|
|
22
23
|
```sh
|
|
23
|
-
npm i @itd-api/captcha patchright
|
|
24
|
+
npm i @itd-api/captcha patchright@1.61.1
|
|
24
25
|
npx patchright install chromium
|
|
25
26
|
```
|
|
26
27
|
|
|
@@ -38,68 +39,42 @@ import { createCaptchaSolver } from '@itd-api/captcha';
|
|
|
38
39
|
|
|
39
40
|
const itd = new ItdClient({
|
|
40
41
|
storage: new FileTokenStorage('./.itd-session.json'),
|
|
41
|
-
auth: {
|
|
42
|
-
|
|
43
|
-
password: process.env.ITD_PASSWORD!,
|
|
44
|
-
captcha: createCaptchaSolver(),
|
|
45
|
-
},
|
|
42
|
+
auth: { email: process.env.ITD_EMAIL!, password: process.env.ITD_PASSWORD! },
|
|
43
|
+
captcha: createCaptchaSolver(),
|
|
46
44
|
});
|
|
47
45
|
```
|
|
48
46
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
живёт несколько минут. Браузер поднимается на время одного вызова и сразу закрывается.
|
|
47
|
+
Клиент спрашивает токен каждый раз, когда сервер требует капчу; браузер поднимается на
|
|
48
|
+
время одного вызова и сразу закрывается. Тот же источник, заданный контейнеру
|
|
49
|
+
`ItdAccounts`, достаётся каждому аккаунту.
|
|
53
50
|
|
|
54
|
-
|
|
51
|
+
### Какую капчу проходить
|
|
55
52
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const token = await solveCaptcha(CaptchaType.Itd);
|
|
60
|
-
```
|
|
53
|
+
Сервер принимает обе. Без настройки источник следует за сервером: клиент спрашивает
|
|
54
|
+
активного провайдера и просит пройти именно его виджет, так что переключение провайдера
|
|
55
|
+
переживается без правок кода.
|
|
61
56
|
|
|
62
|
-
|
|
57
|
+
Нужен конкретный виджет — назовите его, и лишнего запроса к серверу не будет:
|
|
63
58
|
|
|
64
59
|
```ts
|
|
65
|
-
import {
|
|
60
|
+
import { createCaptchaSolver, CaptchaType } from '@itd-api/captcha';
|
|
66
61
|
|
|
67
|
-
|
|
68
|
-
|
|
62
|
+
createCaptchaSolver({ type: CaptchaType.Cloudflare }); // всегда Turnstile
|
|
63
|
+
createCaptchaSolver({ type: CaptchaType.Itd }); // всегда капча ИТД
|
|
69
64
|
```
|
|
70
65
|
|
|
71
|
-
|
|
66
|
+
Незнакомый тип отвергается при создании, а не в момент входа.
|
|
67
|
+
|
|
68
|
+
Имя поля, в котором сервер ждёт токен, клиент при закреплённом типе берёт из своей таблицы.
|
|
69
|
+
Если сервер его переименовал, назовите поле сами: `createCaptchaSolver({ type, field: 'c7f2' })`.
|
|
72
70
|
|
|
73
|
-
|
|
74
|
-
навигации, ожидание, клик по чекбоксу и повторы одинаковы для всех виджетов и уже сделаны;
|
|
75
|
-
описать нужно только то, чем виджет отличается:
|
|
71
|
+
### Один токен без клиента
|
|
76
72
|
|
|
77
73
|
```ts
|
|
78
|
-
import { solveCaptcha,
|
|
79
|
-
|
|
80
|
-
const myCaptcha: CaptchaHandler = {
|
|
81
|
-
type: 'my-captcha',
|
|
82
|
-
label: 'моей капчи',
|
|
83
|
-
// Страница отдаётся вместо настоящей по адресу из `origin`, поэтому виджет видит верный домен.
|
|
84
|
-
buildPage: ({ theme }) => `<!doctype html>…<div id="widget" data-theme="${theme}"></div>…`,
|
|
85
|
-
// По этому селектору видно, что виджет отрисовался.
|
|
86
|
-
widgetReadySelector: '#widget',
|
|
87
|
-
// Насколько правее левого края контейнера чекбокс: клик идёт по координатам.
|
|
88
|
-
checkboxOffsetX: 30,
|
|
89
|
-
readState: (page) =>
|
|
90
|
-
page.evaluate(() => {
|
|
91
|
-
const field = document.querySelector('#my-token') as { value?: string } | null;
|
|
92
|
-
return { token: field?.value || null, error: null };
|
|
93
|
-
}),
|
|
94
|
-
// Повтор не поможет — например, ключ не разрешён для домена.
|
|
95
|
-
isPermanentWidgetError: (code) => code === 'domain-mismatch',
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
const token = await solveCaptcha(myCaptcha);
|
|
99
|
-
```
|
|
74
|
+
import { solveCaptcha, CaptchaType } from '@itd-api/captcha';
|
|
100
75
|
|
|
101
|
-
|
|
102
|
-
|
|
76
|
+
const token = await solveCaptcha(CaptchaType.Itd);
|
|
77
|
+
```
|
|
103
78
|
|
|
104
79
|
## Запуск на сервере
|
|
105
80
|
|
|
@@ -155,20 +130,12 @@ CDP), а не программной установкой значения, —
|
|
|
155
130
|
| `browser` | — | Готовый браузер. Тогда пакет его не запускает и не закрывает. |
|
|
156
131
|
| `launch` | — | Свой запуск браузера. Заменяет все параметры запуска. |
|
|
157
132
|
| `contextOptions` | `locale: 'ru-RU'`, окно 1280×800 | Настройки контекста. Заменяют стандартные целиком. |
|
|
158
|
-
| `logger` |
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
в его фабрике:
|
|
162
|
-
|
|
163
|
-
| Фабрика | Параметр | По умолчанию | Что делает |
|
|
164
|
-
| --- | --- | --- | --- |
|
|
165
|
-
| `itdCaptcha` | `sitekey` | ключ итд.com | Публичный ключ виджета. |
|
|
166
|
-
| `itdCaptcha` | `action` | `'login'` | Назначение токена: `login`, `register`, `password_reset`. |
|
|
167
|
-
| `itdCaptcha` | `captchaOrigin` | `https://captcha.xn--d1ah4a.com` | Базовый адрес виджета. |
|
|
168
|
-
| `turnstile` | `sitekey` | ключ итд.com | Публичный ключ виджета. |
|
|
133
|
+
| `logger` | логгер клиента | Куда писать ход решения: объект вида `console`. `createCaptchaSolver` без него пишет в логгер `itd-api`. |
|
|
134
|
+
| `type` | — | Какую капчу проходить всегда. Только для `createCaptchaSolver`. |
|
|
135
|
+
| `field` | — | Поле тела запроса для закреплённого типа. Только вместе с `type`. |
|
|
169
136
|
|
|
170
137
|
```ts
|
|
171
|
-
|
|
138
|
+
createCaptchaSolver({ type: CaptchaType.Itd, timeout: 90_000, headless: true });
|
|
172
139
|
```
|
|
173
140
|
|
|
174
141
|
Свой драйвер:
|
|
@@ -212,19 +179,52 @@ createCaptchaSolver({
|
|
|
212
179
|
|
|
213
180
|
## Совместимость
|
|
214
181
|
|
|
215
|
-
|
|
216
|
-
|
|
182
|
+
Ниже — связки, на которых Cloudflare Turnstile выдал токен. Проверено 19.09.2026
|
|
183
|
+
с окном, если не указано иное.
|
|
184
|
+
|
|
185
|
+
| Драйвер | Версия и конфигурация | Браузер |
|
|
186
|
+
|---|---|---|
|
|
187
|
+
| `patchright` | 1.59.4 · 1.60.2 · 1.61.1, штатные сборки | Chromium 147 · 148 · 149 соответственно |
|
|
188
|
+
| | 1.62.3 · 1.63.0, `executablePath` | Chromium 149 |
|
|
189
|
+
| | 1.63.0, `executablePath` | Chromium 148 |
|
|
190
|
+
| `playwright` | 1.60.0 · 1.61.0, штатные сборки | Chromium 148 · 149 соответственно |
|
|
191
|
+
| | 1.62.1 · 1.63.0, `executablePath` | Chromium 149 |
|
|
192
|
+
| `playwright-core` | 1.62.1 · 1.63.0, `executablePath` | Chromium 149 |
|
|
193
|
+
| `camoufox-js` | 0.11.5 с окном · 0.12.0 с окном и headless, `contextOptions: {}` | Camoufox 152.0.4-beta.29 |
|
|
194
|
+
| `rebrowser-playwright` | 1.48.2 · 1.49.1, `executablePath` | Chromium 149 |
|
|
195
|
+
|
|
196
|
+
<details>
|
|
197
|
+
<summary>Комбинации, не прошедшие проверку</summary>
|
|
198
|
+
|
|
199
|
+
| Драйвер | Версия и конфигурация | Браузер | Результат |
|
|
200
|
+
|---|---|---|---|
|
|
201
|
+
| `patchright` | 1.62.3, штатная сборка | Chromium 151 | таймаут |
|
|
202
|
+
| | 1.63.0, штатная сборка | Chrome for Testing 153 | таймаут |
|
|
203
|
+
| | 1.63.0, `executablePath` | Google Chrome 152 | таймаут |
|
|
204
|
+
| `playwright` | 1.62.1, штатная сборка | Chromium 151 | `600010`, таймаут |
|
|
205
|
+
| | 1.63.0, штатная сборка | Chrome for Testing 153 | `600010`, таймаут |
|
|
206
|
+
| | 1.63.0, `executablePath` | Google Chrome 152 | `600010`, таймаут |
|
|
207
|
+
| `rebrowser-playwright` | 1.52.0, `executablePath` | Chromium 149 | `600010`, таймаут |
|
|
208
|
+
| `playwright-extra` + `puppeteer-extra-plugin-stealth` | 4.3.6 + 2.11.2, `playwright` 1.62.1 · 1.63.0, `executablePath` | Chromium 149 | `600010`, таймаут |
|
|
209
|
+
|
|
210
|
+
</details>
|
|
211
|
+
|
|
212
|
+
Собственная капча ИТД менее требовательна: ее можно получить любым подходящим драйвером и версией браузера.
|
|
213
|
+
|
|
214
|
+
Таблица проверяется скриптом `scripts/drivers.mjs` из исходников пакета:
|
|
217
215
|
|
|
218
216
|
```sh
|
|
219
|
-
npm i --no-save patchright playwright camoufox-js
|
|
217
|
+
npm i --no-save patchright playwright camoufox-js rebrowser-playwright \
|
|
218
|
+
playwright-extra puppeteer-extra-plugin-stealth
|
|
220
219
|
node scripts/drivers.mjs # Turnstile
|
|
221
220
|
node scripts/drivers.mjs --itd # капча ИТД
|
|
222
221
|
```
|
|
223
222
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
223
|
+
Штатная сборка приезжает вместе с версией драйвера. Другую можно поставить командой
|
|
224
|
+
`npx @puppeteer/browsers install chrome@149.0.7827.55` и передать путь в `executablePath`.
|
|
225
|
+
Версия драйвера сама по себе не гарантирует результат: например, `patchright` и
|
|
226
|
+
`playwright` 1.63.0 не проходят Turnstile на штатном Chrome for Testing 153, но проходят
|
|
227
|
+
на Chromium 149 через `executablePath`.
|
|
228
228
|
|
|
229
229
|
## Лицензия
|
|
230
230
|
|
package/dist/index.cjs
CHANGED
|
@@ -54,9 +54,8 @@ const DEFAULT_ARGS = ["--disable-dev-shm-usage", "--disable-blink-features=Autom
|
|
|
54
54
|
/**
|
|
55
55
|
* Драйверы в порядке предпочтения.
|
|
56
56
|
*
|
|
57
|
-
* `patchright` впереди намеренно:
|
|
58
|
-
*
|
|
59
|
-
* ничего не меняется — берётся он.
|
|
57
|
+
* `patchright` впереди намеренно: проверенная связка `patchright@1.61.1` со штатным
|
|
58
|
+
* Chromium 149 проходит виджет. Если стоит только `playwright`, берётся он.
|
|
60
59
|
*/
|
|
61
60
|
const DRIVERS = [
|
|
62
61
|
"patchright",
|
|
@@ -101,7 +100,7 @@ async function loadDriver(requested) {
|
|
|
101
100
|
} catch (error) {
|
|
102
101
|
if (!isModuleNotFound(error)) throw error;
|
|
103
102
|
}
|
|
104
|
-
throw new CaptchaError(CaptchaFailure.DriverMissing, requested ? `Драйвер ${requested} не установлен.` : "Не найден драйвер браузера. Установите
|
|
103
|
+
throw new CaptchaError(CaptchaFailure.DriverMissing, requested ? `Драйвер ${requested} не установлен.` : "Не найден драйвер браузера. Установите проверенную связку командой: npm i patchright@1.61.1 && npx patchright install chromium. Либо передайте свой запуск браузера через параметр launch.");
|
|
105
104
|
}
|
|
106
105
|
/** Собирает параметры запуска, не поднимая браузер. @internal */
|
|
107
106
|
function resolveLaunchOptions(options, driver = "") {
|
|
@@ -140,6 +139,12 @@ async function launchBrowser(options) {
|
|
|
140
139
|
//#region src/options.ts
|
|
141
140
|
/** Базовый URL сайта итд.com. Домен записан в punycode: `итд.com`. */
|
|
142
141
|
const DEFAULT_ORIGIN = "https://xn--d1ah4a.com";
|
|
142
|
+
const LOGGER_METHODS = [
|
|
143
|
+
"debug",
|
|
144
|
+
"info",
|
|
145
|
+
"warn",
|
|
146
|
+
"error"
|
|
147
|
+
];
|
|
143
148
|
/**
|
|
144
149
|
* Приводит адрес к корню и проверяет его пригодность.
|
|
145
150
|
*
|
|
@@ -172,7 +177,10 @@ function resolveOptions(options) {
|
|
|
172
177
|
if (theme !== "auto" && theme !== "light" && theme !== "dark") throw new TypeError("theme должен быть 'auto', 'light' или 'dark'");
|
|
173
178
|
for (const [name, value] of [["headless", options.headless], ["disableSandbox", options.disableSandbox]]) if (value !== void 0 && typeof value !== "boolean") throw new TypeError(`${name} должен быть boolean`);
|
|
174
179
|
for (const [name, value] of [["driver", options.driver], ["channel", options.channel]]) if (value !== void 0 && (typeof value !== "string" || value.trim() === "")) throw new TypeError(`${name} должен быть непустой строкой`);
|
|
175
|
-
if (options.logger !== void 0
|
|
180
|
+
if (options.logger !== void 0) {
|
|
181
|
+
if (typeof options.logger !== "object" || options.logger === null) throw new TypeError("logger должен быть объектом с методами debug, info, warn и error");
|
|
182
|
+
for (const method of LOGGER_METHODS) if (typeof options.logger[method] !== "function") throw new TypeError(`logger.${method} должен быть функцией`);
|
|
183
|
+
}
|
|
176
184
|
if (options.contextOptions !== void 0 && (typeof options.contextOptions !== "object" || options.contextOptions === null || Array.isArray(options.contextOptions))) throw new TypeError("contextOptions должен быть объектом");
|
|
177
185
|
if (options.launch !== void 0 && typeof options.launch !== "function") throw new TypeError("launch должен быть функцией");
|
|
178
186
|
if (options.args !== void 0 && (!Array.isArray(options.args) || options.args.some((argument) => typeof argument !== "string"))) throw new TypeError("args должен быть массивом строк");
|
|
@@ -521,7 +529,8 @@ async function clickCheckbox(page, offsetX) {
|
|
|
521
529
|
}
|
|
522
530
|
/** Ждёт токен, периодически подталкивая виджет кликом. */
|
|
523
531
|
async function waitForToken(page, handler, options, browser) {
|
|
524
|
-
const
|
|
532
|
+
const startedAt = Date.now();
|
|
533
|
+
const deadline = startedAt + options.timeout;
|
|
525
534
|
await page.waitForSelector(handler.widgetReadySelector, {
|
|
526
535
|
timeout: Math.min(WIDGET_APPEAR_TIMEOUT, options.timeout),
|
|
527
536
|
state: "attached"
|
|
@@ -531,7 +540,7 @@ async function waitForToken(page, handler, options, browser) {
|
|
|
531
540
|
while (Date.now() < deadline) {
|
|
532
541
|
const state = await handler.readState(page);
|
|
533
542
|
if (state.token) {
|
|
534
|
-
options.logger?.(
|
|
543
|
+
options.logger?.info(`токен ${handler.label} получен за ${Date.now() - startedAt} мс`);
|
|
535
544
|
return state.token;
|
|
536
545
|
}
|
|
537
546
|
if (state.error && state.error !== lastError) {
|
|
@@ -540,10 +549,10 @@ async function waitForToken(page, handler, options, browser) {
|
|
|
540
549
|
widgetCode: state.error
|
|
541
550
|
});
|
|
542
551
|
lastError = state.error;
|
|
543
|
-
options.logger?.(`виджет сообщил об ошибке ${state.error}, ждём повтора`);
|
|
552
|
+
options.logger?.debug(`виджет ${handler.label} сообщил об ошибке ${state.error}, ждём повтора`);
|
|
544
553
|
}
|
|
545
554
|
if (Date.now() >= nextClickAt && await clickCheckbox(page, handler.checkboxOffsetX)) {
|
|
546
|
-
options.logger?.(
|
|
555
|
+
options.logger?.debug(`клик по чекбоксу ${handler.label}`);
|
|
547
556
|
nextClickAt = Date.now() + CLICK_INTERVAL;
|
|
548
557
|
}
|
|
549
558
|
await sleep(POLL_INTERVAL);
|
|
@@ -609,16 +618,17 @@ function toCaptchaError(error, handler) {
|
|
|
609
618
|
* @throws {CaptchaError} если токен получить не удалось
|
|
610
619
|
*/
|
|
611
620
|
async function runSolver(handler, options) {
|
|
612
|
-
const browser = options.browser ?? await launchBrowser(options);
|
|
613
621
|
const owned = options.browser === void 0;
|
|
622
|
+
if (owned) options.logger?.info(`запуск браузера для ${handler.label}`);
|
|
623
|
+
const browser = options.browser ?? await launchBrowser(options);
|
|
614
624
|
try {
|
|
615
625
|
for (let attempt = 1; attempt <= options.attempts; attempt++) try {
|
|
616
|
-
options.logger?.(`попытка ${attempt} из ${options.attempts}`);
|
|
626
|
+
options.logger?.debug(`попытка ${attempt} из ${options.attempts} для ${handler.label}`);
|
|
617
627
|
return await solveOnce(browser, handler, options);
|
|
618
628
|
} catch (raw) {
|
|
619
629
|
const error = toCaptchaError(raw, handler);
|
|
620
630
|
if (isPermanent(error) || attempt === options.attempts) throw error;
|
|
621
|
-
options.logger?.(`попытка ${attempt} не
|
|
631
|
+
options.logger?.warn(`попытка ${attempt} из ${options.attempts} для ${handler.label} не удалась (${error instanceof Error ? error.message : String(error)}), повтор`);
|
|
622
632
|
}
|
|
623
633
|
throw new CaptchaError(CaptchaFailure.Timeout, `Токен ${handler.label} получить не удалось`, { type: handler.type });
|
|
624
634
|
} finally {
|
|
@@ -645,30 +655,46 @@ async function runSolver(handler, options) {
|
|
|
645
655
|
async function solveCaptcha(target, options = {}) {
|
|
646
656
|
return runSolver(resolveHandler(target), resolveOptions(options));
|
|
647
657
|
}
|
|
658
|
+
/** Без своего логгера источник пишет ход решения в логгер клиента. */
|
|
659
|
+
function withClientLogger(options, context) {
|
|
660
|
+
if (options.logger || !context?.logger) return options;
|
|
661
|
+
return {
|
|
662
|
+
...options,
|
|
663
|
+
logger: context.logger
|
|
664
|
+
};
|
|
665
|
+
}
|
|
648
666
|
/**
|
|
649
|
-
* Собирает источник токена для клиента `itd-api`.
|
|
667
|
+
* Собирает источник токена капчи для клиента `itd-api`.
|
|
650
668
|
*
|
|
651
669
|
* Токен одноразовый и живёт несколько минут, поэтому клиент спрашивает его заново перед
|
|
652
|
-
* каждым
|
|
670
|
+
* каждым запросом, которому нужна капча. Браузер поднимается на время одного вызова
|
|
671
|
+
* и сразу закрывается.
|
|
672
|
+
*
|
|
673
|
+
* @throws {TypeError} если тип капчи незнаком или `field` задан без него
|
|
653
674
|
*
|
|
654
675
|
* @example
|
|
655
676
|
* ```ts
|
|
656
677
|
* import { ItdClient } from 'itd-api';
|
|
657
678
|
* import { FileTokenStorage } from 'itd-api/node';
|
|
658
|
-
* import { createCaptchaSolver } from '@itd-api/captcha';
|
|
679
|
+
* import { createCaptchaSolver, CaptchaType } from '@itd-api/captcha';
|
|
659
680
|
*
|
|
660
681
|
* const itd = new ItdClient({
|
|
661
682
|
* storage: new FileTokenStorage('./.itd-session.json'),
|
|
662
|
-
* auth: {
|
|
663
|
-
*
|
|
664
|
-
* password: process.env.ITD_PASSWORD!,
|
|
665
|
-
* captcha: createCaptchaSolver(),
|
|
666
|
-
* },
|
|
683
|
+
* auth: { email: process.env.ITD_EMAIL!, password: process.env.ITD_PASSWORD! },
|
|
684
|
+
* captcha: createCaptchaSolver({ type: CaptchaType.Cloudflare }),
|
|
667
685
|
* });
|
|
668
686
|
* ```
|
|
669
687
|
*/
|
|
670
688
|
function createCaptchaSolver(options = {}) {
|
|
671
|
-
|
|
689
|
+
const { type, field, ...solveOptions } = options;
|
|
690
|
+
if (type === void 0) return { getToken: (requested, context) => solveCaptcha(requested, withClientLogger(solveOptions, context)) };
|
|
691
|
+
const handler = resolveHandler(type);
|
|
692
|
+
const resolved = resolveOptions(solveOptions);
|
|
693
|
+
return {
|
|
694
|
+
type: handler.type,
|
|
695
|
+
...field === void 0 ? {} : { field },
|
|
696
|
+
getToken: (_requested, context) => runSolver(handler, withClientLogger(resolved, context))
|
|
697
|
+
};
|
|
672
698
|
}
|
|
673
699
|
//#endregion
|
|
674
700
|
exports.CaptchaError = CaptchaError;
|