So lösen Sie reCAPTCHA in Deno: Berechtigungen und Timeouts

solve recaptcha in deno - How to Solve reCAPTCHA in Deno: Permissions and Timeouts

Um reCAPTCHA in Deno zu lösen, importieren Sie das CapSkip Node.js SDK über einen npm-Specifier und lesen die Adresse des Solvers selbst aus der Umgebung aus. Führen Sie das Skript dann mit Berechtigungs-Flags aus, die nur CapSkip und Ihre Website nennen. CapSkip läuft auf Ihrem eigenen Windows-PC, daher gibt es keine Rechnung pro Lösung. Der Code ist kurz. Das Berechtigungsmodell von Deno ist der schwierige Teil. Das SDK meldet einen blockierten Aufruf als Netzwerkfehler. Ein CapSkip, das verstummt, lässt das Skript endlos hängen, ohne Fehler und ohne dass es sich beendet. Und der übliche Weg, ein Timeout hinzuzufügen, hält den Prozess für das gesamte Zeitbudget am Leben. Dieser Leitfaden behandelt jeden dieser Punkte.

Was Sie brauchen

  • CapSkip läuft auf einem Windows-PC, mit eingeschaltetem reCAPTCHA-Löser. Die Einrichtungsanleitung führt durch den ersten Start.
  • Deno 2. Alles unten wurde unter Windows 11 mit Deno 2.9.6 ausgeführt und noch einmal mit 2.9.7, jeweils mit capskip 1.3.0 von npm.
  • Eine PowerShell-Eingabeaufforderung. Die Befehle verwenden PowerShell-Syntax.
  • Eine Seite mit einer reCAPTCHA-v2-Checkbox in einem Formular. Das Beispiel meldet sich an einem Formular im Django-Stil an, das einen CSRF-Token trägt. Passen Sie die Feldnamen an Ihre Website an.
  • Eine Adresse für den Solver. Im Local-Modus antwortet CapSkip auf 127.0.0.1 nur für dieses Gerät. Im Server-Modus lauscht es auf Ihrer Netzwerkadresse oder öffentlichen IP, damit ein Skript auf einem anderen Rechner es über die API aufrufen kann. Eingestellt werden beide Modi in den Verbindungseinstellungen.

Die Messungen unten stammen von einem Mock-CapSkip und einer Mock-Login-Seite (nachgebaute Testattrappen) auf demselben PC, verwendet mit dem echten Deno und dem echten SDK. Die Berechtigungsfehler, der Polling-Zeitplan und das Verhalten beim Hängen gehören zu Deno und zum SDK, gelten also auch für Ihre eigene Website. Die Lösezeiten und die Regeln der Login-Seite zum Annehmen und Ablehnen sind dagegen die des Mocks.

Schritt 1: Deno installieren und das Projekt anlegen

Installieren Sie Deno mit dem offiziellen Installer oder mit winget. Auf der Installationsseite von Deno finden Sie weitere Optionen, etwa Scoop und Chocolatey.

# PowerShell: the official installer (or: winget install DenoLand.Deno)
irm https://deno.land/install.ps1 | iex
deno --version

Erstellen Sie dann einen Projektordner und legen Sie eine deno.json hinein, bevor Sie irgendetwas anderes ausführen. Die Import-Map legt die Version des SDK fest und erlaubt es dem Skript, "capskip" statt eines langen npm-Specifiers zu schreiben.

{
  "imports": {
    "capskip": "npm:[email protected]"
  }
}

Führen Sie deno add nicht aus, bevor diese Datei existiert. Liegt eine package.json in einem Ordner oberhalb Ihres Projekts, was in einem Benutzerprofil häufig vorkommt, behandelt Deno das Projekt als Node-Projekt. Ein Import von npm:[email protected] scheitert dann mit Could not find "capskip" in a node_modules folder. Ein Import ohne Version lädt stillschweigend das capskip, das dort oben installiert ist. Auf dem Test-PC war das ein älteres 1.0.2. Ein Aufruf von deno add ohne deno.json hat sogar diese übergeordnete package.json umgeschrieben. Eine deno.json in Ihrem eigenen Ordner sorgt dafür, dass Deno Ihren Ordner als Projektwurzel behandelt.

