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

activepieces captcha - How to Solve CAPTCHAs in 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, что и CapSkipLocal mode, host остаётся 127.0.0.1. Добавьте его в список разрешений, если сетевой режим STRICT
Self-hosted в Docker или на другой машине в вашей сетиServer mode с локальным сетевым адресом решателя. Этот адрес тоже добавьте в список разрешений
Activepieces CloudServer 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?

Запросы во всех четырёх инструментах одинаковы. Различается препятствие, которое каждый из них ставит перед ними.

Activepieces добавляет два собственных препятствия: режим песочницы, который решает, существует ли npm вообще, и защиту от SSRF, способную наотрез отказать частному адресу.

Коротко

Стройте решение капчи на HTTP piece, потому что он работает во всех режимах песочницы, а маршрут через шаг Code нет. Отправляйте задание на endpoint отправки, ставьте задержку дольше десяти секунд, чтобы запуск приостанавливался, а не занимал воркер, затем прочитайте результат один раз и сохраните его. Ключ держите в переменной проекта. Если экземпляр self-hosted и сетевой режим строгий, добавьте решатель в список разрешений SSRF, а если Activepieces работает где угодно, кроме машины самого решателя, переключите CapSkip в Server mode. Никогда не ждите решения капчи на синхронном webhook.

Об одном стоит подумать, прежде чем ставить этот flow на запуск каждые несколько минут: CapSkip работает как безлимитный сервис распознавания капчи на железе, которое у вас уже есть, поэтому постоянно срабатывающий flow стоит ровно столько же, сколько срабатывающий изредка.