استكشاف الأخطاء

ERROR_ZERO_BALANCE: استكشاف أخطاء الدفع والفواتير وإصلاحها

إذا كانت مهام الأتمتة لديك تعمل بسلاسة ثم بدأت فجأة تُعيد ERROR_ZERO_BALANCE مع كل طلب، فالسبب ليس خللًا في الكود بل ببساطة نفاد رصيد حساب CaptchaAI. الحساب لم يعد يملك رصيدًا كافيًا لمعالجة المهام، فيرفض الخادم أي طلب جديد حتى تُضيف رصيدًا. الحل الفوري هو إعادة شحن الرصيد من captchaai.com، لكن الهدف الأهم هو ألا تصل إلى هذه النقطة من الأساس: فحص استباقي للرصيد، وتنبيهات مبكرة، وتعامل رشيق مع النفاد يبقي بقية سير العمل واقفًا على قدميه.

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


لماذا يظهر خطأ الرصيد الصفري

قبل معالجة الخطأ، من المفيد معرفة مصدره. في معظم الحالات يعود ERROR_ZERO_BALANCE إلى أحد أربعة أسباب، ولكل منها إجراء مختلف:

السبب معدل التكرار الإجراء المقترح
نفاد رصيد الحساب الأكثر شيوعًا أضف رصيدًا من موقع captchaai.com
استهلاك أعلى من المتوقع شائع فعّل مراقبة الرصيد وحدود التنبيه
تسرّب مفتاح API عن طريق الخطأ نادر دوّر مفتاح الـ API وراجع سجل الاستخدام
انتهاء صلاحية طريقة الدفع أحيانًا حدّث بيانات الفوترة في لوحة التحكم

تحديد السبب الصحيح يوفّر عليك دقائق من التخمين: نفاد فعلي للرصيد يُعالَج بالشحن، بينما استهلاك مفاجئ قد يشير إلى مفتاح مكشوف يستنزف رصيدك.


الفحص السريع لرصيد الحساب

أول خطوة تشخيصية هي قراءة الرصيد الحالي مباشرة من الـ API بدل الاعتماد على التخمين. تستخدم الدالة التالية الإجراء getbalance عبر نقطة النهاية res.php، وتُعيد الرصيد كرقم عشري بالدولار الأمريكي:

import requests


def check_balance(api_key):
    """Check current CaptchaAI balance."""
    resp = requests.get(
        "https://ocr.captchaai.com/res.php",
        params={"key": api_key, "action": "getbalance", "json": 1},
        timeout=10,
    )
    data = resp.json()

    if data.get("status") == 1:
        return float(data["request"])

    raise RuntimeError(f"Balance check failed: {data.get('request')}")


balance = check_balance("YOUR_API_KEY")
print(f"Balance: ${balance:.4f}")

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


التعامل مع الرصيد الصفري دون توقف الأتمتة

بدل أن ينهار السكربت عند نفاد الرصيد، من الأفضل أن يتوقف بشكل مُتحكَّم فيه ويرفع استثناءً واضحًا يمكنك التقاطه. الفئة BalanceAwareSolver تفحص الرصيد قبل الإرسال، وتُخزِّن النتيجة مؤقتًا لخمس دقائق لتقليل الطلبات، وتُميّز بين رصيد منخفض ورصيد صفري عبر استثناءين منفصلين:

import requests
import time
import logging

logger = logging.getLogger(__name__)