Schritt 2: Das SDK importieren und die Einstellungen auslesen

Speichern Sie den Code aus den Schritten 2 bis 5 in der gezeigten Reihenfolge in einer Datei namens solve.js. Die ganze Datei steht gegen Ende dieses Leitfadens noch einmal. Deno führt sie als ES-Modul aus, daher scheitert der require-Aufruf aus der README des SDK hier mit require is not defined. Verwenden Sie den benannten Import. Auch ein Default-Import scheitert, weil er Ihnen das gesamte exports-Objekt liefert: new CapSkip() löst CapSkip is not a constructor aus.

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";

Das SDK liest von sich aus keine Umgebungsvariablen, daher liest das Skript sie aus und übergibt sie an den Konstruktor. Mit den Standardwerten läuft die Datei ohne jede Einrichtung. CAPSKIP_API_KEY spielt nur eine Rolle, wenn Sie in CapSkip die Validierung des API-Schlüssels eingeschaltet haben.

Schritt 3: Die Seite abrufen und ihre Cookies behalten

Das fetch von Deno hat keinen Cookie-Speicher, daher führt das Skript selbst einen. Es liest jede Set-Cookie-Zeile mit getSetCookie aus. Ein Aufruf von headers.get für set-cookie verklebt die Zeilen stattdessen zu einer einzigen Zeichenfolge. Das Expires-Datum eines Cookies enthält ein Komma, daher zerschneidet ein naives Aufteilen an Kommas die Cookies an den falschen Stellen.

// 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("; ");

Als Nächstes lädt das Skript die Login-Seite und liest daraus den sitekey und den CSRF-Token aus. Passen Sie die beiden Muster an das Markup Ihrer Seite an.

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");

Schritt 4: Bei CapSkip einen Token anfordern

Ein einziger Aufruf erledigt die Arbeit. Er übermittelt die Aufgabe, pollt auf die Antwort und gibt den Token zurück. Das SDK pollt sofort, wartet dann eine Viertelsekunde und verdoppelt die Wartezeit jedes Mal, bis zu 5 Sekunden. Ein Lösevorgang, den CapSkip in einer Sekunde abgeschlossen hatte, kam im Test nach etwa 1,8 Sekunden zurück, genauso wie unter 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);

Das Ergebnis hat ein Feld code, und das ist der Token. Die Typdatei des SDK deklariert es als String oder als Objekt, weil manche Captcha-Typen im selben Feld ein Objekt zurückgeben. String() macht daraus einen einfachen String. Ein späterer Abschnitt fügt diesem Aufruf ein Timeout hinzu.

Schritt 5: Das Formular absenden und das Ergebnis prüfen

Das Skript sendet den Token im Feld g-recaptcha-response neben den anderen Formularfeldern ab. Zwei Details sind wichtig. Das fetch von Deno sendet keinen Referer, und eine strenge Website wie Django über HTTPS lehnt einen Formular-POST ohne Referer ab, daher setzt das Skript ihn. Und der fetch-Aufruf verwendet redirect manual, damit die 302 zurückkommt und ihr neues Session-Cookie gelesen werden kann. Mit dem Standardverhalten, Weiterleitungen zu folgen, geht das Cookie der 302 verloren, und der Login landet mit einer 200 wieder auf dem Formular.

// 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);

Ein abgelehnter Login antwortet oft mit 200 und zeigt das Formular erneut. Deshalb behandelt das Skript alles außer einer 302 als Fehlschlag und sucht dann nach einem Merkmal, das nur eine Seite für angemeldete Benutzer zeigt. Ersetzen Sie Welcome back durch einen Text von Ihrer eigenen Website. Ein Skript, das bei einem abgelehnten Login Erfolg meldet, ist schlimmer als eines, das scheitert.

// 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);

Schritt 6: Mit dem kleinstmöglichen Berechtigungssatz ausführen

