Referenz

VS-Code-Erweiterung für die CaptchaAI-API-Entwicklung

Eine auf die CaptchaAI-Entwicklung zugeschnittene VS-Code-Erweiterung kann Ihren Arbeitsablauf beschleunigen – erkennen Sie Sitekeys im Code, testen Sie Lösungen im Editor, zeigen Sie Ihren Kontostand in der Statusleiste an und fügen Sie Codeausschnitte für gängige API-Muster ein.

Der wichtigste Architekturentscheid davor ist jedoch einfacher: Brauchen Sie wirklich eine Erweiterung oder genügt ein Skript, CLI-Tool oder kleines internes UI? Eine Erweiterung lohnt sich vor allem dann, wenn der Solve-Workflow regelmäßig direkt im Editor stattfindet.

Wann ist eine Erweiterung sinnvoll, wann nicht?

Situation VS-Code-Erweiterung sinnvoll Skript oder internes Tool oft besser
Entwickler testen Solve-Flows ständig im Editor Ja
Sie wollen Sitekeys, Snippets und Balance direkt neben dem Code Ja
Nur ein gelegentlicher API-Test ist nötig Kleines Skript reicht oft
Der Workflow läuft primär in CI, Browsern oder Backends Editor-Erweiterung bringt dann wenig Mehrwert

Erweiterungsfunktionen

Funktion Was es tut
Statusleiste des Guthabens Zeigt den aktuellen CaptchaAI-Saldo auf einen Blick
Befehl lösen Senden Sie eine CAPTCHA-Lösung direkt aus VS Code
Sitekey-Erkennung Markieren Sie Sitekeys und extrahieren Sie sie aus geöffneten Dateien
Codeausschnitte Fügen Sie ein Boilerplate für reCAPTCHA-, Turnstile- und hCaptcha-API-Aufrufe ein
Fehlersuche Bewegen Sie den Mauszeiger über die Fehlercodes, um Beschreibungen anzuzeigen

Erweiterungsstruktur

captchaai-vscode/
├── package.json
├── src/
│   └── extension.js
├── snippets/
│   ├── python.json
│   └── javascript.json
└── README.md

package.json

{
  "name": "captchaai-dev-tools",
  "displayName": "CaptchaAI Dev Tools",
  "description": "CaptchaAI API development tools for VS Code",
  "version": "1.0.0",
  "engines": { "vscode": "^1.80.0" },
  "categories": ["Snippets", "Other"],
  "activationEvents": ["onStartupFinished"],
  "main": "./src/extension.js",
  "contributes": {
    "commands": [
      {
        "command": "captchaai.checkBalance",
        "title": "CaptchaAI: Check Balance"
      },
      {
        "command": "captchaai.solveRecaptcha",
        "title": "CaptchaAI: Solve reCAPTCHA v2"
      },
      {
        "command": "captchaai.solveTurnstile",
        "title": "CaptchaAI: Solve Turnstile"
      },
      {
        "command": "captchaai.detectSitekey",
        "title": "CaptchaAI: Detect Sitekey in File"
      }
    ],
    "configuration": {
      "title": "CaptchaAI",
      "properties": {
        "captchaai.apiKey": {
          "type": "string",
          "default": "",
          "description": "Your CaptchaAI API key"
        },
        "captchaai.showBalance": {
          "type": "boolean",
          "default": true,
          "description": "Show balance in status bar"
        },
        "captchaai.pollInterval": {
          "type": "number",
          "default": 5,
          "description": "Poll interval in seconds"
        }
      }
    },
    "snippets": [
      {
        "language": "python",
        "path": "./snippets/python.json"
      },
      {
        "language": "javascript",
        "path": "./snippets/javascript.json"
      }
    ]
  }
}

Erweiterungsimplementierung

// src/extension.js
const vscode = require("vscode");

const API_BASE = "https://ocr.captchaai.com";