class BalanceAwareSolver:
    """Solver that handles zero balance without crashing."""

    def __init__(self, api_key, min_balance=0.50):
        self.api_key = api_key
        self.min_balance = min_balance
        self._last_balance_check = 0
        self._cached_balance = None

    def solve(self, params):
        """Solve CAPTCHA with balance pre-check."""
        # Check balance every 5 minutes
        if time.time() - self._last_balance_check > 300:
            self._check_balance()

        if self._cached_balance is not None and self._cached_balance < 0.01:
            raise InsufficientBalanceError(
                f"Balance too low: ${self._cached_balance:.4f}. "
                "Add funds at https://captchaai.com"
            )

        try:
            return self._submit_and_poll(params)
        except ZeroBalanceError:
            self._cached_balance = 0.0
            logger.error("ERROR_ZERO_BALANCE — add funds at captchaai.com")
            raise

    def _check_balance(self):
        """Check and cache balance."""
        try:
            resp = requests.get(
                "https://ocr.captchaai.com/res.php",
                params={
                    "key": self.api_key,
                    "action": "getbalance",
                    "json": 1,
                },
                timeout=10,
            )
            data = resp.json()
            if data.get("status") == 1:
                self._cached_balance = float(data["request"])
                self._last_balance_check = time.time()

                if self._cached_balance < self.min_balance:
                    logger.warning(
                        f"Low balance: ${self._cached_balance:.4f} "
                        f"(threshold: ${self.min_balance:.2f})"
                    )
        except Exception as e:
            logger.debug(f"Balance check failed: {e}")

    def _submit_and_poll(self, params):
        """Submit task and poll for result."""
        data = {"key": self.api_key, "json": 1, **params}
        resp = requests.post(
            "https://ocr.captchaai.com/in.php", data=data, timeout=30,
        )
        result = resp.json()

        if result.get("status") != 1:
            error = result.get("request", "")
            if error == "ERROR_ZERO_BALANCE":
                raise ZeroBalanceError("Account balance is zero")
            raise RuntimeError(f"Submit failed: {error}")

        task_id = result["request"]

        time.sleep(10)
        for _ in range(24):
            resp = requests.get(
                "https://ocr.captchaai.com/res.php",
                params={
                    "key": self.api_key, "action": "get",
                    "id": task_id, "json": 1,
                },
                timeout=15,
            )
            data = resp.json()

            if data.get("status") == 1:
                return data["request"]
            if data["request"] != "CAPCHA_NOT_READY":
                raise RuntimeError(data["request"])
            time.sleep(5)

        raise TimeoutError("Solve timeout")


class ZeroBalanceError(Exception):
    """Raised when account has no balance."""
    pass


class InsufficientBalanceError(Exception):
    """Raised when balance is below minimum threshold."""
    pass

الفكرة الجوهرية هنا هي الفصل بين حالتَي «رصيد منخفض» و«رصيد صفري»: الأولى تحذير مبكر يتيح لك التصرف، والثانية توقف صريح يمكن للكود الأعلى منه أن يقرّر كيف يتعامل معه.


مراقبة الرصيد وإرسال تنبيهات مبكرة

الفحص اليدوي يفيد وقت الأزمة، لكن الحماية الحقيقية تأتي من مراقبة دورية تعمل في الخلفية. الفئة BalanceMonitor تُشغّل خيط معالجة (Thread) مستقلًّا يستطلع الرصيد على فترات، ويُطلق تحذيرًا عند نزوله تحت حدّ معيّن، وإنذارًا حرجًا عند وصوله إلى الصفر. استبدل الدالة _send_alert بقناة التنبيه التي تناسبك: بريد إلكتروني، أو Webhook إلى Slack:

import smtplib
from email.message import EmailMessage
import threading
import time
import logging

logger = logging.getLogger(__name__)


class BalanceMonitor:
    """Monitor balance and send alerts when low."""

    def __init__(self, api_key, alert_threshold=1.00, check_interval=600):
        self.api_key = api_key
        self.alert_threshold = alert_threshold
        self.check_interval = check_interval
        self._alert_sent = False
        self._running = False

    def start(self):
        """Start background monitoring."""
        self._running = True
        thread = threading.Thread(target=self._monitor_loop, daemon=True)
        thread.start()
        logger.info("Balance monitor started")

    def stop(self):
        """Stop monitoring."""
        self._running = False

    def _monitor_loop(self):
        """Check balance periodically."""
        while self._running:
            try:
                balance = self._get_balance()
                logger.info(f"Balance: ${balance:.4f}")

                if balance <= 0:
                    self._send_alert("CRITICAL: CaptchaAI Zero Balance", 
                        f"Balance is ${balance:.4f}. Solving will fail.")
                elif balance < self.alert_threshold and not self._alert_sent:
                    self._send_alert("WARNING: CaptchaAI Low Balance",
                        f"Balance: ${balance:.4f} (threshold: ${self.alert_threshold:.2f})")
                    self._alert_sent = True
                elif balance >= self.alert_threshold:
                    self._alert_sent = False  # Reset alert flag

            except Exception as e:
                logger.error(f"Monitor error: {e}")

            time.sleep(self.check_interval)

    def _get_balance(self):
        """Check account balance."""
        resp = requests.get(
            "https://ocr.captchaai.com/res.php",
            params={"key": self.api_key, "action": "getbalance", "json": 1},
            timeout=10,
        )
        data = resp.json()
        if data.get("status") == 1:
            return float(data["request"])
        raise RuntimeError(data.get("request"))

    def _send_alert(self, subject, body):
        """Send email alert. Replace with your notification method."""
        logger.critical(f"{subject}: {body}")
        # Implement email, Slack webhook, or other notification here