Deno verweigert den Zugriff auf Netzwerk, Umgebung und Dateien, bis Sie ihn erlauben. Um reCAPTCHA in Deno zu lösen, braucht dieses Skript zwei Dinge, und der Befehl unten gewährt genau diese.

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

Die env-Liste nennt die drei CAPSKIP-Variablen. Die net-Liste nennt Adresse und Port von CapSkip sowie die Website. Ersetzen Sie example.com und die Seitenadresse durch Ihre eigene Website. Nehmen Sie auch nur einen Eintrag heraus, scheitert der Lauf, und der Fehler nennt, was fehlt.

BerechtigungWas sie erlaubtWas Sie ohne sie sehen
env, drei NamenLesen der CAPSKIP-VariablenNotCapable: Requires env access to "CAPSKIP_HOST", gefolgt von dem Hinweis, den Befehl erneut mit dem Flag env auszuführen. Auch eine nicht gesetzte Variable wird verweigert, die Standardwerte helfen also nicht.
net, Host und Port von CapSkipDie Aufrufe des SDK an CapSkipEine NetworkException mit der Meldung Requires net access to "127.0.0.1:8080", gefolgt von demselben Hinweis für das Flag net.
net, die WebsiteIhre fetch-Aufrufe an die Seite und das FormularEin NotCapable-Fehler, der die Website nennt.

Ein paar Regeln zur net-Liste. Ein Host ohne Port erlaubt jeden Port, daher deckt example.com sowohl 80 als auch 443 ab. Ein Eintrag mit Port erlaubt nur diesen Port. 127.0.0.1 und 127.0.0.2 sind verschiedene Hosts. Und eine Weiterleitung auf einen anderen Hostnamen braucht diesen Host ebenfalls in der Liste. Eine Seite, die von example.com auf www.example.com weiterleitet, scheitert an der zweiten Anfrage der Weiterleitungskette, bis Sie diesen Host hinzufügen. Mehr war nicht nötig: keine Berechtigung für read, run, sys oder import.

In einem Terminal fragt Deno nach, statt zu scheitern. Es fragt einmal für jede Variable und jeden Host, und wer mit Nein antwortet, bekommt dieselben Fehler. In einem geplanten Job gibt es kein Terminal, daher scheitert es sofort. Das Flag allow-all (-A) hebt jede dieser Prüfungen auf, setzen Sie es daher nur für ein schnelles Experiment ein.

Sie können denselben Befehl als Task in die deno.json schreiben, dann tippen Sie weniger. Der erste Block unten ist die deno.json mit der Import-Map und einem Task solve, und der zweite setzt die Umgebung in PowerShell und führt ihn aus. Deno hängt die Seitenadresse, die Sie nach dem Task-Namen übergeben, hinten an.

{
  "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

Verwenden Sie für CAPSKIP_HOST die IP-Adresse. Setzen Sie einen Namen wie localhost, muss dieser Name selbst in der net-Liste stehen, sonst scheitert das SDK mit getaddrinfo EPERM localhost. Ihn aufzulisten ist ebenfalls eine Falle. Läuft CapSkip nicht, versucht Deno bei localhost die IPv6-Adresse [::1], die nicht in der Liste steht. Der Fehler wird von einem Timer ausgelöst, nicht vom Aufruf des SDK, ist also keine NetworkException, und try und catch können ihn nicht abfangen.

Einen blockierten Aufruf von einem CapSkip unterscheiden, das nicht läuft

Beide kommen als NetworkException an. Ein Handler, der immer "is CapSkip running?" meldet, schickt Sie daher in die falsche Richtung. Der Unterschied liegt in der Meldung und in der Ursache. Bei einem blockierten Aufruf beginnt die Meldung mit Requires net access, und seine Ursache ist der NotCapable-Fehler von Deno. Ist CAPSKIP_HOST ein Name, lautet die Meldung stattdessen getaddrinfo EPERM, und die Ursache hat den Code EPERM. Bei einem CapSkip, das nicht läuft, beginnt die Meldung mit connect ECONNREFUSED. Außerdem dauert es unter Deno auf Windows etwa zwei Sekunden, bis der Fehler kommt, gegenüber wenigen Millisekunden unter Node. Ein Skript, das vor der Meldung zu pausieren scheint, hängt nicht. Die Funktion unten prüft beide Arten der Blockade. Das vollständige Skript am Ende ruft sie in einem catch-Block um den gesamten Ablauf auf.

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);
}

