Skip to content

Commit f0d4e08

Browse files
committed
Add example templates for various programming languages (Bash, C#, Go, Kotlin, Ruby, Rust) demonstrating CAPTCHA solving using the CaptchaAI API, including environment variable handling and error classification.
1 parent 6a7358f commit f0d4e08

9 files changed

Lines changed: 853 additions & 0 deletions

File tree

templates/bash-basic/solve.sh

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
#!/usr/bin/env bash
2+
# Solve {CAPTCHA_TYPE} using the CaptchaAI API.
3+
#
4+
# Usage:
5+
# chmod +x solve.sh
6+
# ./solve.sh
7+
#
8+
# Requires .env file in parent directory with:
9+
# CAPTCHAAI_API_KEY and CAPTCHA-specific variables
10+
#
11+
# Dependencies: curl, jq
12+
13+
set -euo pipefail
14+
15+
SUBMIT_URL="https://ocr.captchaai.com/in.php"
16+
RESULT_URL="https://ocr.captchaai.com/res.php"
17+
18+
# Load .env file
19+
ENV_FILE="$(dirname "$0")/../.env"
20+
if [[ -f "$ENV_FILE" ]]; then
21+
while IFS='=' read -r key value; do
22+
key=$(echo "$key" | xargs)
23+
[[ -z "$key" || "$key" == \#* ]] && continue
24+
value=$(echo "$value" | xargs)
25+
export "$key=$value"
26+
done < "$ENV_FILE"
27+
fi
28+
29+
CAPTCHAAI_API_KEY="${CAPTCHAAI_API_KEY:-}"
30+
# TODO: Add CAPTCHA-specific variables here
31+
POLL_INTERVAL="${POLL_INTERVAL:-5}"
32+
MAX_TIMEOUT="${MAX_TIMEOUT:-120}"
33+
34+
AUTH_ERRORS="ERROR_WRONG_USER_KEY ERROR_KEY_DOES_NOT_EXIST IP_BANNED"
35+
BALANCE_ERRORS="ERROR_ZERO_BALANCE"
36+
INPUT_ERRORS="ERROR_PAGEURL ERROR_WRONG_GOOGLEKEY ERROR_BAD_PARAMETERS ERROR_BAD_TOKEN_OR_PAGEURL"
37+
TRANSIENT_ERRORS="ERROR_SERVER_ERROR ERROR_INTERNAL_SERVER_ERROR"
38+
SOLVE_ERRORS="ERROR_CAPTCHA_UNSOLVABLE"
39+
PROXY_ERRORS="ERROR_BAD_PROXY ERROR_PROXY_CONNECTION_FAILED"
40+
41+
contains() { [[ " $1 " == *" $2 "* ]]; }
42+
43+
classify_error() {
44+
local error="$1"
45+
if contains "$AUTH_ERRORS" "$error"; then
46+
echo "[!] Authentication error: $error"
47+
echo " Check your API key at https://captchaai.com/dashboard"
48+
elif contains "$BALANCE_ERRORS" "$error"; then
49+
echo "[!] Balance error: $error"
50+
echo " Top up your account at https://captchaai.com"
51+
elif contains "$INPUT_ERRORS" "$error"; then
52+
echo "[!] Input error: $error"
53+
echo " Verify your sitekey and page URL are correct."
54+
elif contains "$PROXY_ERRORS" "$error"; then
55+
echo "[!] Proxy error: $error"
56+
echo " Check your proxy configuration or try a different proxy."
57+
else
58+
echo "[!] Submission failed: $error"
59+
fi
60+
}
61+
62+
# Validate
63+
if [[ -z "$CAPTCHAAI_API_KEY" || "$CAPTCHAAI_API_KEY" == "YOUR_API_KEY" ]]; then
64+
echo "[!] ERROR: CAPTCHAAI_API_KEY is not set."
65+
echo " Copy .env.example to .env and add your real API key."
66+
exit 1
67+
fi
68+
69+
# Check dependencies
70+
if ! command -v jq &> /dev/null; then
71+
echo "[!] ERROR: jq is required but not installed."
72+
echo " Install it: apt-get install jq / brew install jq / choco install jq"
73+
exit 1
74+
fi
75+
76+
# Submit
77+
echo "[*] Submitting CAPTCHA task..."
78+
# TODO: Change method and add CAPTCHA-specific parameters
79+
SUBMIT_RESPONSE=$(curl -s --max-time 30 \
80+
"${SUBMIT_URL}?key=${CAPTCHAAI_API_KEY}&method=userrecaptcha&json=1")
81+
82+
if [[ -z "$SUBMIT_RESPONSE" ]]; then
83+
echo "[!] Network error during submission."
84+
exit 1
85+
fi
86+
87+
SUBMIT_STATUS=$(echo "$SUBMIT_RESPONSE" | jq -r '.status // 0')
88+
SUBMIT_REQUEST=$(echo "$SUBMIT_RESPONSE" | jq -r '.request // "UNKNOWN_ERROR"')
89+
90+
if [[ "$SUBMIT_STATUS" != "1" ]]; then
91+
classify_error "$SUBMIT_REQUEST"
92+
exit 1
93+
fi
94+
95+
TASK_ID="$SUBMIT_REQUEST"
96+
echo "[+] Task submitted. ID: $TASK_ID"
97+
98+
# Poll
99+
echo "[*] Waiting 15s before first poll..."
100+
sleep 15
101+
102+
ELAPSED=15
103+
ATTEMPT=0
104+
BACKOFF=$POLL_INTERVAL
105+
106+
while [[ $ELAPSED -lt $MAX_TIMEOUT ]]; do
107+
ATTEMPT=$((ATTEMPT + 1))
108+
echo "[*] Polling for result (attempt $ATTEMPT)..."
109+
110+
POLL_RESPONSE=$(curl -s --max-time 30 \
111+
"${RESULT_URL}?key=${CAPTCHAAI_API_KEY}&action=get&id=${TASK_ID}&json=1" 2>/dev/null || echo "")
112+
113+
if [[ -z "$POLL_RESPONSE" ]]; then
114+
echo "[!] Network error during polling."
115+
sleep "$BACKOFF"
116+
ELAPSED=$((ELAPSED + BACKOFF))
117+
BACKOFF=$((BACKOFF * 2))
118+
[[ $BACKOFF -gt 30 ]] && BACKOFF=30
119+
continue
120+
fi
121+
122+
POLL_STATUS=$(echo "$POLL_RESPONSE" | jq -r '.status // 0')
123+
POLL_REQUEST=$(echo "$POLL_RESPONSE" | jq -r '.request // ""')
124+
125+
if [[ "$POLL_STATUS" == "1" ]]; then
126+
TOKEN="$POLL_REQUEST"
127+
TRUNCATED="${TOKEN:0:50}"
128+
echo "[+] Solved! Token: ${TRUNCATED}..."
129+
echo "[+] Full token length: ${#TOKEN} characters"
130+
exit 0
131+
fi
132+
133+
if [[ "$POLL_REQUEST" == "CAPCHA_NOT_READY" ]]; then
134+
echo "[*] Not ready yet, waiting ${POLL_INTERVAL}s..."
135+
sleep "$POLL_INTERVAL"
136+
ELAPSED=$((ELAPSED + POLL_INTERVAL))
137+
BACKOFF=$POLL_INTERVAL
138+
continue
139+
fi
140+
if contains "$TRANSIENT_ERRORS" "$POLL_REQUEST"; then
141+
echo "[!] Transient error: $POLL_REQUEST, retrying in ${BACKOFF}s..."
142+
sleep "$BACKOFF"
143+
ELAPSED=$((ELAPSED + BACKOFF))
144+
BACKOFF=$((BACKOFF * 2))
145+
[[ $BACKOFF -gt 30 ]] && BACKOFF=30
146+
continue
147+
fi
148+
if contains "$SOLVE_ERRORS" "$POLL_REQUEST"; then
149+
echo "[!] Solve error: $POLL_REQUEST"
150+
echo " The CAPTCHA could not be solved. Verify parameters and retry."
151+
exit 1
152+
fi
153+
if contains "$PROXY_ERRORS" "$POLL_REQUEST"; then
154+
echo "[!] Proxy error: $POLL_REQUEST"
155+
echo " Check your proxy configuration or try a different proxy."
156+
exit 1
157+
fi
158+
159+
echo "[!] Unexpected error: $POLL_REQUEST"
160+
exit 1
161+
done
162+
163+
echo "[!] Timeout: no solution received within ${MAX_TIMEOUT} seconds."
164+
exit 1

templates/csharp-basic/Solve.cs

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
// Solve {CAPTCHA_TYPE} using the CaptchaAI API.
2+
//
3+
// Usage:
4+
// dotnet run
5+
//
6+
// Requires .env file in parent directory with:
7+
// CAPTCHAAI_API_KEY and CAPTCHA-specific variables
8+
9+
using System;
10+
using System.Collections.Generic;
11+
using System.IO;
12+
using System.Net.Http;
13+
using System.Text.Json;
14+
using System.Threading.Tasks;
15+
16+
class Solve
17+
{
18+
private const string SubmitUrl = "https://ocr.captchaai.com/in.php";
19+
private const string ResultUrl = "https://ocr.captchaai.com/res.php";
20+
21+
private static readonly HashSet<string> AuthErrors = new()
22+
{ "ERROR_WRONG_USER_KEY", "ERROR_KEY_DOES_NOT_EXIST", "IP_BANNED" };
23+
private static readonly HashSet<string> BalanceErrors = new()
24+
{ "ERROR_ZERO_BALANCE" };
25+
private static readonly HashSet<string> InputErrors = new()
26+
{ "ERROR_PAGEURL", "ERROR_WRONG_GOOGLEKEY", "ERROR_BAD_PARAMETERS", "ERROR_BAD_TOKEN_OR_PAGEURL" };
27+
private static readonly HashSet<string> TransientErrors = new()
28+
{ "ERROR_SERVER_ERROR", "ERROR_INTERNAL_SERVER_ERROR" };
29+
private static readonly HashSet<string> SolveErrors = new()
30+
{ "ERROR_CAPTCHA_UNSOLVABLE" };
31+
private static readonly HashSet<string> ProxyErrors = new()
32+
{ "ERROR_BAD_PROXY", "ERROR_PROXY_CONNECTION_FAILED" };
33+
34+
private static readonly HttpClient Client = new() { Timeout = TimeSpan.FromSeconds(30) };
35+
36+
private readonly string _apiKey;
37+
private readonly int _pollInterval;
38+
private readonly int _maxTimeout;
39+
// TODO: Add CAPTCHA-specific fields
40+
41+
public Solve(Dictionary<string, string> env)
42+
{
43+
_apiKey = GetEnv(env, "CAPTCHAAI_API_KEY", "");
44+
// TODO: Add CAPTCHA-specific environment variables
45+
_pollInterval = int.Parse(GetEnv(env, "POLL_INTERVAL", "5"));
46+
_maxTimeout = int.Parse(GetEnv(env, "MAX_TIMEOUT", "120"));
47+
}
48+
49+
private static string GetEnv(Dictionary<string, string> env, string key, string fallback)
50+
{
51+
if (env.TryGetValue(key, out var val) && !string.IsNullOrEmpty(val)) return val;
52+
var sysVal = Environment.GetEnvironmentVariable(key);
53+
return !string.IsNullOrEmpty(sysVal) ? sysVal : fallback;
54+
}
55+
56+
private void ValidateConfig()
57+
{
58+
if (string.IsNullOrEmpty(_apiKey) || _apiKey == "YOUR_API_KEY")
59+
{
60+
Console.WriteLine("[!] ERROR: CAPTCHAAI_API_KEY is not set.");
61+
Console.WriteLine(" Copy .env.example to .env and add your real API key.");
62+
Environment.Exit(1);
63+
}
64+
}
65+
66+
private async Task<string> SubmitTaskAsync()
67+
{
68+
Console.WriteLine("[*] Submitting CAPTCHA task...");
69+
var query = $"key={Uri.EscapeDataString(_apiKey)}" +
70+
$"&method=userrecaptcha" + // TODO: Change method for your CAPTCHA type
71+
// TODO: Add CAPTCHA-specific parameters
72+
$"&json=1";
73+
74+
string body;
75+
try { body = await Client.GetStringAsync($"{SubmitUrl}?{query}"); }
76+
catch (Exception ex)
77+
{
78+
Console.WriteLine($"[!] Network error during submission: {ex.Message}");
79+
Environment.Exit(1); return "";
80+
}
81+
82+
using var doc = JsonDocument.Parse(body);
83+
var root = doc.RootElement;
84+
var status = root.GetProperty("status").GetInt32();
85+
var request = root.GetProperty("request").ToString();
86+
87+
if (status != 1) { ClassifyError(request); Environment.Exit(1); }
88+
89+
Console.WriteLine($"[+] Task submitted. ID: {request}");
90+
return request;
91+
}
92+
93+
private async Task<string> PollResultAsync(string taskId)
94+
{
95+
Console.WriteLine("[*] Waiting 15s before first poll...");
96+
await Task.Delay(15000);
97+
98+
var query = $"key={Uri.EscapeDataString(_apiKey)}&action=get&id={Uri.EscapeDataString(taskId)}&json=1";
99+
var elapsed = 15; var attempt = 0; var backoff = _pollInterval;
100+
101+
while (elapsed < _maxTimeout)
102+
{
103+
attempt++;
104+
Console.WriteLine($"[*] Polling for result (attempt {attempt})...");
105+
106+
string body;
107+
try { body = await Client.GetStringAsync($"{ResultUrl}?{query}"); }
108+
catch (Exception ex)
109+
{
110+
Console.WriteLine($"[!] Network error during polling: {ex.Message}");
111+
await Task.Delay(backoff * 1000);
112+
elapsed += backoff; backoff = Math.Min(backoff * 2, 30); continue;
113+
}
114+
115+
using var doc = JsonDocument.Parse(body);
116+
var root = doc.RootElement;
117+
var status = root.GetProperty("status").GetInt32();
118+
var request = root.GetProperty("request").ToString();
119+
120+
if (status == 1) return request;
121+
if (request == "CAPCHA_NOT_READY")
122+
{
123+
Console.WriteLine($"[*] Not ready yet, waiting {_pollInterval}s...");
124+
await Task.Delay(_pollInterval * 1000);
125+
elapsed += _pollInterval; backoff = _pollInterval; continue;
126+
}
127+
if (TransientErrors.Contains(request))
128+
{
129+
Console.WriteLine($"[!] Transient error: {request}, retrying in {backoff}s...");
130+
await Task.Delay(backoff * 1000);
131+
elapsed += backoff; backoff = Math.Min(backoff * 2, 30); continue;
132+
}
133+
if (SolveErrors.Contains(request))
134+
{
135+
Console.WriteLine($"[!] Solve error: {request}");
136+
Console.WriteLine(" The CAPTCHA could not be solved. Verify parameters and retry.");
137+
Environment.Exit(1);
138+
}
139+
if (ProxyErrors.Contains(request))
140+
{
141+
Console.WriteLine($"[!] Proxy error: {request}");
142+
Console.WriteLine(" Check your proxy configuration or try a different proxy.");
143+
Environment.Exit(1);
144+
}
145+
Console.WriteLine($"[!] Unexpected error: {request}");
146+
Environment.Exit(1);
147+
}
148+
149+
Console.WriteLine($"[!] Timeout: no solution received within {_maxTimeout} seconds.");
150+
Environment.Exit(1); return "";
151+
}
152+
153+
private static void ClassifyError(string error)
154+
{
155+
if (AuthErrors.Contains(error)) { Console.WriteLine($"[!] Authentication error: {error}"); Console.WriteLine(" Check your API key at https://captchaai.com/dashboard"); }
156+
else if (BalanceErrors.Contains(error)) { Console.WriteLine($"[!] Balance error: {error}"); Console.WriteLine(" Top up your account at https://captchaai.com"); }
157+
else if (InputErrors.Contains(error)) { Console.WriteLine($"[!] Input error: {error}"); Console.WriteLine(" Verify your sitekey and page URL are correct."); }
158+
else if (ProxyErrors.Contains(error)) { Console.WriteLine($"[!] Proxy error: {error}"); Console.WriteLine(" Check your proxy configuration or try a different proxy."); }
159+
else Console.WriteLine($"[!] Submission failed: {error}");
160+
}
161+
162+
private static Dictionary<string, string> LoadEnv(string path)
163+
{
164+
var env = new Dictionary<string, string>();
165+
if (!File.Exists(path)) return env;
166+
foreach (var line in File.ReadAllLines(path))
167+
{
168+
var trimmed = line.Trim();
169+
if (string.IsNullOrEmpty(trimmed) || trimmed.StartsWith('#')) continue;
170+
var idx = trimmed.IndexOf('=');
171+
if (idx > 0) env[trimmed[..idx].Trim()] = trimmed[(idx + 1)..].Trim();
172+
}
173+
return env;
174+
}
175+
176+
public static async Task Main(string[] args)
177+
{
178+
var env = LoadEnv(Path.Combine("..", ".env"));
179+
var solver = new Solve(env);
180+
solver.ValidateConfig();
181+
var taskId = await solver.SubmitTaskAsync();
182+
var token = await solver.PollResultAsync(taskId);
183+
var truncated = token.Length > 50 ? token[..50] : token;
184+
Console.WriteLine($"[+] Solved! Token: {truncated}...");
185+
Console.WriteLine($"[+] Full token length: {token.Length} characters");
186+
}
187+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<TargetFramework>net8.0</TargetFramework>
5+
</PropertyGroup>
6+
</Project>

templates/go-basic/go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module captchaai-example
2+
3+
go 1.21

0 commit comments

Comments
 (0)