Как решать капчу в Retool Workflows (REST-блоки)

retool workflows captcha - How to Solve CAPTCHAs in Retool Workflows (REST Blocks)

Решение капчи в Retool Workflows состоит из трёх блоков: отправка, ожидание, чтение. Соберите их как resource query block по REST, а не как code block на JavaScript или Python, потому что Retool выполняет блоки кода в отдельном изолированном сервисе, чьи правила брандмауэра по умолчанию отклоняют частные адреса. Запрос к ресурсу представляет собой конфигурацию, а не пользовательский код, поэтому он не проходит через этот сервис и добирается до решателя в вашей собственной сети без этого спора.

Что понадобится

  • Организация Retool с Workflows, на Retool Cloud или на собственном сервере. Оба варианта работают, но с разной настройкой сети.
  • CapSkip, запущенный на машине с Windows. Local mode, если на этой же машине работает self-hosted Retool, и Server mode во всех остальных случаях.
  • Sitekey и URL страницы с той капчей, которую вы решаете.
  • Место для хранения API-ключа. Секреты Retool доступны и из code block, и из конфигурации ресурса, поэтому вставлять ключ прямо в блок не нужно.

CapSkip отвечает по совместимому с 2captcha API на порту 8080, поэтому Retool не нужен ни коннектор, ни собственная интеграция. Это обычный REST-ресурс, направленный на машину, которая принадлежит вам.

Шаг 1: направьте REST-ресурс на решатель

Создайте ресурс REST API с базовым URL машины, на которой работает CapSkip. Аутентификацию оставьте пустой. API-ключ передаётся как обычный параметр в каждом запросе: именно так устроен совместимый с 2captcha протокол.

# Base URL for the resource. Loopback only works when Retool
# is self-hosted on the same Windows box as the solver.
http://127.0.0.1:8080

# Server mode, which is what you want everywhere else.
http://192.168.1.40:8080

Дальше любой workflow, который что-то решает, переиспользует этот единственный ресурс. Для всей работы достаточно двух блоков запроса.

Шаг 2: отправьте капчу на решение

Добавьте resource query block, назовите его submitCaptcha, выберите тип действия POST и укажите путь к эндпоинту отправки. Тело запроса представляет собой небольшой объект JSON.

{
  "key": "YOUR_KEY",
  "method": "userrecaptcha",
  "googlekey": "YOUR_SITEKEY",
  "pageurl": "https://example.com/page-with-recaptcha",
  "json": 1
}

В ответе приходит id, по которому вы будете опрашивать результат.

{ "status": 1, "request": "2122988149" }

Это тело запроса для reCAPTCHA v2. Остальные типы, которые поддерживает CapSkip, вызываются точно так же, только с другими параметрами: добавьте invisible или enterprise со значением 1, либо version со значением v3 и именем действия, либо смените метод на turnstile или geetest. Полный список параметров приведён в документации CapSkip API.

Каждый resource query block передаёт вниз по цепочке три свойства. Тело ответа приходит в data, сообщение об ошибке в error, а всё остальное лежит в metadata. Поэтому только что полученный id доступен следующему блоку как submitCaptcha.data.request.

Шаг 3: подождите, затем один раз прочитайте токен

Добавьте Wait block. Для чекбокса reCAPTCHA v2 разумное первое ожидание составляет пятнадцать секунд. Картиночные капчи возвращаются примерно за секунду, v3 за десять или пятнадцать, GeeTest примерно за пять. Wait block принимает число или выражение на JavaScript, настраивается в секундах, минутах, часах или днях, вплоть до потолка в шестьдесят дней, и приостанавливает только те блоки, которые идут непосредственно за ним.

Затем добавьте второй resource query block с именем readResult и методом GET.

# GET, with the id from step 2 in the query string.
/res.php?key=YOUR_KEY&action=get&id={{ submitCaptcha.data.request }}&json=1

Возможны два ответа. У готового результата status равен 1, а токен лежит в поле request. У результата, который ещё считается, status равен 0, а в поле request стоит строка CAPCHA_NOT_READY, написанная без буквы T, и означает она «продолжайте ждать», а не «что-то сломалось». История этого написания разобрана в полном разборе ответа CAPCHA_NOT_READY.

Разведите два случая с помощью Branch block. Условия пишутся на обычном JavaScript и обращаются к блоку выше по цепочке, поэтому проверка выглядит так: readResult.data.status === 1. Ветка If передаёт токен дальше. В ветку Else ставится второй Wait на двадцать секунд и повторное чтение.

Не поддавайтесь искушению заменить это на Loop block. Причин две, и по-настоящему больно бьёт вторая. У Loop block таймаут по умолчанию равен десяти секундам, а потолок составляет две минуты, что заметно меньше трёхсот секунд, которые CapSkip отводит на решение reCAPTCHA, так что цикл всё равно не покроет медленный хвост. Но важнее другое: результат CapSkip читается только один раз. Цикл, который повторно читает уже забранный id, второй раз токен не получит.