Verhindern, dass ein stummes CapSkip das Skript hängen lässt

Das SDK setzt kein Timeout auf seine Verbindung. Nimmt CapSkip einen Aufruf an und antwortet dann nie, wartet das Skript ewig. Deno gibt nichts aus und beendet sich nicht. Die Einstellung recaptchaTimeout des SDK hilft nicht. Sie wird nur zwischen zwei Abfragen geprüft und kann daher nicht auslösen, während der Aufruf zum Übermitteln oder eine Abfrage auf eine Antwort wartet. Node verhält sich genauso. Ohne einen solchen Wrapper ist Ctrl+C der einzige Ausweg.

Setzen Sie dem Aufruf selbst eine Zeitobergrenze. Das übliche Werkzeug ist AbortSignal.timeout, und in Deno ist es hier das falsche. Ein Timeout-Signal mit einem abort-Listener hält den Prozess am Leben, bis der Timer auslöst. Ein Zeitbudget von 30 Sekunden um einen Lösevorgang, der 1,8 Sekunden gedauert hatte, ließ das Skript erst nach 30 Sekunden enden. Ein einfaches setTimeout, das Sie löschen, sobald der Aufruf abgeschlossen ist, lässt das Skript bei Erfolg sofort enden.

// 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));
}

Ersetzen Sie dann den Block aus Schritt 4 durch diesen. Er umschließt denselben Aufruf zum Lösen. Fügen Sie explain und withTimeout an beliebiger Stelle oberhalb der Zeilen ein, die sie verwenden. Das SDK gibt von selbst nach 300 Sekunden auf, ein Zeitbudget von 330 Sekunden löst also nur aus, wenn CapSkip verstummt.

// 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);

Gewinnt der Timer, ist die ausstehende Verbindung noch offen, daher endet der catch-Block im vollständigen Beispiel mit Deno.exit. Ohne es würde der Prozess weiter auf den Socket warten. Im Test endeten sowohl ein CapSkip, das das Übermitteln nie beantwortete, als auch eines, das die Abfragen des Ergebnisses nie beantwortete, nach dem Zeitbudget mit dem Fehler. Der Exit-Code war 1.

Das Skript auf Typen prüfen

Deno kann JavaScript gegen die mitgelieferten Typen des SDK prüfen, tut bei einer einfachen .js-Datei aber nichts, solange Sie es nicht verlangen. Ein deno check auf einer einfachen .js-Datei meldet nichts, selbst wenn der Code einen Typfehler hat. Erst wenn Sie checkJs in der deno.json einschalten, wird die Prüfung echt.

{
  "imports": {
    "capskip": "npm:[email protected]"
  },
  "compilerOptions": {
    "checkJs": true
  }
}
deno check solve.js

Ist die Prüfung eingeschaltet, ist eine falsch geschriebene Konstruktor-Option wie hots statt host ein Fehler. Eine falsch geschriebene Option im Aufruf zum Lösen ist es nicht, weil der Optionstyp jeden Schlüssel akzeptiert, ein Tippfehler dort wird also kompiliert und bewirkt nichts. Sehen Sie die Prüfung als Hilfe, nicht als Garantie.

Zu einer einzigen exe kompilieren

Weil die Berechtigungen Teil des Befehls sind, kann deno compile sie fest einbauen. Ein Skript, das reCAPTCHA in Deno löst, wird dann als eine einzige solve.exe ausgeliefert, die auf einem PC ohne installiertes Deno läuft. Das passt zu einem Windows-Rechner, auf dem CapSkip und ein Skript zusammen laufen.

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

