لحلّ أي اختبار CAPTCHA من داخل سكربت Bash لا تحتاج إلى لغة برمجة كاملة ولا إلى إطار عمل؛ يكفيك أمران: curl لإرسال المهمة إلى CaptchaAI، وjq لقراءة الرمز من الاستجابة. بهذا تتحوّل الطرفية وحدها إلى بيئة عمل مكتملة لأتمتة CAPTCHA على أي خادم Linux أو macOS.
وعندما تصطدم مهام cron أو خطوط CI/CD أو سكربتات المراقبة الخفيفة باختبار CAPTCHA، يمكنك استدعاء واجهة CaptchaAI مباشرة عبر أوامر HTTP من دون إضافة وقت تشغيل جديد. يغطّي هذا الدليل حل reCAPTCHA v2/v3 وCloudflare Turnstile وCAPTCHA الصورية باستخدام Bash فقط، مع دوال جاهزة يمكنك تجميعها في مكتبة واحدة.
يمر كل حل بأربع خطوات ثابتة مهما اختلف نوع CAPTCHA:
- أرسل المهمة إلى
in.phpواحصل على معرّف الطلب. - احفظ المعرّف الذي تعيده الاستجابة.
- استطلع
res.phpدوريًا حتى تتغيّر الحالة إلى جاهز. - استخدم الرمز الناتج في طلبك التالي إلى الموقع المستهدف.
متى تعتمد على 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.