دروس API

Bash + cURL + CaptchaAI: أتمتة CAPTCHA من سطر الأوامر

لحلّ أي اختبار CAPTCHA من داخل سكربت Bash لا تحتاج إلى لغة برمجة كاملة ولا إلى إطار عمل؛ يكفيك أمران: curl لإرسال المهمة إلى CaptchaAI، وjq لقراءة الرمز من الاستجابة. بهذا تتحوّل الطرفية وحدها إلى بيئة عمل مكتملة لأتمتة CAPTCHA على أي خادم Linux أو macOS.

وعندما تصطدم مهام cron أو خطوط CI/CD أو سكربتات المراقبة الخفيفة باختبار CAPTCHA، يمكنك استدعاء واجهة CaptchaAI مباشرة عبر أوامر HTTP من دون إضافة وقت تشغيل جديد. يغطّي هذا الدليل حل reCAPTCHA v2/v3 وCloudflare Turnstile وCAPTCHA الصورية باستخدام Bash فقط، مع دوال جاهزة يمكنك تجميعها في مكتبة واحدة.

يمر كل حل بأربع خطوات ثابتة مهما اختلف نوع CAPTCHA:

  1. أرسل المهمة إلى in.php واحصل على معرّف الطلب.
  2. احفظ المعرّف الذي تعيده الاستجابة.
  3. استطلع res.php دوريًا حتى تتغيّر الحالة إلى جاهز.
  4. استخدم الرمز الناتج في طلبك التالي إلى الموقع المستهدف.

متى تعتمد على Bash وcURL في أتمتة CAPTCHA

الجواب المختصر: حين تريد أخف مسار ممكن بلا تبعيات. هذه أبرز الحالات التي يتفوق فيها هذا النهج:

  • من دون تبعيات إضافية — يأتي Bash وcURL افتراضيًا مع معظم أنظمة Linux وmacOS
  • خفيف تشغيليًا — لا وقت تشغيل إضافي ولا مدير حزم ولا خطوة تثبيت
  • مناسب لـ cron — تجدول المهام المعتمدة على CAPTCHA بأدوات cron التقليدية
  • جاهز لبيئات CI/CD — يعمل داخل Docker وGitHub Actions وJenkins وGitLab CI
  • سهل الدمج في الأنابيب — تربطه بسهولة مع jq وgrep وawk وبقية أدوات Unix

ما تحتاجه قبل البدء

قائمة قصيرة، ومعظمها متوفر أصلًا على أي خادم:

  • Bash 4.0+
  • cURL (مثبت افتراضيًا على Linux/macOS)
  • jq لتحليل JSON: apt install jq أو brew install jq
  • مفتاح CaptchaAI API (أنشئ مفتاحك من هنا)

نصيحة أمان: لا تكتب المفتاح داخل السكربت مباشرة. مرّره عبر متغيّر بيئة مثل CAPTCHAAI_KEY كي لا يتسرّب إلى سجلّ الأوامر أو نظام التحكم بالإصدار.


الدالتان الأساسيتان: الإرسال والاستطلاع

تقوم كل عمليات الحل على دالتين: واحدة تُرسل المهمة إلى in.php وتعيد معرّف الطلب، وأخرى تستطلع res.php بشكل دوري حتى يجهز الرمز.

دالة إرسال المهمة

تبني هذه الدالة طلب POST إلى in.php، ثم تقرأ حقلي status وrequest من الاستجابة وتعيد معرّف المهمة عند النجاح.

#!/bin/bash

CAPTCHAAI_URL="https://ocr.captchaai.com"

submit_task() {
    local api_key="$1"
    shift
    local params=("$@")

    local response
    response=$(curl -s -X POST "${CAPTCHAAI_URL}/in.php" \
        -d "key=${api_key}" \
        -d "json=1" \
        "${params[@]}")

    local status
    status=$(echo "$response" | jq -r '.status')
    local request
    request=$(echo "$response" | jq -r '.request')

    if [ "$status" != "1" ]; then
        echo "ERROR: Submit failed: $request" >&2
        return 1
    fi

    echo "$request"
}

دالة استطلاع النتيجة

تكرّر هذه الدالة الاستفسار عن res.php بفاصل زمني ثابت، وتتجاهل الحالة CAPCHA_NOT_READY حتى يجهز الرمز أو تنتهي المهلة.

