دروس API

PowerShell + CaptchaAI: حل اختبار CAPTCHA لأتمتة Windows

تحلّ CaptchaAI أي اختبار CAPTCHA داخل PowerShell عبر الأمر المدمج Invoke-RestMethod وحده — دون تثبيت أي وحدة خارجية أو مكتبة إضافية. تتلخّص الآلية في أربع خطوات متكررة:

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

يشرح هذا الدليل كيفية تطبيق تلك الخطوات على reCAPTCHA v2/v3 وCloudflare Turnstile واختبارات CAPTCHA الصورية، عبر دوال ووحدة PowerShell جاهزة للإنتاج يمكنك إسقاطها مباشرة في مهام Windows المجدولة أو سكربتات الإدارة لديك.


لماذا تختار PowerShell لحل الكابتشا؟

PowerShell هو المسار الأقصر لأي فريق يعمل على Windows بالفعل، لأنه يوفّر كل ما تحتاجه للتكامل مع واجهة CaptchaAI دون أدوات وسيطة:

  • مدمج في Windows — لا حاجة لأي تثبيت (PowerShell 5.1 فما فوق)
  • Invoke-RestMethod — دعم أصلي لواجهات REST مع تحليل JSON تلقائيًا
  • Task Scheduler — جدولة السكربتات المعتمدة على حل الكابتشا محليًا
  • مناسب لخطوط الأتمتة — تربط الحل مباشرةً بالخطوة التالية في سير العمل
  • متعدد المنصات — يعمل PowerShell 7+ على Linux وmacOS أيضًا

تخيّل فريق ضمان جودة في متجر تجارة إلكترونية بالخليج يشغّل اختبارات انحدار ليلية على خوادم Windows داخلية: كل عملية تسجيل دخول أو تسجيل حساب تعبر بوابة reCAPTCHA أو Turnstile. بدلاً من إيقاف الاختبار عند هذه النقطة، يستدعي السكربت CaptchaAI ويكمل التدفق آليًا — وهذا هو بالضبط ما تبنيه في الأقسام التالية.


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

  • PowerShell 5.1 (على Windows) أو PowerShell 7+ (عبر المنصات المختلفة)
  • مفتاح CaptchaAI API (أنشئ حسابك واحصل عليه من هنا)
  • لا وحدات إضافية مطلوبة — الأوامر المدمجة تكفي

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

تقوم كل عملية حل على لبنتين قابلتين لإعادة الاستخدام:

  • Submit-CaptchaTask — تبني حمولة الطلب وتعيد معرّف المهمة.
  • Get-CaptchaResult — تستطلع النتيجة حتى تجهز أو تنتهي المهلة.

بمجرد بنائهما، يصبح حل أي نوع من الكابتشا مجرد تمرير الوسائط الصحيحة.

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

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