Vier Dinge sollten Sie wissen. Das erste Kompilieren lädt von dl.deno.land eine Laufzeitumgebung von etwa 33 MB herunter, und die exe ist etwa 80 MB groß. Umgebungsvariablen funktionieren weiterhin, CAPSKIP_PORT wird also beachtet. Die Berechtigungen stehen zur Kompilierzeit fest, und die exe hat kein Flag, um sie zu erweitern: Übergeben Sie das Flag allow-all, wird es zum ersten Programmargument. Und in der exe sagt ein verweigerter Aufruf, dass die Berechtigung beim Kompilieren angegeben werden muss.

Ein Start nach dem Vorbild des Task Schedulers führte die exe im Test aus und endete mit Exit-Code 0. Er verwendete System32 als Arbeitsordner, keine Konsole und einen PATH ohne Deno. Ein echter geplanter Task wurde nicht registriert, lesen Sie daher den Leitfaden zum Lösen von Captchas in Windows-Task-Scheduler-Jobs zu den Einstellungen für Zeitlimit und Neustart, auf die es dort ankommt.

CapSkip auf einem anderen Rechner betreiben

Das Skript und der Solver müssen sich keinen PC teilen. Schalten Sie CapSkip in den Verbindungseinstellungen in den Server-Modus, dann lauscht es auf Ihrer Netzwerkadresse oder öffentlichen IP. Ändern Sie danach zwei Dinge: CAPSKIP_HOST und die net-Liste. Vergessen Sie die neue Adresse in der net-Liste, nennt der Fehler sie.

$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

Verwenden Sie eine statische öffentliche IP, wenn der Weg über das Internet führt. Aktivieren Sie die Validierung des API-Schlüssels, mit demselben Schlüssel in CAPSKIP_API_KEY, den auch der Block oben setzt. CapSkip öffnet seinen Port beim Start des Server-Modus für jede Adresse. Um den Zugriff auf die Rechner zu beschränken, die Sie erwarten, fügen Sie in der Windows Firewall eine Blockierregel für alle anderen hinzu. Der Solver bleibt Ihr eigener Windows-Rechner, und abgerechnet wird pro Lösung weiterhin nichts.

Das vollständige Skript

Hier ist solve.js in einem Stück, mit dem Timeout-Wrapper und dem Fehler-Handler an ihrem Platz. Speichern Sie die Datei neben der deno.json und führen Sie sie mit dem Befehl aus Schritt 6 aus.

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);
}

Häufige Fehler beim Lösen von reCAPTCHA in Deno

Ohne try-Block gibt Deno zuerst den Klassennamen aus, wie in der ersten Spalte. Das vollständige Skript gibt dieselben Meldungen nach seinem eigenen Präfix aus.

Was Sie sehenUrsacheBeheben
Requires env access to "CAPSKIP_HOST"Bei der Berechtigung env fehlt ein NameFühren Sie alle drei CAPSKIP-Namen auf
NetworkException: Requires net access to "127.0.0.1:8080"Host und Port von CapSkip stehen nicht in der net-ListeFügen Sie sie hinzu und nehmen Sie auch die Website auf
NetworkException: connect ECONNREFUSED, nach etwa 2 SekundenCapSkip läuft nicht, oder der Port ist falschStarten Sie CapSkip und prüfen Sie den Port in den Verbindungseinstellungen
NetworkException: getaddrinfo EPERM localhostEs wird ein Hostname verwendet, aber nur die IP steht in der net-ListeVerwenden Sie 127.0.0.1 in CAPSKIP_HOST
ReferenceError: require is not definedEin CommonJS-Aufruf in einem ES-ModulVerwenden Sie den benannten Import
TypeError: CapSkip is not a constructorEin Default-Import liefert das gesamte exports-ObjektImportieren Sie { CapSkip } per Name
Could not find "capskip" in a node_modules folderEine package.json oberhalb des Projekts überschreibt, wie Imports aufgelöst werdenLegen Sie eine deno.json in den Projektordner
Keine Ausgabe, kein BeendenCapSkip hat den Aufruf angenommen und nie geantwortetUmschließen Sie den Aufruf mit withTimeout und rufen Sie Deno.exit auf
ApiException: ERROR_CAPTCHA_UNSOLVABLECapSkip hat es versucht und ist gescheitertÜbermitteln Sie eine neue Aufgabe, siehe die Hinweise zu ERROR_CAPTCHA_UNSOLVABLE