poll_result() {
    local api_key="$1"
    local task_id="$2"
    local max_wait="${3:-300}"
    local interval="${4:-5}"

    local elapsed=0

    while [ "$elapsed" -lt "$max_wait" ]; do
        sleep "$interval"
        elapsed=$((elapsed + interval))

        local response
        response=$(curl -s "${CAPTCHAAI_URL}/res.php?key=${api_key}&action=get&id=${task_id}&json=1")

        local status
        status=$(echo "$response" | jq -r '.status')
        local request
        request=$(echo "$response" | jq -r '.request')

        if [ "$request" = "CAPCHA_NOT_READY" ]; then
            echo "Waiting... (${elapsed}s/${max_wait}s)" >&2
            continue
        fi

        if [ "$status" != "1" ]; then
            echo "ERROR: Solve failed: $request" >&2
            return 1
        fi

        echo "$request"
        return 0
    done

    echo "ERROR: Timeout after ${max_wait}s" >&2
    return 1
}

حل reCAPTCHA v2 خطوة بخطوة

تجمع الدالة التالية بين الإرسال والاستطلاع: تُرسل sitekey وpageurl بالطريقة userrecaptcha، ثم تعيد الرمز الجاهز.

solve_recaptcha_v2() {
    local api_key="$1"
    local site_url="$2"
    local sitekey="$3"

    echo "Submitting reCAPTCHA v2..." >&2
    local task_id
    task_id=$(submit_task "$api_key" \
        -d "method=userrecaptcha" \
        -d "googlekey=${sitekey}" \
        -d "pageurl=${site_url}")

    if [ $? -ne 0 ]; then return 1; fi
    echo "Task ID: $task_id" >&2

    echo "Polling for solution..." >&2
    local token
    token=$(poll_result "$api_key" "$task_id")

    if [ $? -ne 0 ]; then return 1; fi
    echo "$token"
}

# Usage
API_KEY="YOUR_API_KEY"
TOKEN=$(solve_recaptcha_v2 "$API_KEY" \
    "https://example.com/login" \
    "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-")

echo "Token: ${TOKEN:0:50}..."

حل Cloudflare Turnstile

مقارنة بـ reCAPTCHA، يتغيّر شيئان فقط:

  • الطريقة تصبح method=turnstile
  • يُمرَّر مفتاح الموقع في المعامل key بدل googlekey

ويبقى تدفّق الإرسال ثم الاستطلاع كما هو.

solve_turnstile() {
    local api_key="$1"
    local site_url="$2"
    local sitekey="$3"

    local task_id
    task_id=$(submit_task "$api_key" \
        -d "method=turnstile" \
        -d "key=${sitekey}" \
        -d "pageurl=${site_url}")

    if [ $? -ne 0 ]; then return 1; fi

    poll_result "$api_key" "$task_id"
}

# Usage
TOKEN=$(solve_turnstile "$API_KEY" \
    "https://example.com/form" \
    "0x4AAAAAAAB5...")

حل reCAPTCHA v3 مع تمرير الإجراء

يضيف الإصدار v3 معاملين على الطلب نفسه:

  • version=v3 لتحديد الإصدار
  • action يحمل اسم الإجراء المطابق لما يتوقعه الموقع المستهدف (تسجيل دخول، إرسال نموذج، إلخ)