function getApiKey() {
  const config = vscode.workspace.getConfiguration("captchaai");
  const key = config.get("apiKey");
  if (!key) {
    vscode.window.showErrorMessage(
      "CaptchaAI: Set your API key in Settings → CaptchaAI"
    );
    return null;
  }
  return key;
}

// --- Balance Status Bar ---

let balanceStatusBar;
let balanceInterval;

async function updateBalance() {
  const key = getApiKey();
  if (!key) return;

  try {
    const url = new URL(`${API_BASE}/res.php`);
    url.searchParams.set("key", key);
    url.searchParams.set("action", "getbalance");
    url.searchParams.set("json", "1");

    const response = await fetch(url);
    const result = await response.json();

    if (result.status === 1) {
      const balance = parseFloat(result.request).toFixed(2);
      balanceStatusBar.text = `$(credit-card) CaptchaAI: $${balance}`;
      balanceStatusBar.tooltip = `CaptchaAI Balance: $${balance}`;
    } else {
      balanceStatusBar.text = "$(warning) CaptchaAI: Error";
    }
  } catch {
    balanceStatusBar.text = "$(warning) CaptchaAI: Offline";
  }
}

// --- Solve Command ---

async function solveCaptcha(method, extraFields) {
  const key = getApiKey();
  if (!key) return;

  const sitekey = await vscode.window.showInputBox({
    prompt: "Enter the CAPTCHA sitekey",
    placeHolder: "6LeIxAcTAAAAAJcZ...",
  });
  if (!sitekey) return;

  const pageurl = await vscode.window.showInputBox({
    prompt: "Enter the page URL",
    placeHolder: "https://example.com",
  });
  if (!pageurl) return;

  const params = {
    key,
    method,
    pageurl,
    json: 1,
    ...extraFields,
  };

  if (method === "userrecaptcha") {
    params.googlekey = sitekey;
  } else {
    params.sitekey = sitekey;
  }

  // Submit
  vscode.window.withProgress(
    {
      location: vscode.ProgressLocation.Notification,
      title: "CaptchaAI: Solving...",
      cancellable: true,
    },
    async (progress, cancellation) => {
      try {
        const submitResponse = await fetch(`${API_BASE}/in.php`, {
          method: "POST",
          body: new URLSearchParams(params),
        });
        const submitResult = await submitResponse.json();

        if (submitResult.status !== 1) {
          vscode.window.showErrorMessage(
            `CaptchaAI: ${submitResult.request || "Submit failed"}`
          );
          return;
        }

        const taskId = submitResult.request;
        progress.report({ message: `Task ${taskId} submitted` });

        // Poll
        const config = vscode.workspace.getConfiguration("captchaai");
        const interval = config.get("pollInterval") * 1000;

        for (let i = 0; i < 60; i++) {
          if (cancellation.isCancellationRequested) return;

          await new Promise((r) => setTimeout(r, interval));

          const pollUrl = new URL(`${API_BASE}/res.php`);
          pollUrl.searchParams.set("key", key);
          pollUrl.searchParams.set("action", "get");
          pollUrl.searchParams.set("id", taskId);
          pollUrl.searchParams.set("json", "1");

          const pollResponse = await fetch(pollUrl);
          const pollResult = await pollResponse.json();

          if (pollResult.request === "CAPCHA_NOT_READY") {
            progress.report({ message: `Waiting... (${(i + 1) * (interval / 1000)}s)` });
            continue;
          }

          if (pollResult.status === 1) {
            const token = pollResult.request;

            // Copy to clipboard
            await vscode.env.clipboard.writeText(token);
            vscode.window.showInformationMessage(
              `CaptchaAI: Solved! Token copied to clipboard (${token.length} chars)`
            );

            // Also insert at cursor if editor is active
            const editor = vscode.window.activeTextEditor;
            if (editor) {
              const action = await vscode.window.showQuickPick(
                ["Copy only", "Insert at cursor"],
                { placeHolder: "Token copied. Insert into editor?" }
              );
              if (action === "Insert at cursor") {
                editor.edit((editBuilder) => {
                  editBuilder.insert(editor.selection.active, token);
                });
              }
            }
            return;
          }

          vscode.window.showErrorMessage(
            `CaptchaAI: ${pollResult.request || "Solve failed"}`
          );
          return;
        }

        vscode.window.showErrorMessage("CaptchaAI: Solve timed out");
      } catch (err) {
        vscode.window.showErrorMessage(`CaptchaAI: ${err.message}`);
      }
    }
  );
}

