Как решить reCAPTCHA в Deno: разрешения и таймауты

Чтобы решить reCAPTCHA в Deno, импортируйте Node.js SDK от CapSkip через npm-спецификатор и сами прочитайте адрес решателя из окружения. Затем запустите скрипт с флагами разрешений, в которых названы только CapSkip и ваш сайт. CapSkip работает на вашем собственном ПК с Windows, поэтому платы за каждое решение нет. Код короткий, а сложность в модели разрешений Deno. SDK сообщает о заблокированном вызове как о сетевой ошибке. Если CapSkip замолчал, скрипт зависает без ошибки и без завершения. А привычный способ добавить таймаут держит процесс запущенным до конца всего выставленного лимита. В этом руководстве разобрано каждое из этих мест.
Что понадобится
- CapSkip, запущенный на ПК с Windows, с включённым решателем reCAPTCHA. Пошаговое руководство по настройке проведёт вас через первый запуск.
- Deno 2. Всё ниже запускалось на Windows 11 с Deno 2.9.6 и ещё раз на 2.9.7, с capskip 1.3.0 из npm.
- Окно PowerShell. Команды написаны в синтаксисе PowerShell.
- Страница с чекбоксом reCAPTCHA v2 в форме. Пример выполняет вход через форму в стиле Django, в которой есть CSRF-токен. Поменяйте имена полей под свой сайт.
- Адрес решателя. В режиме Local CapSkip отвечает на 127.0.0.1 и только для этого устройства. В режиме Server он слушает ваш сетевой адрес или публичный IP, чтобы скрипт на другой машине мог обращаться к нему через API. Оба режима задаются в разделе Настройки подключения.
Замеры ниже получены на тестовой имитации CapSkip и тестовой странице входа на том же ПК, с настоящим Deno и настоящим SDK. Ошибки разрешений, график опроса и поведение при зависании относятся к Deno и SDK, поэтому верны и для вашего собственного сайта. Время решения, а также правила приёма и отклонения на странице входа относятся только к этой тестовой имитации.
Шаг 1: устанавливаем Deno и создаём проект
Установите Deno с помощью официального установщика или через winget. На странице установки Deno перечислены другие способы, например Scoop и Chocolatey.
# PowerShell: the official installer (or: winget install DenoLand.Deno) irm https://deno.land/install.ps1 | iex deno --version
Затем создайте папку проекта и положите в неё deno.json, прежде чем запускать что-либо ещё. Карта импорта фиксирует версию SDK и позволяет скрипту писать "capskip" вместо длинного npm-спецификатора.
{
"imports": {
"capskip": "npm:[email protected]"
}
}Не запускайте deno add, пока этого файла нет. Если package.json лежит в папке выше вашего проекта, а в профиле пользователя так бывает часто, Deno считает проект проектом Node. Тогда импорт npm:[email protected] завершается ошибкой Could not find "capskip" in a node_modules folder. Импорт без версии молча загружает тот capskip, который установлен выше. На тестовом ПК это была более старая версия 1.0.2. Запуск deno add без deno.json даже переписал тот родительский package.json. Файл deno.json в вашей собственной папке привязывает всё к вашему проекту.
Шаг 2: импортируем SDK и читаем настройки
Сохраните код из шагов со 2 по 5 в один файл solve.js в показанном порядке. Весь файл повторяется ближе к концу этого руководства. Deno запускает его как ES-модуль, поэтому вызов require из README SDK здесь падает с ошибкой require is not defined. Используйте именованный импорт. Импорт по умолчанию тоже не работает, потому что он отдаёт весь объект exports: new CapSkip() выбрасывает CapSkip is not a constructor.
import { CapSkip } from "capskip";
// The SDK never reads the environment, so read it here and pass it in.
const host = Deno.env.get("CAPSKIP_HOST") ?? "127.0.0.1";
const port = Number(Deno.env.get("CAPSKIP_PORT") ?? 8080);
const apiKey = Deno.env.get("CAPSKIP_API_KEY") ?? "capskip";
const solver = new CapSkip({ host, port, apiKey });
// The login page to solve, passed on the command line.
const pageUrl = Deno.args[0] ?? "https://example.com/login";SDK сам не читает переменные окружения, поэтому скрипт читает их и передаёт в конструктор. Значения по умолчанию позволяют запустить файл без настройки. CAPSKIP_API_KEY нужен, только если вы включили проверку API-ключа в CapSkip.
Шаг 3: загружаем страницу и сохраняем её cookies
У fetch в Deno нет хранилища cookies, поэтому скрипт ведёт его сам. Он читает каждую строку Set-Cookie через getSetCookie. Если вместо этого вызвать headers.get для set-cookie, строки склеятся в одну. Дата Expires в cookie содержит запятую, поэтому наивное разбиение по запятым режет cookies не в тех местах.
// Deno's fetch keeps no cookies, so collect every Set-Cookie line.
// (headers.get("set-cookie") would glue them into one string.)
const jar = new Map();
/** @param {Response} response */
function keepCookies(response) {
for (const line of response.headers.getSetCookie()) {
const [pair] = line.split(";");
const eq = pair.indexOf("=");
jar.set(pair.slice(0, eq).trim(), pair.slice(eq + 1).trim());
}
}
const cookies = () => Array.from(jar, ([k, v]) => `${k}=${v}`).join("; ");Затем скрипт загружает страницу входа и достаёт из неё sitekey и CSRF-токен. Подстройте два шаблона под разметку вашей страницы.
const page = await fetch(pageUrl);
keepCookies(page);
const html = await page.text();
// Adjust these two patterns to your page's markup.
const sitekey = html.match(/data-sitekey="([^"]+)"/)?.[1];
const csrf = html.match(/name="csrfmiddlewaretoken" value="([^"]+)"/)?.[1];
if (!sitekey || !csrf) throw new Error("sitekey or CSRF token not found");Шаг 4: просим у CapSkip токен
Всю работу делает один вызов. Он отправляет задание, опрашивает результат и возвращает токен. SDK опрашивает сразу, затем ждёт четверть секунды и каждый раз удваивает ожидание, вплоть до 5 секунд. Решение, которое CapSkip завершил за одну секунду, в тесте вернулось примерно за 1.8 секунды, как и в Node.
// code is typed "string or object" (Capy shares the field), so
// String() keeps deno check quiet.
const { code } = await solver.recaptcha(sitekey, pageUrl);
const token = String(code);В результате есть поле code, и именно оно содержит токен. Файл типов SDK объявляет его как строку или объект, потому что некоторые типы капчи возвращают в этом поле объект. String() превращает его в обычную строку. В одном из следующих разделов к этому вызову добавляется таймаут.
Шаг 5: отправляем форму и проверяем результат
Скрипт отправляет токен в поле g-recaptcha-response рядом с остальными полями формы. Важны две детали. fetch в Deno не отправляет Referer, а строгий сайт вроде Django по HTTPS отклоняет отправку формы без него, поэтому скрипт задаёт его сам. И вызов fetch использует redirect manual, чтобы вернулся ответ 302 и можно было прочитать его новую сессионную cookie. При поведении по умолчанию, когда перенаправления выполняются автоматически, cookie из ответа 302 теряется, и вход возвращается на форму с ответом 200.
// fetch sends no Referer, and a strict site rejects the post without
// one. redirect "manual" keeps the 302 so we can read its new cookie.
const response = await fetch(pageUrl, {
method: "POST",
redirect: "manual",
headers: { Cookie: cookies(), Referer: pageUrl },
body: new URLSearchParams({
csrfmiddlewaretoken: csrf,
username: "YOUR_USERNAME",
password: "YOUR_PASSWORD",
"g-recaptcha-response": token,
}),
});
keepCookies(response);Отклонённый вход часто отвечает кодом 200 и снова показывает форму. Поэтому скрипт считает сбоем любой ответ, кроме 302, а затем ищет маркер, который показывает только страница после входа. Замените Welcome back текстом со своего сайта. Скрипт, который сообщает об успехе при отклонённом входе, хуже скрипта, который падает.
// A good login answers 302 and rotates the session cookie.
if (response.status !== 302) {
const body = await response.text();
throw new Error(`login rejected: ${response.status} ${body}`);
}
const next = new URL(response.headers.get("location") ?? "/", pageUrl);
const home = await fetch(next, { headers: { Cookie: cookies() } });
// Swap "Welcome back" for a marker your own site shows after login.
const ok = (await home.text()).includes("Welcome back");
console.log(ok ? "logged in" : "NOT logged in");
if (!ok) Deno.exit(2);Шаг 6: запускаем с минимальным набором разрешений
Deno запрещает доступ к сети, окружению и файлам, пока вы его не разрешите. Чтобы решить reCAPTCHA в Deno, этому скрипту нужны две вещи, и команда ниже выдаёт ровно их.
deno run ` --allow-env=CAPSKIP_HOST,CAPSKIP_PORT,CAPSKIP_API_KEY ` --allow-net=127.0.0.1:8080,example.com ` solve.js https://example.com/login
Список env называет три переменные CAPSKIP. Список net называет адрес и порт CapSkip, а также сайт. Замените example.com и адрес страницы на свой сайт. Уберите любую одну запись, и запуск завершится ошибкой, в которой названо то, чего не хватает.
| Разрешение | Что оно разрешает | Что вы видите без него |
|---|---|---|
| env, три имени | Чтение переменных CAPSKIP | NotCapable: Requires env access to "CAPSKIP_HOST", а следом совет запустить скрипт снова с флагом env. Отказ получает и переменная, которая не задана, поэтому значения по умолчанию не помогают. |
| net, хост и порт CapSkip | Вызовы SDK к CapSkip | NetworkException с сообщением Requires net access to "127.0.0.1:8080", а следом тот же совет про флаг net. |
| net, сайт | Ваши вызовы fetch к странице и к форме | Ошибка NotCapable, в которой назван сайт. |
Несколько правил о списке net. Хост без порта разрешает все порты, поэтому example.com покрывает и 80, и 443. Запись с портом разрешает только этот порт. 127.0.0.1 и 127.0.0.2 считаются разными хостами. И перенаправление на другое имя хоста требует, чтобы и этот хост был в списке. Страница, которая перенаправляет с example.com на www.example.com, падает на втором переходе, пока вы не добавите его. Больше ничего не понадобилось: никаких разрешений read, run, sys или import.
В терминале Deno спрашивает, а не падает. Он один раз запрашивает подтверждение для каждой переменной и каждого хоста, а при отказе вы получаете те же ошибки. В задании по расписанию терминала нет, поэтому оно падает сразу. Флаг allow-all (-A) снимает все эти проверки, так что оставьте его для быстрого эксперимента.
Ту же команду можно записать в deno.json как задачу, чтобы печатать меньше. В первом блоке ниже лежит deno.json с картой импорта и задачей solve, а во втором в PowerShell задаётся окружение и запускается эта задача. Deno дописывает в конец адрес страницы, который вы передаёте после имени задачи.
{
"imports": {
"capskip": "npm:[email protected]"
},
"tasks": {
"solve": "deno run --allow-env=CAPSKIP_HOST,CAPSKIP_PORT,CAPSKIP_API_KEY --allow-net=127.0.0.1:8080,example.com solve.js"
}
}$env:CAPSKIP_HOST = "127.0.0.1" $env:CAPSKIP_PORT = "8080" deno task solve https://example.com/login
Для CAPSKIP_HOST используйте IP-адрес. Если задать имя вроде localhost, само это имя должно быть в списке net, иначе SDK падает с getaddrinfo EPERM localhost. Но и добавление его в список таит ловушку. Если CapSkip не запущен, localhost заставляет Deno попробовать IPv6-адрес [::1], которого в списке нет. Ошибка выбрасывается из таймера, а не из вызова SDK, поэтому это не NetworkException, и try и catch её не перехватывают.
Как отличить заблокированный вызов от незапущенного CapSkip
Оба случая приходят как NetworkException, поэтому обработчик, который всегда говорит "is CapSkip running?", направляет вас не туда. Разница в сообщении и в причине. У заблокированного вызова сообщение начинается с Requires net access, а причиной служит ошибка NotCapable из Deno. Если в CAPSKIP_HOST указано имя, сообщение будет getaddrinfo EPERM, а у причины вместо этого код EPERM. У незапущенного CapSkip сообщение начинается с connect ECONNREFUSED. Кроме того, в Deno на Windows такая ошибка появляется примерно через две секунды, а в Node через несколько миллисекунд. Если скрипт перед этим сообщением будто на паузе, он не завис. Функция ниже проверяет оба вида блокировки. Полный скрипт в конце вызывает её в блоке catch вокруг всего процесса.
import { ApiException, NetworkException, TimeoutException } from "capskip";
// Deno's permission errors arrive wrapped in NetworkException. The
// cause is NotCapable for an IP host, and has the code EPERM for a
// host name. Tell them apart from a CapSkip that is not running.
/** @param {any} err */
function explain(err) {
const cause = err?.cause;
if (err instanceof NetworkException) {
const denied = cause instanceof Deno.errors.NotCapable ||
cause?.code === "EPERM";
return denied
? `Deno blocked the call, add a flag: ${err.message}`
: `CapSkip is not reachable: ${err.message || cause?.code}`;
}
if (err instanceof ApiException) return `CapSkip replied: ${err.message}`;
if (err instanceof TimeoutException) return `Too slow: ${err.message}`;
return String(err?.stack ?? err);
}Как не дать молчащему CapSkip повесить скрипт
SDK не задаёт таймаут для своего соединения. Если CapSkip принял вызов и после этого никогда не отвечает, скрипт ждёт вечно. Deno ничего не выводит и не завершается. Настройка recaptchaTimeout в SDK тут не помогает. Её проверяют только между опросами, поэтому она не может сработать, пока вызов отправки или очередной опрос ждёт ответа. В Node всё происходит так же. Без какой-либо обёртки выйти можно только по Ctrl+C.
Ограничьте вызов по времени сами. Привычно для этого брать AbortSignal.timeout, но в Deno здесь он не подходит. Сигнал таймаута с обработчиком события abort удерживает процесс живым, пока не сработает таймер. Лимит в 30 секунд вокруг решения, которое заняло 1.8 секунды, заставил скрипт завершиться только через 30 секунд. Обычный setTimeout, который вы сбрасываете, когда вызов завершился, при успехе завершает процесс сразу.
// The SDK sets no socket timeout, so a CapSkip that accepts the call
// and never answers would hang the script. Race it against a timer,
// and clear the timer so the process can exit on success.
/**
* @param {Promise<any>} promise
* @param {number} ms
*/
function withTimeout(promise, ms) {
const { promise: limit, reject } = Promise.withResolvers();
const timer = setTimeout(
() => reject(new Error(`no answer in ${ms} ms`)),
ms,
);
return Promise.race([promise, limit]).finally(() => clearTimeout(timer));
}Затем замените блок из шага 4 этим. Он оборачивает тот же вызов решения. Вставьте explain и withTimeout в любое место выше строк, которые их используют. SDK сам сдаётся через 300 секунд, поэтому лимит в 330 секунд срабатывает, только когда CapSkip замолчал.
// The SDK gives up after 300 s by itself, so a 330 s budget only
// fires when CapSkip goes silent. code is typed "string or object"
// (Capy shares the field), so String() hands the form a plain string.
const solving = solver.recaptcha(sitekey, pageUrl);
const { code } = await withTimeout(solving, 330_000);
const token = String(code);Когда побеждает таймер, ожидающее соединение остаётся открытым, поэтому блок catch в полном примере заканчивается вызовом Deno.exit. Без него процесс продолжал бы ждать на сокете. В тесте оба варианта, и CapSkip, который не ответил на отправку, и тот, который не ответил на опросы результата, завершились ошибкой по истечении лимита. Код выхода был 1.
Проверка типов в скрипте
Deno умеет проверять JavaScript по типам, поставляемым вместе с SDK, но для обычного файла .js ничего не делает, пока вы об этом не попросите. Команда deno check для обычного файла .js ничего не сообщает, даже если в коде есть ошибка типов. Включение checkJs в deno.json делает проверку настоящей.
{
"imports": {
"capskip": "npm:[email protected]"
},
"compilerOptions": {
"checkJs": true
}
}deno check solve.js
При включённой проверке опечатка в опции конструктора, например hots вместо host, считается ошибкой. Опечатка в опции внутри вызова решения ошибкой не считается, потому что тип опций принимает любой ключ, так что опечатка там компилируется и ничего не делает. Относитесь к проверке как к помощи, а не как к гарантии.
Компиляция в один exe
Поскольку разрешения входят в команду, deno compile может зашить их внутрь. Скрипт, который решает reCAPTCHA в Deno, тогда поставляется как один solve.exe, который запускается на ПК без установленного Deno. Это подходит для машины с Windows, на которой CapSkip и скрипт работают рядом.
deno compile ` --allow-env=CAPSKIP_HOST,CAPSKIP_PORT,CAPSKIP_API_KEY ` --allow-net=127.0.0.1:8080,example.com ` --output solve.exe solve.js .\solve.exe https://example.com/login
Четыре вещи, которые стоит знать. При первой компиляции с dl.deno.land скачивается среда выполнения размером около 33 МБ, а сам exe занимает около 80 МБ. Переменные окружения по-прежнему работают, поэтому CAPSKIP_PORT учитывается. Разрешения фиксируются при компиляции, и у exe нет флага, чтобы их расширить: если передать флаг allow-all, он станет первым аргументом программы. И в exe запрещённый вызов сообщает, что разрешение нужно указать при компиляции.
Запуск, воспроизводящий условия Task Scheduler, в тесте выполнил exe и завершился с кодом 0. Он использовал System32 как рабочую папку, не имел консоли и работал с PATH, в котором нет Deno. Настоящая запланированная задача не регистрировалась, поэтому прочитайте руководство по решению капчи в заданиях Windows Task Scheduler и найдите в нём лимит времени и настройки перезапуска, которые важны для запланированных задач.
Запуск CapSkip на другой машине
Скрипт и решатель не обязаны работать на одном ПК. Переключите CapSkip в режим Server в настройках подключения, и он будет слушать ваш сетевой адрес или публичный IP. Затем измените две вещи: CAPSKIP_HOST и список net. Если вы забудете добавить новый адрес в список net, в ошибке будет назван именно он.
$env:CAPSKIP_HOST = "192.168.1.50" $env:CAPSKIP_PORT = "8080" $env:CAPSKIP_API_KEY = "YOUR_API_KEY" deno run ` --allow-env=CAPSKIP_HOST,CAPSKIP_PORT,CAPSKIP_API_KEY ` --allow-net=192.168.1.50:8080,example.com ` solve.js https://example.com/login
Если маршрут идёт через интернет, используйте статический публичный IP. Включите проверку API-ключа и укажите в CAPSKIP_API_KEY тот же ключ, что задан в блоке выше. При запуске режима Server CapSkip открывает свой порт для всех адресов. Чтобы доступ был только у ожидаемых машин, добавьте в Windows Firewall правило, блокирующее все остальные. Решателем по-прежнему остаётся ваша собственная машина с Windows, и решения по-прежнему не тарифицируются.
Полный скрипт
Вот solve.js целиком, с обёрткой таймаута и обработчиком ошибок на месте. Сохраните его рядом с deno.json и запустите командой из шага 6.
import {
ApiException,
CapSkip,
NetworkException,
TimeoutException,
} from "capskip";
// The SDK never reads the environment, so read it here and pass it in.
const host = Deno.env.get("CAPSKIP_HOST") ?? "127.0.0.1";
const port = Number(Deno.env.get("CAPSKIP_PORT") ?? 8080);
const apiKey = Deno.env.get("CAPSKIP_API_KEY") ?? "capskip";
const solver = new CapSkip({ host, port, apiKey });
// The login page to solve, passed on the command line.
const pageUrl = Deno.args[0] ?? "https://example.com/login";
// Deno's fetch keeps no cookies, so collect every Set-Cookie line.
// (headers.get("set-cookie") would glue them into one string.)
const jar = new Map();
/** @param {Response} response */
function keepCookies(response) {
for (const line of response.headers.getSetCookie()) {
const [pair] = line.split(";");
const eq = pair.indexOf("=");
jar.set(pair.slice(0, eq).trim(), pair.slice(eq + 1).trim());
}
}
const cookies = () => Array.from(jar, ([k, v]) => `${k}=${v}`).join("; ");
// Deno's permission errors arrive wrapped in NetworkException. The
// cause is NotCapable for an IP host, and has the code EPERM for a
// host name. Tell them apart from a CapSkip that is not running.
/** @param {any} err */
function explain(err) {
const cause = err?.cause;
if (err instanceof NetworkException) {
const denied = cause instanceof Deno.errors.NotCapable ||
cause?.code === "EPERM";
return denied
? `Deno blocked the call, add a flag: ${err.message}`
: `CapSkip is not reachable: ${err.message || cause?.code}`;
}
if (err instanceof ApiException) return `CapSkip replied: ${err.message}`;
if (err instanceof TimeoutException) return `Too slow: ${err.message}`;
return String(err?.stack ?? err);
}
// The SDK sets no socket timeout, so a CapSkip that accepts the call
// and never answers would hang the script. Race it against a timer,
// and clear the timer so the process can exit on success.
/**
* @param {Promise<any>} promise
* @param {number} ms
*/
function withTimeout(promise, ms) {
const { promise: limit, reject } = Promise.withResolvers();
const timer = setTimeout(
() => reject(new Error(`no answer in ${ms} ms`)),
ms,
);
return Promise.race([promise, limit]).finally(() => clearTimeout(timer));
}
try {
const page = await fetch(pageUrl);
keepCookies(page);
const html = await page.text();
// Adjust these two patterns to your page's markup.
const sitekey = html.match(/data-sitekey="([^"]+)"/)?.[1];
const csrf = html.match(/name="csrfmiddlewaretoken" value="([^"]+)"/)?.[1];
if (!sitekey || !csrf) throw new Error("sitekey or CSRF token not found");
// The SDK gives up after 300 s by itself, so a 330 s budget only
// fires when CapSkip goes silent. code is typed "string or object"
// (Capy shares the field), so String() hands the form a plain string.
const solving = solver.recaptcha(sitekey, pageUrl);
const { code } = await withTimeout(solving, 330_000);
const token = String(code);
// fetch sends no Referer, and a strict site rejects the post without
// one. redirect "manual" keeps the 302 so we can read its new cookie.
const response = await fetch(pageUrl, {
method: "POST",
redirect: "manual",
headers: { Cookie: cookies(), Referer: pageUrl },
body: new URLSearchParams({
csrfmiddlewaretoken: csrf,
username: "YOUR_USERNAME",
password: "YOUR_PASSWORD",
"g-recaptcha-response": token,
}),
});
keepCookies(response);
// A good login answers 302 and rotates the session cookie.
if (response.status !== 302) {
const body = await response.text();
throw new Error(`login rejected: ${response.status} ${body}`);
}
const next = new URL(response.headers.get("location") ?? "/", pageUrl);
const home = await fetch(next, { headers: { Cookie: cookies() } });
// Swap "Welcome back" for a marker your own site shows after login.
const ok = (await home.text()).includes("Welcome back");
console.log(ok ? "logged in" : "NOT logged in");
if (!ok) Deno.exit(2);
} catch (err) {
console.error(explain(err));
Deno.exit(1);
}Частые ошибки при решении reCAPTCHA в Deno
Без блока try Deno сначала выводит имя класса, как в первом столбце. Полный скрипт выводит те же сообщения после собственного префикса.
| Что вы видите | Причина | Исправить |
|---|---|---|
| Requires env access to "CAPSKIP_HOST" | В разрешении env не хватает имени | Перечислите все три имени CAPSKIP |
| NetworkException: Requires net access to "127.0.0.1:8080" | Хоста и порта CapSkip нет в списке net | Добавьте их, а также сайт |
| NetworkException: connect ECONNREFUSED, примерно через 2 секунды | CapSkip не запущен либо неверно указан порт | Запустите CapSkip и проверьте порт в настройках подключения |
| NetworkException: getaddrinfo EPERM localhost | Используется имя хоста, а в списке net указан только IP | Используйте 127.0.0.1 в CAPSKIP_HOST |
| ReferenceError: require is not defined | Вызов CommonJS в ES-модуле | Используйте именованный импорт |
| TypeError: CapSkip is not a constructor | Импорт по умолчанию отдаёт весь объект exports | Импортируйте { CapSkip } по имени |
| Could not find "capskip" in a node_modules folder | package.json выше проекта подменяет разрешение импортов | Положите deno.json в папку проекта |
| Ни вывода, ни завершения | CapSkip принял вызов и так и не ответил | Оберните вызов в withTimeout и вызовите Deno.exit |
| ApiException: ERROR_CAPTCHA_UNSOLVABLE | CapSkip пытался решить, но не смог | Отправьте новое задание, см. заметки об ERROR_CAPTCHA_UNSOLVABLE |
FAQ
Можно ли пропустить флаги и использовать allow-all?
Это работает, но лишает смысла запуск под Deno. Тогда у скрипта есть доступ ко всему: к сети, файлам и окружению, в том числе у пакетов, которые он импортирует. Один список из трёх имён и один список из двух хостов достаточно короткие, чтобы их поддерживать.
Работает ли это в Deno Deploy или в другой хостинговой среде выполнения?
Deno Deploy и другие хостинговые среды выполнения здесь не тестировались. Сервер в чужом дата-центре не может обратиться к 127.0.0.1 на вашем ПК. Поэтому запустите CapSkip в режиме Server на машине с Windows, которой вы управляете, со статическим публичным IP и направьте на него CAPSKIP_HOST. Платформа также должна разрешать исходящие подключения к этому адресу и порту, так что проверьте её сетевые правила. Отдельное руководство по решению капчи в Netlify Functions описывает такую же настройку режима Server для вызова из размещённой функции.
Почему лучше использовать SDK, а не вызывать CapSkip через fetch?
CapSkip понимает API 2captcha, поэтому fetch к in.php и res.php работает. Но тогда цикл опроса, нарастающие паузы между попытками и сопоставление ошибок вам придётся писать самому. SDK уже опрашивает по расписанию, которое начинается с четверти секунды, возвращает токен и выбрасывает ApiException, NetworkException и TimeoutException. За это приходится платить двумя особенностями: ошибки разрешений приходят обёрнутыми, а SDK не задаёт таймаут сокета.
Работают ли другие типы капчи в Deno так же?
Для этого руководства под Deno запускалась только reCAPTCHA v2. Типы с токеном, например Turnstile и GeeTest, обращаются к тем же эндпоинтам, поэтому должны подойти те же два флага, но это не проверялось. Капче с картинкой, заданной путём к файлу, дополнительно нужно разрешение read, а если она задана URL, этот сайт должен быть в списке net. Параметры для каждого типа смотрите на странице сервиса распознавания капчи для Node.js перед копированием вызова.
Коротко
Чтобы решить reCAPTCHA в Deno, положите в свою папку deno.json с импортом SDK и импортируйте именованный экспорт. Читайте настройки CapSkip сами. Запускайте со списком env и списком net, в котором есть адрес CapSkip и ваш сайт, и больше ничего. Заблокированный вызов отличайте от остановленного CapSkip по причине ошибки. Ограничивайте решение обычным таймером, потому что AbortSignal.timeout удерживает процесс открытым, а молчащий CapSkip иначе подвешивает его навсегда.
- Все типы капчи, которые поддерживает пакет capskip, перечислены на странице сервиса распознавания капчи для Node.js.
- reCAPTCHA с чекбоксом на чистом Node описана в руководстве по решению reCAPTCHA v2 в Node.js.
- Как работает решатель reCAPTCHA с чекбоксом, описано на странице сервиса распознавания reCAPTCHA v2.
Скрипт, который вы компилируете один раз и запускаете по расписанию, может делать тысячи решений в месяц. Когда безлимитный сервис распознавания капчи выполняется на вашей собственной машине с Windows, это число никогда не превращается в счёт.