solve_recaptcha_v3() {
    local api_key="$1"
    local site_url="$2"
    local sitekey="$3"
    local action="${4:-verify}"

    local task_id
    task_id=$(submit_task "$api_key" \
        -d "method=userrecaptcha" \
        -d "googlekey=${sitekey}" \
        -d "pageurl=${site_url}" \
        -d "version=v3" \
        -d "action=${action}" \

    if [ $? -ne 0 ]; then return 1; fi

    poll_result "$api_key" "$task_id"
}

قراءة CAPTCHA الصورية عبر OCR

للصور النصية، حوّل الملف إلى Base64 وأرسله بالطريقة base64. الدالة التالية تدعم مصدرين:

  • ملف محلي على القرص تقرأه مباشرة
  • صورة عبر رابط، تُنزَّل مؤقتًا ثم تُحذف بعد الحل

وتتعامل مع اختلاف صيغة base64 بين Linux وmacOS تلقائيًا.

solve_image_captcha() {
    local api_key="$1"
    local image_path="$2"

    if [ ! -f "$image_path" ]; then
        echo "ERROR: File not found: $image_path" >&2
        return 1
    fi

    local base64_data
    base64_data=$(base64 -w 0 "$image_path" 2>/dev/null || base64 "$image_path")

    local task_id
    task_id=$(submit_task "$api_key" \
        -d "method=base64" \
        --data-urlencode "body=${base64_data}")

    if [ $? -ne 0 ]; then return 1; fi

    poll_result "$api_key" "$task_id"
}

# From URL
solve_image_from_url() {
    local api_key="$1"
    local image_url="$2"
    local tmp_file
    tmp_file=$(mktemp /tmp/captcha_XXXXXX.png)

    curl -s -o "$tmp_file" "$image_url"
    local result
    result=$(solve_image_captcha "$api_key" "$tmp_file")
    rm -f "$tmp_file"

    echo "$result"
}

# Usage
TEXT=$(solve_image_captcha "$API_KEY" "captcha.png")
echo "CAPTCHA text: $TEXT"

مكتبة CaptchaAI كاملة في ملف واحد

بدل تكرار الدوال في كل سكربت، اجمعها في مكتبة واحدة تستوردها عند الحاجة. تضم المكتبة الدوال الأساسية إضافةً إلى captchaai_balance لقراءة الرصيد.

احفظ الكود في ملف captchaai.sh، ثم استورده من أي سكربت آخر عبر source ./captchaai.sh.

#!/bin/bash
# CaptchaAI Solver Library
# Source this file: source ./captchaai.sh

CAPTCHAAI_URL="https://ocr.captchaai.com"
CAPTCHAAI_POLL_INTERVAL=5
CAPTCHAAI_MAX_WAIT=300

captchaai_submit() {
    local api_key="$1"; shift
    local response
    response=$(curl -s -X POST "${CAPTCHAAI_URL}/in.php" \
        -d "key=${api_key}" -d "json=1" "$@")
    local status=$(echo "$response" | jq -r '.status')
    local request=$(echo "$response" | jq -r '.request')
    [ "$status" = "1" ] && echo "$request" || { echo "Submit: $request" >&2; return 1; }
}

captchaai_poll() {
    local api_key="$1" task_id="$2" elapsed=0
    while [ "$elapsed" -lt "$CAPTCHAAI_MAX_WAIT" ]; do
        sleep "$CAPTCHAAI_POLL_INTERVAL"
        elapsed=$((elapsed + CAPTCHAAI_POLL_INTERVAL))
        local resp=$(curl -s "${CAPTCHAAI_URL}/res.php?key=${api_key}&action=get&id=${task_id}&json=1")
        local req=$(echo "$resp" | jq -r '.request')
        local st=$(echo "$resp" | jq -r '.status')
        [ "$req" = "CAPCHA_NOT_READY" ] && continue
        [ "$st" = "1" ] && { echo "$req"; return 0; }
        echo "Solve: $req" >&2; return 1
    done
    echo "Timeout" >&2; return 1
}

captchaai_balance() {
    local api_key="$1"
    curl -s "${CAPTCHAAI_URL}/res.php?key=${api_key}&action=getbalance&json=1" | jq -r '.request'
}

captchaai_recaptcha_v2() {
    local key="$1" url="$2" sk="$3"
    local tid=$(captchaai_submit "$key" -d "method=userrecaptcha" -d "googlekey=$sk" -d "pageurl=$url") || return 1
    captchaai_poll "$key" "$tid"
}

captchaai_turnstile() {
    local key="$1" url="$2" sk="$3"
    local tid=$(captchaai_submit "$key" -d "method=turnstile" -d "key=$sk" -d "pageurl=$url") || return 1
    captchaai_poll "$key" "$tid"
}

captchaai_image() {
    local key="$1" path="$2"
    local b64=$(base64 -w 0 "$path" 2>/dev/null || base64 "$path")
    local tid=$(captchaai_submit "$key" -d "method=base64" --data-urlencode "body=$b64") || return 1
    captchaai_poll "$key" "$tid"
}

استدعاء المكتبة

#!/bin/bash
source ./captchaai.sh

API_KEY="YOUR_API_KEY"

# Check balance
echo "Balance: $(captchaai_balance "$API_KEY")"

# Solve reCAPTCHA v2
TOKEN=$(captchaai_recaptcha_v2 "$API_KEY" \
    "https://example.com/login" \
    "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-")

echo "Token: ${TOKEN:0:50}..."

إرسال النموذج بعد الحصول على الرمز

بعد أن تعيد المكتبة الرمز، مرّره في حقل g-recaptcha-response ضمن طلب POST إلى الموقع المستهدف.

submit_form_with_token() {
    local url="$1"
    local token="$2"
    shift 2

    curl -s -X POST "$url" \
        -d "g-recaptcha-response=${token}" \
        "$@"
}

# Usage: solve then submit
TOKEN=$(captchaai_recaptcha_v2 "$API_KEY" \
    "https://example.com/login" "SITEKEY")

RESPONSE=$(submit_form_with_token "https://example.com/login" \
    "$TOKEN" \
    -d "username=user@example.com" \
    -d "password=password")

echo "Response: $RESPONSE"

تشغيل عدة عمليات حل بالتوازي

عندما تحتاج إلى حل CAPTCHA لعدة مواقع دفعة واحدة، شغّل كل عملية في وظيفة خلفية مستقلة واجمع النتائج في نهاية التشغيل. تذكّر أن كل عملية متوازية تشغل خيطًا واحدًا من خطتك.

#!/bin/bash
source ./captchaai.sh

API_KEY="YOUR_API_KEY"
RESULTS_DIR=$(mktemp -d)

# Define tasks
declare -A TASKS
TASKS["site-a"]="https://site-a.com|SITEKEY_A"
TASKS["site-b"]="https://site-b.com|SITEKEY_B"
TASKS["site-c"]="https://site-c.com|SITEKEY_C"

# Launch parallel solves
pids=()
for name in "${!TASKS[@]}"; do
    IFS='|' read -r url sitekey <<< "${TASKS[$name]}"
    (
        token=$(captchaai_recaptcha_v2 "$API_KEY" "$url" "$sitekey" 2>/dev/null)
        if [ $? -eq 0 ]; then
            echo "$token" > "${RESULTS_DIR}/${name}.token"
        else
            echo "FAILED" > "${RESULTS_DIR}/${name}.token"
        fi
    ) &
    pids+=($!)
done

# Wait for all
for pid in "${pids[@]}"; do
    wait "$pid"
done

# Collect results
echo "=== Results ==="
for name in "${!TASKS[@]}"; do
    token=$(cat "${RESULTS_DIR}/${name}.token")
    if [ "$token" = "FAILED" ]; then
        echo "$name: FAILED"
    else
        echo "$name: ${token:0:50}..."
    fi
done

rm -rf "$RESULTS_DIR"

إعادة المحاولة مع التراجع الأسي

ليست كل الأخطاء متساوية، والتفريق بينها يوفّر الرصيد والوقت:

  • قابلة للإعادة: ERROR_NO_SLOT_AVAILABLE وERROR_CAPTCHA_UNSOLVABLE — أعد المحاولة بعد تأخير متزايد
  • غير قابلة للإعادة: أخطاء المفتاح والرصيد — أوقف المحاولة فورًا وسجّل السبب

تفرّق الدالة التالية بينهما وتزيد فترة الانتظار أسيًا مع كل محاولة.

solve_with_retry() {
    local api_key="$1"
    local solve_cmd="$2"
    shift 2
    local max_retries="${1:-3}"

    local retryable_errors=("ERROR_NO_SLOT_AVAILABLE" "ERROR_CAPTCHA_UNSOLVABLE")
    local attempt=0

    while [ "$attempt" -le "$max_retries" ]; do
        if [ "$attempt" -gt 0 ]; then
            local delay=$((2 ** attempt + RANDOM % 3))
            echo "Retry $attempt/$max_retries after ${delay}s..." >&2
            sleep "$delay"
        fi

        local result
        result=$($solve_cmd "$api_key" "${@:2}")

        if [ $? -eq 0 ]; then
            echo "$result"
            return 0
        fi

        # Check if error is retryable
        local is_retryable=0
        for err in "${retryable_errors[@]}"; do
            if echo "$result" | grep -q "$err"; then
                is_retryable=1
                break
            fi
        done

        if [ "$is_retryable" -eq 0 ]; then
            echo "$result"
            return 1
        fi

        attempt=$((attempt + 1))
    done

    echo "Max retries exceeded" >&2
    return 1
}

الجدولة عبر cron

مثال واقعي: فريق صغير في المنطقة يشغّل سكربت cron ليليًا على خادم VPS متواضع لتصدير تقارير من بوابة خدمات محمية بـ reCAPTCHA v2. مع خطة BASIC ($15 شهريًا و5 خيوط) يكفي خيط واحد لهذه المهمة الليلية، بينما تتيح الخيوط المتبقية تشغيل بوابات إضافية بالتوازي عند الحاجة.

# Edit crontab: crontab -e
# Run daily at 8 AM
0 8 * * * /path/to/captcha-automation.sh >> /var/log/captcha.log 2>&1

سكربت cron كامل مع فحص الرصيد

#!/bin/bash
source /path/to/captchaai.sh

API_KEY="YOUR_API_KEY"
LOG_FILE="/var/log/captcha-$(date +%Y%m%d).log"

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_FILE"; }

# Check balance first
BALANCE=$(captchaai_balance "$API_KEY")
log "Balance: $BALANCE"

if (( $(echo "$BALANCE < 1.0" | bc -l) )); then
    log "WARNING: Low balance!"
    exit 1
fi

# Solve and process
TOKEN=$(captchaai_recaptcha_v2 "$API_KEY" \
    "https://portal.example.com" "SITEKEY")

if [ $? -eq 0 ]; then
    log "Solved successfully"
    # Submit form, download data, etc.
    curl -s "https://portal.example.com/data" \
        -d "g-recaptcha-response=$TOKEN" \
        -o "/data/export-$(date +%Y%m%d).csv"
    log "Data exported"
else
    log "ERROR: Failed to solve CAPTCHA"
    exit 1
fi

التشغيل داخل حاوية Docker

صورة Alpine مع bash وcurl وjq تكفي لتشغيل السكربت في حاوية صغيرة جدًا.

FROM alpine:3.19

RUN apk add --no-cache bash curl jq

COPY captchaai.sh /usr/local/lib/captchaai.sh
COPY automation.sh /app/automation.sh

RUN chmod +x /app/automation.sh

CMD ["/app/automation.sh"]

استكشاف الأخطاء ومعالجتها

معظم المشكلات في الطرفية تعود إلى أداة مفقودة أو مفتاح خاطئ. هذا جدول مرجعي سريع لأكثرها شيوعًا:

خطأ السبب الإصلاح
ERROR_WRONG_USER_KEY مفتاح API غير صالح تحقّق من المفتاح في لوحة التحكم
ERROR_ZERO_BALANCE الرصيد صفر اشحن الحساب
curl: (60) SSL certificate حزمة شهادات CA مفقودة أضف --cacert /path/to/ca-bundle.crt أو -k للاختبار فقط
jq: command not found jq غير مثبت apt install jq أو brew install jq
base64: invalid option -- 'w' صيغة base64 في macOS مختلفة استخدم base64 file بدلاً من base64 -w 0 file
استجابة فارغة مشكلة في الشبكة أضف الوسيط -v إلى curl لتتبّع الأخطاء

الأسئلة الشائعة

هل يعمل هذا داخل GitHub Actions وGitLab CI؟

نعم. ولأن السكربت لا يعتمد إلا على bash وcurl وjq، فهو يعمل في أي منفّذ (runner) يوفّر هذه الأدوات. أضف خطوة apt install jq عند الحاجة، ومرّر مفتاح الـ API عبر أسرار المستودع (secrets) لا داخل الملف.

كم خيطًا أحتاج لتشغيل عمليات الحل المتوازية؟

كل خيط في CaptchaAI يعالج اختبار CAPTCHA واحدًا في اللحظة نفسها، والقاعدة بسيطة:

  • ثلاث عمليات حل بالتوازي = ثلاثة خيوط متاحة على الأقل
  • تبدأ خطة BASIC بخمسة خيوط ($15 شهريًا)، وترتفع مع الخطط الأعلى
  • لا توجد رسوم لكل عملية حل؛ الفوترة على عدد الخيوط المتزامنة فقط

ما الفرق بين الاستطلاع الدوري والانتظار الطويل؟

الأسلوب المستخدم هنا هو الاستطلاع الدوري: يرسل السكربت طلبًا إلى res.php كل بضع ثوانٍ حتى يجهز الرمز. هذا أبسط ما يناسب Bash، ويتحكم المتغيّران CAPTCHAAI_POLL_INTERVAL وCAPTCHAAI_MAX_WAIT في وتيرة الفحص والمهلة القصوى.

كيف أخزّن مفتاح الـ API بأمان في سكربت مجدول؟

استخدم متغيرات البيئة: export CAPTCHAAI_KEY="..." ثم استعن بـ $CAPTCHAAI_KEY داخل السكربت. لا تكتب المفتاح مباشرة في ملفات خاضعة للتحكم بالإصدار، وقيّد صلاحيات ملف السكربت على المستخدم الذي يشغّل مهمة cron.

لماذا تعود القيمة CAPCHA_NOT_READY باستمرار؟

هذه ليست خطأً، بل تعني أن الحل ما زال قيد المعالجة، ويتابع السكربت الاستطلاع تلقائيًا. أما إن استمرت حتى انتهاء المهلة، فراجع صحة sitekey وpageurl، وتأكد من أن نوع CAPTCHA مطابق للدالة التي تستدعيها.


أدلة ذات صلة


شغّل حل CAPTCHA من طرفيتك مباشرة — أنشئ مفتاح الـ API وابدأ الأتمتة باستخدام Bash.

التعليقات غير مفعّلة لهذا المقال.