Как решать капчи в Activepieces (HTTP piece)

Решение капчи в Activepieces состоит из трёх шагов: отправить задание, подождать, прочитать токен. Стройте его на HTTP piece, а не на шаге Code, потому что сама возможность использовать npm в шаге Code зависит от того, в каком режиме песочницы работает ваш экземпляр, а в том режиме, который использует Activepieces Cloud, npm нет. HTTP piece работает во всех режимах. Есть ещё одна настройка, которая важнее самого кода: именно она решает, сможет ли ваш flow вообще достучаться до решателя по частному адресу.
Что понадобится
- Проект Activepieces, в котором вы можете опубликовать flow, в их облаке или на своём сервере.
- CapSkip, запущенный на машине с Windows. Local mode подходит только если Activepieces работает на этой же машине, что на практике означает self-hosted установку. Во всех остальных случаях нужен Server mode.
- Sitekey и URL страницы сайта, который вы автоматизируете.
- Переменная проекта с ключом решателя, чтобы он не лежал в теле flow.
- На self-hosted экземпляре с усиленными сетевыми настройками: одна запись в списке разрешений SSRF. Об этом шаг 3.
Почему HTTP piece, а не шаг Code
В редакторе шага Code есть диалог Add npm package. Он находит пакет в реестре npm, фиксирует последнюю версию и записывает её в список зависимостей шага. В Activepieces Cloud этот список затем просто выбрасывается.
Activepieces собирает шаг Code так: записывает ваш исходник в файл TypeScript, устанавливает зависимости и собирает результат в бандл. Зависимости запрашиваются при сборке только тогда, когда режим выполнения экземпляра разрешает пакеты. В режиме V8 sandboxing, который Activepieces в документации называет режимом своего облака, пакеты запрещены, поэтому сборка подставляет пустой набор зависимостей и всё равно компилирует. Шаг разворачивается без ошибок. Импорт падает уже при запуске flow.
| Режим выполнения | npm в шаге Code | Что это значит для этого flow |
|---|---|---|
| V8 sandboxing, значение SANDBOX_CODE_ONLY | Пакеты npm недоступны | Используйте HTTP piece. Это Activepieces Cloud |
| Комбинированная песочница, SANDBOX_CODE_AND_PROCESS | Пакеты npm недоступны | Используйте HTTP piece |
| Пространства имён ядра, SANDBOX_PROCESS | Пакеты npm работают | Node SDK работает и сам опрашивает результат |
| Без песочницы, UNSANDBOXED | Пакеты npm работают | Node SDK работает и сам опрашивает результат |
Итого есть два честных способа сделать это, и выбираете не вы, а ваш администратор. Маршрут через HTTP piece, описанный ниже, работает во всех четырёх режимах. Маршрут через шаг Code в конце этого руководства работает в двух из них и заметно короче, когда он вам доступен.
Шаг 1: отправьте задание
CapSkip говорит на совместимом с 2captcha API по порту 8080, поэтому HTTP piece обращается к нему без установки коннектора. Добавьте действие Send HTTP Request, задайте метод POST и укажите в URL адрес endpoint отправки на вашем решателе.
{
"key": "{{variables['CAPSKIP_KEY']}}",
"method": "userrecaptcha",
"googlekey": "YOUR_SITEKEY",
"pageurl": "https://example.com/page-with-recaptcha",
"json": 1
}В ответ приходит небольшой объект JSON, в поле request которого лежит id, по которому вы опрашиваете результат.
{ "status": 1, "request": "2122988149" }Это reCAPTCHA v2. Остальные типы, которые поддерживает CapSkip, вызываются точно так же, только с другими параметрами: добавьте invisible или enterprise со значением 1, либо version со значением v3 и именем action, либо смените method на turnstile или geetest. Полный список параметров есть в документации CapSkip API.
Ссылайтесь на ключ через синтаксис переменных проекта, а не вставляйте его текстом. Переменные шифруются при хранении и никогда не показываются обратно в списке переменных, а смена ключа сводится к одной правке вместо поиска по всем flow.
Шаг 2: подождите, затем прочитайте токен
Добавьте действие Delay For, а за ним второй HTTP-запрос, который читает результат. Двадцать секунд служат разумным первым ожиданием для чекбокса reCAPTCHA v2. Графические капчи возвращаются примерно за секунду, v3 за десять или пятнадцать, GeeTest примерно за пять.
# GET, with the id from step 1 in the query string. http://127.0.0.1:8080/res.php?key=YOUR_KEY&action=get&id=2122988149&json=1
Возможны два ответа. Готовый результат представляет собой объект JSON той же формы, что и ответ на отправку, с токеном в поле request. Ещё не готовый результат приходит строкой CAPCHA_NOT_READY, написанной без буквы T, и означает, что нужно подождать ещё, а не что что-то сломалось. История этого написания изложена в полном разборе ответа CAPCHA_NOT_READY.
Piece Delay ведёт себя по-разному по обе стороны от десяти секунд, и именно эта деталь делает такую схему опроса дешёвой. Задержка в десять секунд или меньше просто засыпает в процессе воркера. Всё, что дольше, создаёт waitpoint, приостанавливает запуск и возобновляет его, когда время истекает. Приостановленное время не считается временем выполнения, и в документации Activepieces сказано, что flow, приостановленные через Delay или через Wait for Approval, не расходуют таймаут запуска. Поэтому двадцатисекундное ожидание ничего не стоит вам из десятиминутного бюджета, как и второе такое же.
Если одного чтения мало, добавьте ещё один Delay и ещё одно чтение, а не беритесь за цикл. Причин две. Loop on Items проходит по всем элементам списка, поэтому итерации выполняются независимо от того, пришёл токен или нет, а Router внутри него экономит вам работу в ветке, но не сам проход по циклу. И что важнее, результат CapSkip читается только один раз, поэтому цикл, который перечитывает уже полученный id, не получит токен дважды: на втором чтении он получит ошибку.
Шаг 3: сетевая настройка, которая блокирует локальный решатель
Именно на этом люди спотыкаются, и это относится конкретно к Activepieces, а не к оркестраторам вообще. В Activepieces есть защита от SSRF для кода flow, управляемая переменной AP_NETWORK_MODE. По умолчанию она равна UNRESTRICTED. Со значением STRICT движок подменяет в Node DNS-поиск и подключение сокета ещё до запуска любого кода flow и отклоняет любое соединение, адрес которого является loopback, частным по RFC1918, link-local или адресом метаданных облака. При этом выбрасывается ошибка SSRFBlockedError.
Сервис распознавания капчи в вашей собственной сети выглядит ровно так, как то, что эта защита блокирует. И 127.0.0.1, и адрес в локальной сети вроде 192.168.1.40 попадают в её список. Это защита делает свою работу, а не баг, и Activepieces даёт для неё документированное исключение: укажите адрес решателя в AP_SSRF_ALLOW_LIST, который принимает IP-адреса и диапазоны CIDR через запятую и действует одинаково на код flow и на исходящие запросы самого сервера. После изменения перезапустите сервер.
# On a self-hosted Activepieces with AP_NETWORK_MODE=STRICT, # name the solver machine or its subnet so flows can reach it. AP_SSRF_ALLOW_LIST=192.168.1.40,10.0.5.0/24
Отдельно от этой защиты flow вообще должен иметь возможность достучаться до машины. Для этого у CapSkip есть два режима подключения. Local привязывается к 127.0.0.1 и обслуживает только это устройство. Server привязывается к вашему сетевому адресу или публичному IP, поэтому другая машина, хост контейнеров или облачная платформа могут обратиться к той же машине с Windows через API. Оба находятся в разделе Настройки подключения, а Server mode меняет только то, по какому адресу слушает решатель. Оборудование по-прежнему ваше, и распознавание капчи по-прежнему безлимитное.
| Где работает Activepieces | Какой режим и что ещё |
|---|---|
| Self-hosted на той же машине с Windows, что и CapSkip | Local mode, host остаётся 127.0.0.1. Добавьте его в список разрешений, если сетевой режим STRICT |
| Self-hosted в Docker или на другой машине в вашей сети | Server mode с локальным сетевым адресом решателя. Этот адрес тоже добавьте в список разрешений |
| Activepieces Cloud | Server mode со статическим публичным IP и правилом брандмауэра. Защита от SSRF по-прежнему работает, но не блокирует, потому что публичного адреса нет в её списке блокировок |
Шаг 4: таймауты и повторные попытки
Три числа определяют, переживёт ли медленное решение капчи, и только одно из них вы задаёте во flow сами.
| Какой лимит | Значение | Почему это важно для решения капчи |
|---|---|---|
| Весь запуск и любое отдельное действие ограничены независимо друг от друга | По десять минут на каждый, оба из одной переменной AP_FLOW_TIMEOUT_SECONDS | С запасом, потому что время задержки не засчитывается в таймаут запуска flow |
| Таймаут синхронного ответа webhook | Тридцать секунд, задаётся через AP_WEBHOOK_TIMEOUT_SECONDS | Ловушка. Смотрите абзац ниже |
| Retry on Failure, на уровне шага | Четыре попытки с паузами в четыре, восемь и шестнадцать секунд | Спасает, когда решатель перезапускается, но не когда он просто работает медленно |
Больнее всего бьёт именно число для webhook. URL webhook, заканчивающийся словом sync, держит HTTP-соединение открытым и отвечает результатом flow, а через тридцать секунд сдаётся. Решение reCAPTCHA v2 не укладывается в тридцать секунд стабильно, поэтому вызывающая сторона, которая запускает flow синхронно и ждёт токен в ответ, получает HTTP 408, пока flow продолжает выполняться за её спиной. Запускайте flow асинхронно и пусть он сам отправляет токен туда, куда нужно, либо разделите работу так, чтобы синхронная половина никогда не ждала решения капчи.
Retry on Failure стоит включить для шага отправки и не стоит для шага чтения. Задержка растёт экспоненциально от базы в две секунды, поэтому за четыре попытки паузы составляют примерно четыре, восемь и шестнадцать секунд. Для отклонённого соединения это правильно. Для уже полученного токена это неверно из-за правила однократного чтения, о котором сказано выше.
Всё это в одном шаге, на self-hosted экземпляре
Если ваш администратор работает без песочницы или с песочницей на пространствах имён ядра, весь flow выше сворачивается в один шаг Code, потому что SDK опрашивает результат за вас. Добавьте capskip в диалоге npm, затем напишите шаг. Шаги Code написаны на TypeScript и собираются в бандл перед запуском, поэтому обычный import работает.
// npm install capskip - add it in the step's package dialog.
import { CapSkip } from 'capskip';
export const code = async (inputs) => {
// host is the solver machine. Keep 127.0.0.1 only when
// Activepieces runs on the same Windows box as CapSkip.
const solver = new CapSkip({
host: inputs.capskipHost,
port: 8080,
apiKey: inputs.capskipKey,
});
const result = await solver.recaptcha(inputs.sitekey, inputs.pageUrl);
// Return the token, not the whole result. The next step
// submits it, and run logs keep whatever you return.
return { token: result.code };
};Передайте capskipKey как входное значение шага со ссылкой на переменную проекта, чтобы ключ подставлялся во время выполнения и никогда не появлялся в исходном коде. SDK начинает опрос с 250 миллисекунд и постепенно увеличивает интервал вместо ожидания с фиксированным шагом, поэтому такой вариант обычно возвращает результат раньше, чем flow на основе Delay. Его потолок для reCAPTCHA, Turnstile и GeeTest составляет 300 секунд, что заметно меньше десятиминутного таймаута действия.
Отправляйте токен в следующем же шаге. Токен reCAPTCHA годен примерно две минуты, поэтому flow, который решает капчу, ждёт на шаге согласования и только потом отправляет, споткнётся на токене, который в момент создания был совершенно исправным. Этот сценарий отказа разобран в руководстве по истечению срока действия токена reCAPTCHA.
Частые ошибки и что они означают
| Что вы видите | Причина | Исправить |
|---|---|---|
| SSRFBlockedError в журнале запуска | Сетевой режим STRICT, а решатель находится на частном адресе | Добавьте адрес в AP_SSRF_ALLOW_LIST и перезапустите сервер |
| Модуль capskip не найден при выполнении flow | Режим песочницы отбросил зависимость на этапе сборки | Перестройте шаг на вызовы через HTTP piece или разверните self-hosted экземпляр в режиме, где пакеты разрешены |
| Соединение по порту 8080 отклонено | CapSkip привязан к loopback, а воркер находится в другом месте | Переключитесь на Server mode и используйте сетевой адрес решателя |
| Чтение каждый раз возвращает CAPCHA_NOT_READY | Задержка короче, чем время решения капчи | Увеличьте первый Delay или добавьте вторую задержку и чтение |
| Второе чтение того же id завершается ошибкой | Результат CapSkip читается только один раз | Сохраните токен в выходных данных шага и никогда не перечитывайте id |
| ERROR_WRONG_USER_KEY в ответе | Переменная проекта развернулась в пустую строку | Проверьте имя переменной, включая точный регистр |
| HTTP 408 от синхронного webhook | Решение капчи заняло больше тридцатисекундного таймаута webhook | Запускайте асинхронно или уберите решение капчи с синхронного пути |
| Целевой сайт отклоняет действительный токен | Он истёк между шагом решения и шагом отправки | Отправляйте в следующем шаге, без согласования и задержки между ними |
FAQ
Можно ли использовать CapSkip из Activepieces Cloud?
Да, через Server mode. Воркеры находятся на инфраструктуре Activepieces, а не на вашей, поэтому решатель должен слушать адрес, до которого они могут дотянуться: публичный IP, желательно статический, с правилом брандмауэра, пропускающим их трафик. В самом решателе ничего не меняется, меняется только то, где он слушает. Чего нельзя сделать в их облаке, так это использовать Node SDK в шаге Code, потому что в этом режиме нет npm, поэтому стройте flow на HTTP piece.
Почему добавление пакета npm выглядело успешным?
Потому что диалог является функцией интерфейса, а фильтрация происходит на сервере. Диалог находит пакет в реестре npm и записывает его. На этапе сборки сервер проверяет, разрешает ли режим выполнения пакеты, и если нет, подставляет пустой набор зависимостей перед установкой. Шаг компилируется и разворачивается без единого предупреждения. Вы узнаёте об этом во время выполнения, когда import разрешается в пустоту.
Должен ли flow крутиться в цикле, пока не придёт токен?
Обычно нет. Loop on Items проходит весь свой список элементов, поэтому вы платите за каждую настроенную итерацию, и каждая итерация перечитывала бы id, который можно прочитать только один раз. Задержка нужной длины плюс одно чтение и дешевле, и правильнее, а вторая задержка с чтением служит хорошим запасным вариантом. Длинные задержки здесь на удивление дёшевы, потому что задержка дольше десяти секунд приостанавливает запуск, вместо того чтобы занимать воркер, а приостановленное время не расходует таймаут запуска.
Чем это отличается от n8n, Make.com или Zapier?
Запросы во всех четырёх инструментах одинаковы. Различается препятствие, которое каждый из них ставит перед ними.
- Для n8n препятствием является сетевое взаимодействие контейнеров, и руководство по workflow в n8n разбирает его по шагам.
- Для Make.com это сертификат, которого требует его модуль HTTP, и пошаговое руководство по Make.com разбирает этот вопрос полностью.
- Для Zapier это потолок времени выполнения шага Code, о чём рассказано в руководстве по Zapier.
Activepieces добавляет два собственных препятствия: режим песочницы, который решает, существует ли npm вообще, и защиту от SSRF, способную наотрез отказать частному адресу.
Коротко
Стройте решение капчи на HTTP piece, потому что он работает во всех режимах песочницы, а маршрут через шаг Code нет. Отправляйте задание на endpoint отправки, ставьте задержку дольше десяти секунд, чтобы запуск приостанавливался, а не занимал воркер, затем прочитайте результат один раз и сохраните его. Ключ держите в переменной проекта. Если экземпляр self-hosted и сетевой режим строгий, добавьте решатель в список разрешений SSRF, а если Activepieces работает где угодно, кроме машины самого решателя, переключите CapSkip в Server mode. Никогда не ждите решения капчи на синхронном webhook.
- Сам чекбокс reCAPTCHA v2 разобран на странице сервиса распознавания reCAPTCHA v2.
- Эквивалентные версии в один вызов на Python, Node.js, PHP и C# перечислены на на странице SDK для распознавания капчи.
Об одном стоит подумать, прежде чем ставить этот flow на запуск каждые несколько минут: CapSkip работает как безлимитный сервис распознавания капчи на железе, которое у вас уже есть, поэтому постоянно срабатывающий flow стоит ровно столько же, сколько срабатывающий изредка.