// --- Sitekey Detection ---

async function detectSitekey() {
  const editor = vscode.window.activeTextEditor;
  if (!editor) {
    vscode.window.showWarningMessage("No active editor");
    return;
  }

  const text = editor.document.getText();
  const patterns = [
    { regex: /data-sitekey=["']([^"']+)["']/g, type: "HTML data-sitekey" },
    { regex: /googlekey['":\s]+["']([a-zA-Z0-9_-]{40})["']/g, type: "API googlekey" },
    { regex: /sitekey['":\s]+["']([a-zA-Z0-9_-]{20,})["']/g, type: "sitekey parameter" },
    { regex: /render=([a-zA-Z0-9_-]{40})/g, type: "reCAPTCHA render" },
  ];

  const found = [];
  for (const { regex, type } of patterns) {
    let match;
    while ((match = regex.exec(text)) !== null) {
      found.push({ key: match[1], type, position: match.index });
    }
  }

  if (found.length === 0) {
    vscode.window.showInformationMessage("No sitekeys found in current file");
    return;
  }

  const items = found.map((f) => ({
    label: f.key,
    description: f.type,
    detail: `Position: ${f.position}`,
    key: f.key,
  }));

  const selected = await vscode.window.showQuickPick(items, {
    placeHolder: `Found ${found.length} sitekey(s) — select to copy`,
  });

  if (selected) {
    await vscode.env.clipboard.writeText(selected.key);
    vscode.window.showInformationMessage(`Sitekey copied: ${selected.key}`);
  }
}

// --- Activation ---

function activate(context) {
  // Balance status bar
  const config = vscode.workspace.getConfiguration("captchaai");

  if (config.get("showBalance")) {
    balanceStatusBar = vscode.window.createStatusBarItem(
      vscode.StatusBarAlignment.Right,
      100
    );
    balanceStatusBar.command = "captchaai.checkBalance";
    balanceStatusBar.text = "$(credit-card) CaptchaAI";
    balanceStatusBar.show();

    updateBalance();
    balanceInterval = setInterval(updateBalance, 300000); // Every 5 minutes

    context.subscriptions.push(balanceStatusBar);
  }

  // Register commands
  context.subscriptions.push(
    vscode.commands.registerCommand("captchaai.checkBalance", async () => {
      await updateBalance();
      vscode.window.showInformationMessage(balanceStatusBar.tooltip);
    }),

    vscode.commands.registerCommand("captchaai.solveRecaptcha", () => {
      solveCaptcha("userrecaptcha", {});
    }),

    vscode.commands.registerCommand("captchaai.solveTurnstile", () => {
      solveCaptcha("turnstile", {});
    }),

    vscode.commands.registerCommand("captchaai.detectSitekey", detectSitekey)
  );
}

function deactivate() {
  if (balanceInterval) clearInterval(balanceInterval);
}

module.exports = { activate, deactivate };

Codeausschnitte

Python-Schnipsel

{
  "CaptchaAI reCAPTCHA v2": {
    "prefix": "cai-recaptcha-v2",
    "body": [
      "import requests",
      "",
      "# Submit reCAPTCHA v2 task",
      "response = requests.post(",
      "    \"https://ocr.captchaai.com/in.php\",",
      "    data={",
      "        \"key\": \"${1:YOUR_API_KEY}\",",
      "        \"method\": \"userrecaptcha\",",
      "        \"googlekey\": \"${2:SITE_KEY}\",",
      "        \"pageurl\": \"${3:https://example.com}\",",
      "        \"json\": 1,",
      "    },",
      ")",
      "task_id = response.json()[\"request\"]",
      "",
      "# Poll for result",
      "import time",
      "while True:",
      "    time.sleep(5)",
      "    result = requests.get(",
      "        \"https://ocr.captchaai.com/res.php\",",
      "        params={\"key\": \"${1}\", \"action\": \"get\", \"id\": task_id, \"json\": 1},",
      "    ).json()",
      "    if result[\"request\"] != \"CAPCHA_NOT_READY\":",
      "        token = result[\"request\"]",
      "        break"
    ],
    "description": "CaptchaAI reCAPTCHA v2 solve"
  },
  "CaptchaAI Turnstile": {
    "prefix": "cai-turnstile",
    "body": [
      "import requests",
      "",
      "response = requests.post(",
      "    \"https://ocr.captchaai.com/in.php\",",
      "    data={",
      "        \"key\": \"${1:YOUR_API_KEY}\",",
      "        \"method\": \"turnstile\",",
      "        \"sitekey\": \"${2:SITE_KEY}\",",
      "        \"pageurl\": \"${3:https://example.com}\",",
      "        \"json\": 1,",
      "    },",
      ")",
      "task_id = response.json()[\"request\"]"
    ],
    "description": "CaptchaAI Turnstile solve"
  },
  "CaptchaAI Balance Check": {
    "prefix": "cai-balance",
    "body": [
      "import requests",
      "",
      "balance = requests.get(",
      "    \"https://ocr.captchaai.com/res.php\",",
      "    params={\"key\": \"${1:YOUR_API_KEY}\", \"action\": \"getbalance\", \"json\": 1},",
      ").json()",
      "print(f\"Balance: \\${balance['request']}\")"
    ],
    "description": "CaptchaAI balance check"
  }
}

JavaScript-Schnipsel

{
  "CaptchaAI reCAPTCHA v2": {
    "prefix": "cai-recaptcha-v2",
    "body": [
      "const response = await fetch('https://ocr.captchaai.com/in.php', {",
      "  method: 'POST',",
      "  body: new URLSearchParams({",
      "    key: '${1:YOUR_API_KEY}',",
      "    method: 'userrecaptcha',",
      "    googlekey: '${2:SITE_KEY}',",
      "    pageurl: '${3:https://example.com}',",
      "    json: 1,",
      "  }),",
      "});",
      "const { request: taskId } = await response.json();",
      "",
      "// Poll for result",
      "let token;",
      "while (true) {",
      "  await new Promise(r => setTimeout(r, 5000));",
      "  const url = new URL('https://ocr.captchaai.com/res.php');",
      "  url.searchParams.set('key', '${1}');",
      "  url.searchParams.set('action', 'get');",
      "  url.searchParams.set('id', taskId);",
      "  url.searchParams.set('json', '1');",
      "  const result = await (await fetch(url)).json();",
      "  if (result.request !== 'CAPCHA_NOT_READY') {",
      "    token = result.request;",
      "    break;",
      "  }",
      "}"
    ],
    "description": "CaptchaAI reCAPTCHA v2 solve"
  }
}

Fehlerbehebung

Problem Ursache Lösung
Ergebnis passt nicht zum eigenen Fall Solver-Typ oder Eingabeparameter wurden falsch auf den Zieltyp gemappt Vergleiche Zielseite, Solver-Methode und Pflichtparameter noch einmal systematisch
Beispiel läuft, aber Produktion scheitert Session, Header oder Proxy-Kontext weichen vom Test ab Übertrage erfolgreiche Testbedingungen möglichst unverändert in den Live-Workflow
Fehler bleiben unklar Logs enthalten zu wenig Kontext für eine belastbare Diagnose Protokolliere Solver-Typ, Latenz, Fehlercode und Downstream-Reaktion gemeinsam

Verwandte Leitfäden

Kommentare sind für diesen Artikel deaktiviert.