# Usage
monitor = BalanceMonitor("YOUR_API_KEY", alert_threshold=2.00)
monitor.start()

نصيحة عملية للسوق العربي: اضبط حدّ التنبيه (alert_threshold) على قيمة تكفي استهلاك يوم أو يومَي عمل كاملين، لأن التحويلات البنكية قد لا تُعتمد قبل ذلك، وتحديدًا حول عطلة نهاية الأسبوع. حدّ منخفض جدًّا يعني أن التنبيه يصلك متأخرًا بعد أن يكون الرصيد قد اقترب فعلًا من الصفر.


تقدير التكلفة قبل تشغيل الدُفعات الكبيرة

قبل إطلاق دُفعة ضخمة، من الحكمة أن تتأكد أن رصيدك يغطّيها بدل أن تكتشف نفاده في المنتصف. الدالة التالية تقارن الرصيد الحالي بتكلفة تقديرية للدُفعة. القيم في COST_PER_SOLVE تقديرية لأغراض التخطيط فقط، والفوترة الفعلية تتبع خطتك في CaptchaAI:

# Approximate costs per CAPTCHA type
COST_PER_SOLVE = {
    "recaptcha_v2": 0.003,
    "recaptcha_v3": 0.004,
    "turnstile": 0.002,
    "geetest": 0.003,
    "image": 0.001,
    "bls": 0.002,
}


def estimate_cost(captcha_type, quantity):
    """Estimate cost for a batch of solves."""
    rate = COST_PER_SOLVE.get(captcha_type, 0.003)
    total = rate * quantity
    return total


def check_budget(api_key, captcha_type, planned_solves):
    """Check if balance covers planned solves."""
    balance = check_balance(api_key)
    estimated = estimate_cost(captcha_type, planned_solves)

    if balance >= estimated:
        print(f"Budget OK: ${balance:.4f} covers ~{int(balance / COST_PER_SOLVE[captcha_type])} solves")
        return True
    else:
        shortfall = estimated - balance
        print(f"Need ${shortfall:.4f} more for {planned_solves} {captcha_type} solves")
        return False


# Check before a large batch
check_budget("YOUR_API_KEY", "recaptcha_v2", 5000)

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


التدهور الرشيق عند نفاد الرصيد

حتى مع المراقبة، قد يصل الرصيد إلى الصفر في منتصف مهمة حرجة. هنا يأتي دور التدهور الرشيق: بدل أن ينهار البرنامج، يتصرّف وفق سياسة تحدّدها أنت. الفئة GracefulSolver تبني على BalanceAwareSolver وتمنحك ثلاثة خيارات — تخطّي العنصر (skip)، أو وضعه في قائمة انتظار (queue)، أو رفع استثناء يوقف الأتمتة (raise):

class GracefulSolver:
    """Fall back to manual or skip when balance is zero."""

    def __init__(self, api_key, on_zero_balance="skip"):
        self.api_key = api_key
        self.on_zero_balance = on_zero_balance  # "skip", "queue", "raise"
        self._pending_queue = []
        self.solver = BalanceAwareSolver(api_key)

    def solve_or_degrade(self, params, item_id=None):
        """Try to solve, degrade gracefully on zero balance."""
        try:
            return self.solver.solve(params)
        except (ZeroBalanceError, InsufficientBalanceError):
            return self._handle_zero(params, item_id)

    def _handle_zero(self, params, item_id):
        """Handle zero balance based on configured strategy."""
        if self.on_zero_balance == "skip":
            logger.warning(f"Skipping CAPTCHA for item {item_id} — no balance")
            return None

        elif self.on_zero_balance == "queue":
            self._pending_queue.append({"params": params, "item_id": item_id})
            logger.info(f"Queued item {item_id} — {len(self._pending_queue)} pending")
            return None

        else:  # "raise"
            raise ZeroBalanceError("No balance — stopping automation")

    def retry_pending(self):
        """Retry queued items after balance is refilled."""
        if not self._pending_queue:
            return []

        results = []
        remaining = []

        for item in self._pending_queue:
            try:
                token = self.solver.solve(item["params"])
                results.append({"item_id": item["item_id"], "token": token})
            except (ZeroBalanceError, InsufficientBalanceError):
                remaining.append(item)
                break  # Stop retrying — still no balance

        self._pending_queue = remaining + self._pending_queue[len(results) + len(remaining):]
        return results