Шаг 4: сетевое правило, которое решает всё

Это та часть, которая специфична именно для Retool, и именно поэтому в руководстве решение собирается из resource query block.

Retool выполняет ваш JavaScript и Python в отдельном сервисе code executor, изолированном с помощью NsJail. В self-hosted развёртывании этот сервис поставляется с правилами iptables, которые закрывают link local адреса и всю сеть 192.168.0.0/16, и Retool документирует единственный переключатель, который их отключает: DISABLE_IPTABLES_SECURITY_CONFIGURATION. Retool прямо пишет, что рекомендует запускать code executor в привилегированном режиме, чтобы пользовательский код оставался в песочнице. Получается, что code block, который тянется к решателю по адресу 192.168.1.40, требует от вас ослабить песочницу для всего инстанса. А запрос к ресурсу пользовательским кодом не является и там не выполняется.

Отдельно от этого решатель вообще должен быть достижим. Для этого у CapSkip есть два режима подключения. Local привязывается к 127.0.0.1 и обслуживает только это устройство. Server привязывается к вашему сетевому адресу или публичному IP, поэтому другая машина, хост контейнеров или облачная платформа могут обратиться к той же машине с Windows по API. Оба режима находятся в разделе Настройки подключения, а Server mode меняет только то, по какому адресу слушает решатель. Оборудование по-прежнему ваше, и распознавание капчи по-прежнему безлимитное.

Где работает RetoolКакой режим и что ещё
Self-hosted на той же машине с Windows, что и CapSkipLocal mode. Базовый URL остаётся на loopback
Self-hosted на другой машине или в Docker в вашей сетиServer mode с локальным адресом решателя. Используйте resource query block, а не code block
Retool CloudServer mode со статическим публичным IP и входящим правилом брандмауэра для исходящих адресов Retool

Retool Cloud обращается к вашим ресурсам с фиксированного и опубликованного набора адресов, и документация требует, чтобы облачные инстансы обеспечивали доступ к настроенным ресурсам с этих адресов. Регион по умолчанию: AWS us-west-2.

# Retool Cloud outbound ranges, us-west-2, the default region.
35.90.103.132/30
44.208.168.68/30

# eu-central-1
3.77.79.248/30

Разрешите их на брандмауэре перед портом 8080 и запретите всё остальное. Открывается при этом намного меньше, чем кажется, и на этом вся история с Retool Cloud заканчивается.

Шаг 5: таймауты, от которых зависит, переживёт ли система медленное решение

Retool публикует разные потолки для разных типов блоков, и здесь они важны, потому что решение капчи по своей природе медленное.

Какой лимитЗначениеПочему это важно для решения капчи
Resource query block, асинхронный запускДо 10 минутС запасом. Одно чтение возвращается заметно быстрее секунды
Resource query block, синхронный запускДо 2 минутДля чтения этого по-прежнему хватает, потому что ожидание происходит в Wait block
Loop block10 секунд по умолчанию, максимум 2 минутыИменно поэтому цикл опроса здесь не подходит
Весь запуск, асинхронный30 часов, а с Wait block без ограниченийРешение капчи и близко не подходит к этому пределу
Весь запуск, синхронный15 минут до первого Response block вебхукаЛовушка. Смотрите абзац ниже
Одновременных внешних запросов на один workflow50 одновременноНастоящий потолок для пачки решений в одном запуске
Интервал триггера по расписаниюМинимум одна минутаПодходит, и запускать так часто ничего не стоит

Планировать нужно вокруг синхронного числа. Триггер вебхука, который держит соединение открытым и отвечает через Response block, даёт вам пятнадцать минут, и звучит это щедро ровно до того момента, как вы вспомните, что удерживать вызывающую сторону две минуты на открытом HTTP-соединении ради reCAPTCHA уже само по себе является плохим решением. Запускайте workflow асинхронно и отправляйте токен туда, где он нужен, либо отвечайте на вебхук сразу, а решение капчи выполняйте за ним.

У resource query block есть собственные настройки числа повторов и экспоненциальной задержки. Включите их для submitCaptcha и оставьте выключенными для readResult, по той самой причине с однократным чтением, о которой сказано выше.

Если вы всё же предпочитаете писать код

На self-hosted инстансе, где code executor может дотянуться до решателя, весь поток сворачивается в один блок на Python, потому что SDK опрашивает результат за вас. Сначала добавьте capskip в requirements.txt вашего workflow на вкладке Libraries.

# pip install capskip - add it in the Libraries tab instead.
from capskip import CapSkip

# host is the solver machine. Keep 127.0.0.1 only when Retool
# runs on the same Windows box as CapSkip.
solver = CapSkip(host="192.168.1.40", port=8080)

result = solver.recaptcha(
    sitekey="YOUR_SITEKEY",
    url="https://example.com/page-with-recaptcha",
)

# Python blocks serialize their output as JSON, so return
# the token rather than the client object.
{"token": result["code"]}

