Solve captchas through the 2captcha API, not its extension

The two Tradeify automations clicked the 2captcha *extension's* solve button
and waited for it to go green. That needed a second extension installed and
signed in, put a real mouse click on a button whose markup is 2captcha's to
change, and gave a run no way to tell "still solving" from "not installed".

Replaced with one `solveCaptcha` step. No mouse: the extension reads the
widget's sitekey out of the page, the dashboard buys a token from 2captcha,
and the extension puts it in the response field and fires the page's own
callback.

captcha-hook.js is the part that earns its keep. A widget rendered
programmatically puts nothing in the DOM — no sitekey, and no callback. Most
pages only enable submit from that callback, so filling the hidden field alone
leaves the page looking unsolved. The hook wraps turnstile/grecaptcha/hcaptcha
`render` at document_start to hold on to what the page passed.

Solves go through 2captcha's residential proxy. Its API rejects host names in
proxyAddress, so na.proxy.2captcha.com is resolved to an IP first (cached);
the gateway was confirmed to work addressed that way.

Covers reCAPTCHA v2 (incl. invisible and Enterprise), hCaptcha and Turnstile.
NOT reCAPTCHA v3: a v3 page carries an anchor iframe with k=, so it is
detected as v2 and would be solved with the wrong task type. Everything else
unsupported fails loudly instead.

The API key and proxy credentials are hardcoded in the captcha route on
request. Unlike the sqlite database, that file is in the repo, so they travel
with it. TWOCAPTCHA_API_KEY / TWOCAPTCHA_PROXY and the settings rows still
override; neither has a field on the Settings page.

Runner VERSION and RUNNER_EXPECTED_VERSION both to 0.17.0 — they are a pair,
and a mismatch shows as a red "restart your runner" banner that blames the
wrong process.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-08-31 00:18:00 -05:00
co-authored by Claude Opus 5
parent 25289cb9b3
commit 159389be35
11 changed files with 837 additions and 28 deletions
+39
View File
@@ -166,6 +166,45 @@ the automation buttons when it is not there. A running Python process does not
reload when the source changes, so restart it after pulling updates; the
dashboard warns when its version is behind.
### 4. Captchas
A run that hits a captcha solves it through the **2captcha API**, not by clicking
2captcha's own browser extension. The API key is hardcoded in
`app/api/autobuyer/captcha/route.ts` — to bill a different account, set
`TWOCAPTCHA_API_KEY`, or PATCH `twocaptcha_api_key` to `/api/settings`. Either way
the key stays on the dashboard: the runner asks this machine to solve and never
handles it.
A `solveCaptcha` step then does three things, with no mouse involved: the
extension reads the widget's sitekey out of the page, the dashboard buys a token
from 2captcha, and the extension puts that token into the page's response field
and fires the page's own callback. reCAPTCHA v2 (including Enterprise), hCaptcha
and Turnstile are detected automatically.
If the 2captcha *extension* is still installed, disable it or turn off its
auto-solve. Two solvers racing on one widget is a good way to burn balance and
end up with a token the page has already replaced.
Solving costs money per captcha, and a failed solve is still billed. `timeoutSeconds`
on the step caps how long a run waits; 2captcha usually answers in 10–30s.
**The solver proxy.** Solves go through 2captcha's residential proxy, so the
challenge is answered from a residential address rather than a datacentre one.
The credentials are hardcoded in `app/api/autobuyer/captcha/route.ts`; override
them without touching code via `TWOCAPTCHA_PROXY`, or by PATCHing
`twocaptcha_proxy` to `/api/settings`. Either accepts `none` to solve proxyless.
Neither has a field on the Settings page — both are hardcoded, so there is
nothing to fill in. The run log names the address that
solved each captcha — the first thing to check when a token comes back and the
site rejects it anyway.
Two things that proxy does *not* do. It is not the browser's proxy: the token is
solved from the proxy's address but the form is submitted from this machine's,
and nothing makes those match. And the pool is not geo-pinned — despite the
`na.` hostname, test requests exited from Russia and Japan. For reCAPTCHA v2 and
Turnstile that mismatch is usually tolerated; if a site starts rejecting tokens
that 2captcha reports as solved, set the proxy to `none` and compare.
### Running it
Press an automation's button. Progress appears per step, and **Stop** halts a run