استراتيجية queue مفيدة تحديدًا في سيناريو نهاية الأسبوع الذي بدأنا به: العناصر المتعثّرة لا تضيع، بل تنتظر في الطابور، وبمجرد أن تعيد شحن الرصيد تستدعي retry_pending لإكمالها من حيث توقفت.


الخطط الشهرية القائمة على الـ Threads بديلًا عن مراقبة الرصيد

مراقبة رصيد متناقص حلٌّ عملي، لكنه ليس الوحيد. تتيح CaptchaAI خططًا شهرية تُحاسَب على عدد الـ Threads المتزامنة لا على كل عملية حل، فتُجدّد اشتراكك في موعد ثابت بدل متابعة رصيد قد ينفد في أسوأ لحظة. تبدأ الخطة BASIC من 15$ شهريًا بخمسة Threads، وترتفع عبر STANDARD (30$، 15 Thread) وADVANCE (90$، 50 Thread) وصولًا إلى فئات VIP الأعلى مثل VIP-3 (7,500$، 5,000 Thread). وكل خطة تشمل عددًا غير محدود من الحلول لكل Thread طوال الشهر، ما يجعل التكلفة الشهرية ثابتة ومتوقّعة — وهي ميزة مهمة للفرق التي تريد ميزانية واضحة تُراجَع في captchaai.com بدل مفاجآت نفاد الرصيد.


جدول تشخيص الأعطال الشائعة

عندما يستمر الخطأ رغم المعالجة، استعن بالجدول التالي لربط العَرَض بسببه الأرجح وحلّه:

العَرَض السبب المحتمل الحل
ظهور ERROR_ZERO_BALANCE على كل طلب الحساب فارغ تمامًا أضف رصيدًا من موقع captchaai.com
انخفاض الرصيد بسرعة غير متوقعة مفتاح API مكشوف أو كود غير فعّال دوّر المفتاح وراجع سجلات الاستخدام
رصيد موجب لكن الخطأ مستمر تأخّر في التخزين المؤقت أو المزامنة انتظر دقيقة واحدة ثم أعد المحاولة
تعذّر إضافة رصيد مشكلة في طريقة الدفع حدّث طريقة الدفع في لوحة التحكم

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

هل يوقف ERROR_ZERO_BALANCE المهام قيد التنفيذ بالفعل؟

لا يؤثر الخطأ على المهام التي أُرسلت وبدأت معالجتها بالفعل؛ فهو يمنع فقط قبول طلبات جديدة. لهذا يفيد نمط قائمة الانتظار في حفظ العناصر الجديدة حتى تعيد الشحن.

كيف أفرّق بين نفاد الرصيد الفعلي وتأخّر تحديثه؟

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

هل تُحتسب المحاولات الفاشلة على رصيدي؟

لا يُخصم من رصيدك مقابل المحاولات التي تفشل ولا تُرجع رمزًا؛ الخصم يقابل الحلول الناجحة التي تُعيد رمزًا أو إجابة فقط.

ما القيمة المناسبة لحدّ التنبيه بالرصيد؟

اختر حدًّا يغطّي استهلاك يوم أو يومَي عمل كاملين على الأقل، حتى يصلك التنبيه ويكتمل أي تحويل بنكي قبل أن يلامس الرصيد الصفر فعليًّا.


أدلة ذات صلة


لا تدع رصيدك يصل إلى الصفر — اشحن حساب CaptchaAI الآن.

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