SDK начинает опрос с 250 миллисекунд и постепенно увеличивает паузу до пяти секунд, вместо того чтобы спать фиксированный интервал, поэтому такой вариант обычно возвращает результат раньше, чем схема с Wait block. Его потолок для reCAPTCHA, Turnstile и GeeTest составляет триста секунд, что укладывается в десятиминутный таймаут асинхронного code block. Retool Cloud по умолчанию запускает Python 3.10, а аналогичные версии в один вызов для Node.js, PHP и C# собраны на странице SDK для распознавания капчи.

Отправляйте токен в следующем же блоке. Токен reCAPTCHA живёт около двух минут, поэтому workflow, который решает капчу, потом стоит на длинном Wait block и только затем отправляет данные, упрётся в токен, который был действителен в момент создания. Этот сценарий отказа разобран в руководстве по истечению срока действия токена reCAPTCHA.

Частые ошибки и что они означают

Что вы видитеПричинаИсправить
Таймаут в code block при обращении к адресу локальной сетиПравила iptables по умолчанию у self-hosted code executor закрывают 192.168.0.0/16Перенесите вызов в REST resource query block
Соединение по порту 8080 отклоненоCapSkip привязан к loopback, а Retool находится в другом местеПереключитесь на Server mode и используйте сетевой адрес решателя
Retool Cloud вообще не может достучаться до ресурсаБрандмауэр не пропускает исходящие адреса RetoolРазрешите опубликованные диапазоны для вашего региона на порту 8080
readResult каждый раз возвращает CAPCHA_NOT_READYWait block короче, чем занимает решение капчиУвеличьте первое ожидание или добавьте второе ожидание и чтение в ветку Else
Второе чтение того же id возвращается пустымРезультат CapSkip читается только один разДержите токен в выводе блока и никогда не перечитывайте id
ERROR_WRONG_USER_KEY в ответеПараметр key раскрылся в пустую строкуПроверьте имя секрета, включая точный регистр букв
Целевой сайт отклоняет действительный токенОн истёк между решением и отправкойОтправляйте в следующем же блоке, без Wait между ними
Пачка решений застревает на полпутиОдин workflow может держать в полёте 50 внешних запросовРазбейте Loop block на пачки или распределите работу по нескольким запускам

FAQ

Можно ли использовать CapSkip из Retool Cloud?

Да, через Server mode. Retool Cloud обращается к вашим ресурсам со своей собственной инфраструктуры, поэтому решатель должен слушать адрес, до которого они могут дотянуться: публичный IP, желательно статический. Retool публикует исходящие диапазоны, с которых он делает вызовы, поэтому правило брандмауэра получается узким, а не открытым всему миру. В самом решателе ничего не меняется и ничего не становится платным по счётчику. Отличается только адрес, который он слушает.

Почему бы просто не использовать блок на JavaScript с axios?

Из-за того, где этот код выполняется. Retool исполняет code block в отдельном сервисе, изолированном с помощью NsJail, и в self-hosted развёртывании этот сервис ставит правила брандмауэра, закрывающие частные диапазоны. Их отключение выполняется документированным переключателем, но такое решение принимается на уровне всего инстанса ради удобства одного workflow, и Retool советует так не делать. А вот resource query block добирается до того же решателя без всякой подобной дилеммы. Используйте code block для логики, а resource query block для сети.

Нужно ли крутить workflow в цикле, пока не придёт токен?

Нет. Loop block упирается в потолок в две минуты, что заметно меньше трёхсот секунд, отведённых на решение reCAPTCHA, и он выполняет все заданные итерации, а не останавливается раньше. Вдобавок результат читается только один раз, поэтому повторные чтения того же id уходят впустую. Wait block нужной длины плюс одно чтение работает правильно и дёшево, а Branch block со вторым ожиданием и чтением за ним закрывает медленные случаи.

Чем это отличается от n8n или Pipedream?

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

  • У n8n препятствием оказывается сеть контейнеров, и это подробно разобрано здесь: руководство по workflow в n8n.
  • У Pipedream препятствием становятся среда выполнения шага и место хранения секретов, о чём рассказано в руководстве по Pipedream.
  • У Retool препятствием служит собственный брандмауэр сервиса code executor, и именно поэтому в описанном выше потоке HTTP-вызов никогда не попадает в code block.

Коротко

Направьте REST-ресурс на решатель, отправьте капчу через POST, подождите пятнадцать секунд в Wait block, заберите результат через GET и разведите ветки по готовности через Branch block. Держите HTTP в resource query block, а не в code block, потому что правила брандмауэра по умолчанию у сервиса code executor закрывают частные адреса, а их ослабление становится решением уровня всего инстанса. Переключайте CapSkip в Server mode всякий раз, когда Retool работает не на той же машине, что и решатель, и на Retool Cloud пропускайте к порту 8080 только опубликованные исходящие диапазоны. Никогда не ждите решения капчи внутри синхронного вебхука.

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