function Submit-CaptchaTask {
    param(
        [Parameter(Mandatory)]
        [string]$ApiKey,

        [Parameter(Mandatory)]
        [hashtable]$TaskParams
    )

    $body = @{
        key  = $ApiKey
        json = 1
    } + $TaskParams

    $response = Invoke-RestMethod -Uri "https://ocr.captchaai.com/in.php" `
        -Method Post `
        -Body $body `
        -ContentType "application/x-www-form-urlencoded"

    if ($response.status -ne 1) {
        throw "Submit failed: $($response.request)"
    }

    return $response.request
}

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

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

function Get-CaptchaResult {
    param(
        [Parameter(Mandatory)]
        [string]$ApiKey,

        [Parameter(Mandatory)]
        [string]$TaskId,

        [int]$MaxWaitSeconds = 300,
        [int]$PollIntervalSeconds = 5
    )

    $deadline = (Get-Date).AddSeconds($MaxWaitSeconds)

    while ((Get-Date) -lt $deadline) {
        Start-Sleep -Seconds $PollIntervalSeconds

        $response = Invoke-RestMethod -Uri "https://ocr.captchaai.com/res.php" `
            -Method Get `
            -Body @{
                key    = $ApiKey
                action = "get"
                id     = $TaskId
                json   = 1
            }

        if ($response.request -eq "CAPCHA_NOT_READY") {
            Write-Verbose "Waiting for solution..."
            continue
        }

        if ($response.status -ne 1) {
            throw "Solve failed: $($response.request)"
        }

        return $response.request
    }

    throw "Timeout: CAPTCHA not solved within $MaxWaitSeconds seconds"
}

حل الأنواع الشائعة من الكابتشا في PowerShell

الأقسام الأربعة التالية تشترك في الدالتين السابقتين؛ يتغيّر فقط ما تمرّره في TaskParams بحسب نوع الكابتشا.

reCAPTCHA v2

النوع الأكثر شيوعًا في نماذج تسجيل الدخول والتسجيل. تحتاج فقط إلى عنوان الصفحة ومفتاح الموقع (sitekey)، ثم تستدعي الدالتين السابقتين لتحصل على التوكن الجاهز للإرسال:

function Solve-RecaptchaV2 {
    param(
        [Parameter(Mandatory)]
        [string]$ApiKey,

        [Parameter(Mandatory)]
        [string]$SiteUrl,

        [Parameter(Mandatory)]
        [string]$SiteKey
    )

    Write-Host "Submitting reCAPTCHA v2 task..."
    $taskId = Submit-CaptchaTask -ApiKey $ApiKey -TaskParams @{
        method    = "userrecaptcha"
        googlekey = $SiteKey
        pageurl   = $SiteUrl
    }
    Write-Host "Task ID: $taskId"

    Write-Host "Polling for solution..."
    $token = Get-CaptchaResult -ApiKey $ApiKey -TaskId $taskId
    Write-Host "Solved! Token: $($token.Substring(0, [Math]::Min(50, $token.Length)))..."

    return $token
}

# Usage
$apiKey = "YOUR_API_KEY"
$token = Solve-RecaptchaV2 `
    -ApiKey $apiKey `
    -SiteUrl "https://example.com/login" `
    -SiteKey "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-"

Cloudflare Turnstile

يتطابق تدفق Turnstile مع reCAPTCHA تقريبًا؛ الفارق الوحيد هو قيمة method واسم حقل مفتاح الموقع. تحلّ CaptchaAI اختبارات Turnstile بسرعة، ما يجعلها مناسبة للتدفقات الحسّاسة للزمن:

function Solve-Turnstile {
    param(
        [Parameter(Mandatory)]
        [string]$ApiKey,

        [Parameter(Mandatory)]
        [string]$SiteUrl,

        [Parameter(Mandatory)]
        [string]$SiteKey
    )

    $taskId = Submit-CaptchaTask -ApiKey $ApiKey -TaskParams @{
        method  = "turnstile"
        key     = $SiteKey
        pageurl = $SiteUrl
    }

    return Get-CaptchaResult -ApiKey $ApiKey -TaskId $taskId
}

# Usage
$token = Solve-Turnstile `
    -ApiKey "YOUR_API_KEY" `
    -SiteUrl "https://example.com/form" `
    -SiteKey "0x4AAAAAAAB5..."

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

يعتمد الإصدار الثالث على درجة سلوكية بدلاً من نقرة، لذا تضيف الوسيطين version وaction. اجعل قيمة action مطابقة لتلك المعرّفة في الصفحة المستهدفة (مثل login أو verify) للحصول على أفضل درجة:

function Solve-RecaptchaV3 {
    param(
        [Parameter(Mandatory)]
        [string]$ApiKey,

        [Parameter(Mandatory)]
        [string]$SiteUrl,

        [Parameter(Mandatory)]
        [string]$SiteKey,

        [string]$Action = "verify",
    )

    $taskId = Submit-CaptchaTask -ApiKey $ApiKey -TaskParams @{
        method    = "userrecaptcha"
        googlekey = $SiteKey
        pageurl   = $SiteUrl
        version   = "v3"
        action    = $Action
    }

    return Get-CaptchaResult -ApiKey $ApiKey -TaskId $taskId
}

اختبارات CAPTCHA الصورية (OCR)

بالنسبة للصور النصية، تُرمّز الملف بصيغة Base64 وترسله بالطريقة base64. هذا مفيد للنماذج القديمة أو لوحات الإدارة الداخلية التي ما زالت تعرض كابتشا صوريًا بسيطًا:

function Solve-ImageCaptcha {
    param(
        [Parameter(Mandatory)]
        [string]$ApiKey,

        [Parameter(Mandatory)]
        [string]$ImagePath
    )

    if (-not (Test-Path $ImagePath)) {
        throw "Image file not found: $ImagePath"
    }

    $imageBytes = [System.IO.File]::ReadAllBytes($ImagePath)
    $base64 = [Convert]::ToBase64String($imageBytes)

    $taskId = Submit-CaptchaTask -ApiKey $ApiKey -TaskParams @{
        method = "base64"
        body   = $base64
    }

    return Get-CaptchaResult -ApiKey $ApiKey -TaskId $taskId
}

# Usage
$text = Solve-ImageCaptcha -ApiKey "YOUR_API_KEY" -ImagePath "C:\captcha.png"
Write-Host "CAPTCHA text: $text"

حل صورة من رابط مباشر

إن كانت الصورة معروضة على الويب، فلا داعي لحفظها محليًا؛ نزّلها إلى الذاكرة عبر Invoke-WebRequest ثم رمّزها مباشرة:

function Solve-ImageCaptchaFromUrl {
    param(
        [Parameter(Mandatory)]
        [string]$ApiKey,

        [Parameter(Mandatory)]
        [string]$ImageUrl
    )

    $imageBytes = (Invoke-WebRequest -Uri $ImageUrl).Content
    $base64 = [Convert]::ToBase64String($imageBytes)

    $taskId = Submit-CaptchaTask -ApiKey $ApiKey -TaskParams @{
        method = "base64"
        body   = $base64
    }

    return Get-CaptchaResult -ApiKey $ApiKey -TaskId $taskId
}

بناء وحدة حل متكاملة (Module)

بدلاً من نثر الدوال عبر ملفاتك، اجمعها في صنف (class) واحد قابل لإعادة الاستخدام. احفظ الملف باسم CaptchaAI.psm1 لتستورده في أي سكربت لاحقًا، وأضف طريقة GetBalance لمتابعة الرصيد قبل تشغيل الدفعات الكبيرة:

class CaptchaAISolver {
    [string]$ApiKey
    [string]$BaseUrl = "https://ocr.captchaai.com"
    [int]$PollInterval = 5
    [int]$MaxWait = 300

    CaptchaAISolver([string]$apiKey) {
        $this.ApiKey = $apiKey
    }

    [string] SolveRecaptchaV2([string]$siteUrl, [string]$siteKey) {
        return $this.Solve(@{
            method    = "userrecaptcha"
            googlekey = $siteKey
            pageurl   = $siteUrl
        })
    }

    [string] SolveTurnstile([string]$siteUrl, [string]$siteKey) {
        return $this.Solve(@{
            method  = "turnstile"
            key     = $siteKey
            pageurl = $siteUrl
        })
    }

    [string] SolveImage([string]$imagePath) {
        $bytes = [System.IO.File]::ReadAllBytes($imagePath)
        $base64 = [Convert]::ToBase64String($bytes)
        return $this.Solve(@{
            method = "base64"
            body   = $base64
        })
    }

    [double] GetBalance() {
        $response = Invoke-RestMethod -Uri "$($this.BaseUrl)/res.php" `
            -Body @{ key = $this.ApiKey; action = "getbalance"; json = 1 }
        return [double]$response.request
    }

    hidden [string] Solve([hashtable]$params) {
        $taskId = $this.Submit($params)
        return $this.Poll($taskId)
    }

    hidden [string] Submit([hashtable]$params) {
        $body = @{ key = $this.ApiKey; json = 1 } + $params
        $response = Invoke-RestMethod -Uri "$($this.BaseUrl)/in.php" `
            -Method Post -Body $body
        if ($response.status -ne 1) { throw "Submit: $($response.request)" }
        return $response.request
    }

    hidden [string] Poll([string]$taskId) {
        $deadline = (Get-Date).AddSeconds($this.MaxWait)
        while ((Get-Date) -lt $deadline) {
            Start-Sleep -Seconds $this.PollInterval
            $response = Invoke-RestMethod -Uri "$($this.BaseUrl)/res.php" `
                -Body @{ key = $this.ApiKey; action = "get"; id = $taskId; json = 1 }
            if ($response.request -eq "CAPCHA_NOT_READY") { continue }
            if ($response.status -ne 1) { throw "Solve: $($response.request)" }
            return $response.request
        }
        throw "Timeout"
    }
}