FAQ

Kann ich die Flags weglassen und allow-all verwenden?

Es funktioniert, nimmt dem Einsatz von Deno aber den Sinn. Das Skript hat dann uneingeschränkten Zugriff auf Netzwerk, Dateien und Umgebung, und das gilt auch für die Pakete, die es importiert. Eine Liste mit drei Namen und eine Liste mit zwei Hosts sind kurz genug, um sie beizubehalten.

Funktioniert das auf Deno Deploy oder einer anderen gehosteten Laufzeitumgebung?

Deno Deploy und andere gehostete Laufzeitumgebungen wurden hier nicht getestet. Ein Server in einem fremden Rechenzentrum kann 127.0.0.1 auf Ihrem PC nicht erreichen. Betreiben Sie CapSkip also im Server-Modus auf einem Windows-Rechner, den Sie kontrollieren, mit einer statischen öffentlichen IP, und richten Sie CAPSKIP_HOST darauf aus. Die Plattform muss außerdem ausgehende Verbindungen zu dieser Adresse und diesem Port erlauben, prüfen Sie also ihre Netzwerkregeln. Der Leitfaden zum Lösen von Captchas in Netlify Functions behandelt dasselbe Setup im Server-Modus aus einer gehosteten Funktion.

Warum das SDK verwenden, statt CapSkip mit fetch aufzurufen?

CapSkip spricht die 2captcha-API, daher funktioniert fetch gegen in.php und res.php. Dann schreiben Sie die Polling-Schleife, das Back-off und die Fehlerzuordnung selbst. Das SDK pollt bereits nach einem Zeitplan, der bei einer Viertelsekunde beginnt, gibt den Token zurück und löst ApiException, NetworkException und TimeoutException aus. Der Preis sind zwei Details: Die Berechtigungsfehler kommen verpackt an, und das SDK setzt kein Socket-Timeout.

Funktionieren andere Captcha-Typen in Deno genauso?

Für diesen Leitfaden wurde nur reCAPTCHA v2 unter Deno ausgeführt. Die Token-Typen wie Turnstile und GeeTest rufen dieselben Endpunkte auf, daher sollten dieselben zwei Flags gelten, das wurde aber nicht getestet. Ein Bild-Captcha, das als Dateipfad übergeben wird, braucht außerdem die Berechtigung read, und eines, das als URL übergeben wird, braucht diese Website in der net-Liste. Lesen Sie die Optionen für jeden Typ auf der Node.js-Captcha-Solver-Seite nach, bevor Sie einen Aufruf kopieren.

Die Kurzfassung

Um reCAPTCHA in Deno zu lösen, legen Sie eine deno.json mit dem SDK-Import in Ihren eigenen Ordner und importieren Sie den benannten Export. Lesen Sie die CapSkip-Einstellungen selbst aus. Führen Sie das Skript mit der env-Liste und einer net-Liste aus, die die Adresse von CapSkip und Ihre Website enthält, und nichts darüber hinaus. Unterscheiden Sie einen verweigerten Aufruf von einem gestoppten CapSkip anhand der Ursache. Begrenzen Sie den Lösevorgang mit einem einfachen Timer, denn AbortSignal.timeout hält den Prozess offen, und ein stummes CapSkip lässt ihn sonst für immer hängen.

Ein Skript, das Sie einmal kompilieren und planen, kommt auf Tausende Lösungen im Monat. Wenn Sie sie mithilfe unbegrenzter Captcha-Löser auf Ihrem eigenen Windows-Rechner laufen lassen, wird aus dieser Zahl nie eine Rechnung.