# Export
Export-ModuleMember

استدعاء الوحدة

بعد الاستيراد، تُنشئ نسخة واحدة من الصنف وتستدعي طرقه مباشرة:

using module .\CaptchaAI.psm1

$solver = [CaptchaAISolver]::new("YOUR_API_KEY")

# Check balance
$balance = $solver.GetBalance()
Write-Host "Balance: `$$balance"

# Solve reCAPTCHA v2
$token = $solver.SolveRecaptchaV2("https://example.com/login", "SITEKEY")
Write-Host "Token: $($token.Substring(0, 50))..."

من الحل إلى التشغيل في الإنتاج

بعد أن أصبح لديك توكن محلول، تبقى أربع لبنات عملية تحوّل السكربت إلى أتمتة موثوقة: إرسال النموذج، التوازي، إعادة المحاولة، ثم الجدولة.

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

حل الكابتشا نصف المهمة؛ الخطوة المكمّلة هي إدراج التوكن في حمولة النموذج تحت الحقل g-recaptcha-response ثم إرسال الطلب:

function Submit-FormWithToken {
    param(
        [string]$Url,
        [string]$Token,
        [hashtable]$FormData
    )

    $body = $FormData + @{
        "g-recaptcha-response" = $Token
    }

    $response = Invoke-WebRequest -Uri $Url `
        -Method Post `
        -Body $body `
        -ContentType "application/x-www-form-urlencoded"

    return $response
}

# Usage
$token = Solve-RecaptchaV2 -ApiKey "YOUR_API_KEY" `
    -SiteUrl "https://example.com/login" `
    -SiteKey "SITEKEY"

$result = Submit-FormWithToken `
    -Url "https://example.com/login" `
    -Token $token `
    -FormData @{
        username = "user@example.com"
        password = "password"
    }

Write-Host "Response: $($result.StatusCode)"

الحل المتوازي عبر Start-Job

عندما تحتاج لمعالجة عدة مواقع في آنٍ واحد، شغّل كل مهمة داخل وظيفة مستقلة عبر Start-Job واجمع النتائج بعد اكتمالها. لاحظ أن التوازي الفعلي مقيّد بعدد الـ threads المتاح في خطتك:

$apiKey = "YOUR_API_KEY"

$tasks = @(
    @{ Url = "https://site-a.com"; Key = "SITEKEY_A" },
    @{ Url = "https://site-b.com"; Key = "SITEKEY_B" },
    @{ Url = "https://site-c.com"; Key = "SITEKEY_C" }
)

$jobs = $tasks | ForEach-Object {
    $task = $_
    Start-Job -ScriptBlock {
        param($ApiKey, $Url, $SiteKey)

        $taskId = (Invoke-RestMethod -Uri "https://ocr.captchaai.com/in.php" -Method Post -Body @{
            key = $ApiKey; json = 1; method = "userrecaptcha"
            googlekey = $SiteKey; pageurl = $Url
        }).request

        $deadline = (Get-Date).AddSeconds(300)
        while ((Get-Date) -lt $deadline) {
            Start-Sleep -Seconds 5
            $result = Invoke-RestMethod -Uri "https://ocr.captchaai.com/res.php" -Body @{
                key = $ApiKey; action = "get"; id = $taskId; json = 1
            }
            if ($result.request -ne "CAPCHA_NOT_READY" -and $result.status -eq 1) {
                return @{ Url = $Url; Token = $result.request }
            }
        }
        return @{ Url = $Url; Error = "Timeout" }
    } -ArgumentList $apiKey, $task.Url, $task.Key
}

# Wait and collect results
$results = $jobs | Wait-Job | Receive-Job
$results | ForEach-Object {
    if ($_.Token) {
        Write-Host "$($_.Url): $($_.Token.Substring(0, 50))..."
    } else {
        Write-Host "$($_.Url): $($_.Error)" -ForegroundColor Red
    }
}
$jobs | Remove-Job

إعادة المحاولة ومعالجة الأخطاء

في التشغيل الحقيقي تظهر أخطاء عابرة مثل ERROR_NO_SLOT_AVAILABLE. غلّف الحل بمنطق إعادة محاولة يعتمد على التراجع الأسي، مع التمييز بين الأخطاء القابلة لإعادة المحاولة وتلك التي يجب إيقاف التنفيذ عندها:

function Solve-WithRetry {
    param(
        [Parameter(Mandatory)]
        [string]$ApiKey,

        [Parameter(Mandatory)]
        [hashtable]$TaskParams,

        [int]$MaxRetries = 3
    )

    $retryableErrors = @(
        "ERROR_NO_SLOT_AVAILABLE",
        "ERROR_CAPTCHA_UNSOLVABLE"
    )

    for ($attempt = 0; $attempt -le $MaxRetries; $attempt++) {
        if ($attempt -gt 0) {
            $delay = [Math]::Pow(2, $attempt) + (Get-Random -Maximum 3)
            Write-Host "Retry $attempt/$MaxRetries after $($delay)s..."
            Start-Sleep -Seconds $delay
        }

        try {
            $taskId = Submit-CaptchaTask -ApiKey $ApiKey -TaskParams $TaskParams
            $result = Get-CaptchaResult -ApiKey $ApiKey -TaskId $taskId
            return $result
        }
        catch {
            $errorMsg = $_.Exception.Message
            $isRetryable = $retryableErrors | Where-Object { $errorMsg -like "*$_*" }

            if (-not $isRetryable -or $attempt -eq $MaxRetries) {
                throw
            }
            Write-Warning "Retryable error: $errorMsg"
        }
    }
}

الجدولة عبر Task Scheduler

لتشغيل الأتمتة دون تدخل يدوي، سجّل السكربت كمهمة مجدولة تعمل يوميًا في وقت محدد. هذه هي الطريقة التي تحوّل بها الأمثلة السابقة إلى عملية مستمرة على خادم Windows:

نصيحة أمان: لا تكتب مفتاح الـ API داخل ملف السكربت المجدول؛ اقرأه من متغيّر بيئة آمن على الخادم حتى لا يظهر في سجلّات المهام أو نسخ الملفات الاحتياطية.

# Create a scheduled task that runs CAPTCHA automation daily
$action = New-ScheduledTaskAction `
    -Execute "powershell.exe" `
    -Argument "-ExecutionPolicy Bypass -File C:\Scripts\captcha-automation.ps1"

$trigger = New-ScheduledTaskTrigger -Daily -At "08:00"

Register-ScheduledTask `
    -TaskName "CaptchaAutomation" `
    -Action $action `
    -Trigger $trigger `
    -Description "Run daily CAPTCHA automation with CaptchaAI"

استكشاف الأخطاء الشائعة

خطأ السبب الإصلاح
ERROR_WRONG_USER_KEY مفتاح API غير صالح تحقق من المفتاح في لوحة التحكم
ERROR_ZERO_BALANCE الرصيد صفر اشحن الحساب
Invoke-RestMethod: SSL/TLS عدم تطابق إصدار TLS أضف [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
The response content cannot be parsed استجابة غير JSON استخدم Invoke-WebRequest وحلّل الرد يدويًا
خطأ Execution policy السكربت محظور شغّل Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
Cannot convert to double خطأ في تحليل الرصيد استخدم [double]::Parse($response.request)

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

كم يستغرق حل الكابتشا وكيف أضبط مهلة الاستطلاع؟

يعتمد الزمن على نوع الكابتشا وحمل الخدمة؛ اختبارات Turnstile عادةً أسرع من reCAPTCHA. ابدأ بفترة استطلاع من 5 ثوانٍ ومهلة قصوى 300 ثانية كما في الأمثلة، ثم اضبط PollIntervalSeconds وMaxWaitSeconds حسب سلوك موقعك المستهدف.

كم عدد المهام المتزامنة التي يمكنني تشغيلها عبر Start-Job؟

يتحدد ذلك بعدد الـ threads في خطتك، لأن CaptchaAI تُسعّر حسب الـ threads المتزامنة لا حسب عدد عمليات الحل. تبدأ خطة BASIC من 15$ شهريًا بـ 5 threads، وترتفع مع الخطط الأعلى، مع عمليات حل غير محدودة داخل كل thread. اضبط عدد وظائف Start-Job المتزامنة بما يتناسب مع حدّ الـ threads في خطتك.

هل تحل CaptchaAI اختبار hCaptcha في PowerShell؟

لا، لا تدعم CaptchaAI حاليًا حل hCaptcha ولا FunCaptcha. الأنواع المغطّاة هنا — reCAPTCHA v2/v3 وCloudflare Turnstile والكابتشا الصورية — مدعومة بشكل كامل، إلى جانب GeeTest v3 وCloudflare Challenge وBLS.

كيف أؤمّن مفتاح الـ API داخل مهمة مجدولة أو خط CI/CD؟

لا تكتب المفتاح داخل السكربت مباشرة. خزّنه كمتغيّر بيئة على الخادم أو كمتغيّر سري في Azure DevOps أو GitHub Actions أو Jenkins، ثم اقرأه في وقت التشغيل عبر $env:CAPTCHAAI_KEY.


مقالات ذات صلة


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

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