مراقبة مسار CAPTCHA تتلخّص عملياً في أربعة تنبيهات: فشل مهمة حل، رصيد يقترب من النفاد، معدل خطأ يتجاوز عتبة معقولة، وملخص يومي يجمع ما جرى في رسالة واحدة. إذا وصلت هذه الأربعة إلى قناة Slack واحدة، فلن تحتاج إلى فتح لوحة التحكم إلا عند التحقيق في حادثة فعلية.
الإعداد لا يتطلب تطبيق Slack كاملاً ولا صلاحيات OAuth ولا Bot Token؛ يكفي Incoming Webhook واحد ودالة إرسال صغيرة في Python أو Node.js. خلال أقل من نصف ساعة ينتقل السكربت من كونه صامتاً — لا تعرف بتعطّله إلا متأخراً — إلى خط أتمتة يرفع يده بنفسه ويقول أين توقّف ولماذا.
ما الذي يستحق إشعاراً في Slack وما الذي لا يستحق
قبل كتابة أي سطر، احسم سياسة التنبيه. القاعدة العملية: نبّه على ما يتطلب قراراً بشرياً، وسجّل الباقي في السجلات فقط.
- فشل مهمة واحدة: مفيد في بيئة صغيرة، وضجيج في خط يعالج آلاف المهام. مكانه قناة تشخيصية منفصلة.
- ارتفاع معدل الخطأ: التنبيه الحقيقي — يعني تغيّراً في السلوك لا حادثة عابرة.
- انخفاض الرصيد: الأعلى أولوية، لأنه الوحيد الذي يمنع توقفاً كاملاً قبل وقوعه.
- الملخص اليومي: تقرير حالة لا تنبيه؛ مكانه قناة الفريق لا قناة الطوارئ.
الفارق بين نظام مراقبة يستخدمه الفريق ونظام يتجاهله الفريق هو هذا التصنيف تحديداً، وليس جودة الكود.
الخطوة 1: أنشئ Incoming Webhook في Slack
الـ Webhook هو عنوان URL سرّي يقبل طلب POST بصيغة JSON وينشره في قناة محددة. أنشئه مرة واحدة لكل قناة:
- افتح api.slack.com/apps وأنشئ تطبيقاً جديداً باسم يدل على وظيفته، مثل
captcha-ops. - من قائمة الميزات اختر Incoming Webhooks وفعّلها.
- اضغط Add New Webhook to Workspace ثم حدد القناة المستهدفة.
- انسخ عنوان الـ Webhook واحفظه في متغيّر بيئة، لا داخل الكود.
عنوان الـ Webhook يعادل صلاحية النشر في القناة، فتعامل معه كما تتعامل مع مفتاح الـ API: خارج المستودع، وقابل لإعادة الإنشاء عند أي شك.
الخطوة 2: دالة إرسال موحّدة لكل التنبيهات
ابدأ بدالة واحدة يستدعيها كل تنبيه لاحق. توحيد الشكل مبكراً يعني أن تغيير التنسيق أو اللون أو إضافة حقل يحدث في مكان واحد فقط:
import requests
import json
from datetime import datetime
SLACK_WEBHOOK_URL = "https://hooks.slack.com/services/T00/B00/xxx"
def send_slack_alert(title, message, color="#ff0000", fields=None):
"""Send a formatted Slack alert."""
attachment = {
"color": color,
"title": title,
"text": message,
"ts": int(datetime.now().timestamp()),
}
if fields:
attachment["fields"] = [
{"title": k, "value": str(v), "short": True}
for k, v in fields.items()
]
payload = {"attachments": [attachment]}
resp = requests.post(SLACK_WEBHOOK_URL, json=payload, timeout=10)
return resp.status_code == 200
الحقول القصيرة — short: True — تُعرض في عمودين، فتصبح الرسالة مقروءة من إشعار الهاتف. والمهلة المحددة بعشر ثوانٍ مقصودة: لا يصحّ أن يتعطّل سكربت الأتمتة وهو ينتظر قناة إشعارات.
الخطوة 3: تنبيه عند فشل مهمة الحل
أول استخدام للدالة هو الإبلاغ عن مهمة لم تكتمل. مرّر معرّف المهمة ونوع الـ CAPTCHA ورمز الخطأ وعنوان الموقع، لأن هذه الحقول الأربعة هي ما يحتاجه من يفتح الرسالة ليحدد مصدر العطل:
def notify_solve_failure(task_id, captcha_type, error_code, site_url):
send_slack_alert(
title="CAPTCHA Solve Failed",
message=f"Task `{task_id}` failed with `{error_code}`",
color="#ff0000",
fields={
"Type": captcha_type,
"Error": error_code,
"Site": site_url,
"Time": datetime.now().strftime("%H:%M:%S"),
},
)
# Use after a failed solve
result = poll_for_result(task_id)
if result.get("error"):
notify_solve_failure(task_id, "recaptcha_v2", result["error"], "https://example.com")
رمز الخطأ وحده يفصل بين مشكلة في بيانات الطلب — مفتاح موقع خاطئ أو صفحة تغيّرت — ومشكلة في التنفيذ مثل انتهاء المهلة. راجع كيفية حلّ reCAPTCHA v2 عبر الـ API قبل اتهام الخدمة.
الخطوة 4: تنبيه انخفاض الرصيد قبل توقّف الخط
الاستفسار عن الرصيد يمر عبر res.php بالإجراء getbalance، ويقارن الناتج بعتبة تحددها أنت. شغّل الفحص الدوري في خيط مستقل حتى لا يعطّل المسار الأساسي:
def check_balance_alert(api_key, threshold=5.0):
"""Alert when balance drops below threshold."""
resp = requests.get("https://ocr.captchaai.com/res.php", params={
"key": api_key, "action": "getbalance", "json": "1"
}).json()
balance = float(resp.get("request", 0))
if balance < threshold:
send_slack_alert(
title="Low CaptchaAI Balance",
message=f"Balance is ${balance:.2f} (threshold: ${threshold:.2f})",
color="#ff9900",
fields={
"Current Balance": f"${balance:.2f}",
"Threshold": f"${threshold:.2f}",
},
)
return balance
# Run periodically
import threading
def balance_monitor(api_key, interval=300):
"""Check balance every 5 minutes."""
check_balance_alert(api_key)
timer = threading.Timer(interval, balance_monitor, args=[api_key, interval])
timer.daemon = True
timer.start()
balance_monitor("YOUR_API_KEY")
اضبط العتبة على أساس استهلاك يومين لا على رقم ثابت. مثال تشغيلي: متجر إلكتروني في القاهرة يشغّل اختبارات QA ليلية على مسار إتمام الشراء، على خطة STANDARD بسعر $30 شهرياً مع 15 thread. عطلة الفريق الجمعة والسبت، أي أن تنبيهاً يصل مساء الخميس قد لا يقرأه أحد قبل الأحد؛ فارفع العتبة قبل العطلة لتغطية ثلاثة أيام.
الفوترة في CaptchaAI قائمة على عدد الـ threads المتزامنة مع حلول غير محدودة داخل الخطة، لذا يتعلق هذا التنبيه بتجديد الاشتراك لا بسعر كل عملية حل. ومن يصطدم بسقف التزامن يراقب طول قائمة الانتظار بدل الرصيد.
الخطوة 5: تنبيه ارتفاع معدل الخطأ مع فترة تهدئة
هذا هو التنبيه الذي يستحق إيقاظ أحد. الفكرة: احتفظ بنافذة متحركة لآخر خمسين نتيجة، واحسب نسبة الفشل، ونبّه فقط عند تجاوزها العتبة مع فترة تهدئة تمنع تكرار الرسالة كل ثانية:
from collections import deque
class ErrorRateNotifier:
def __init__(self, window=50, threshold=0.3, cooldown=300):
self.results = deque(maxlen=window)
self.threshold = threshold
self.cooldown = cooldown
self.last_alert = 0
def record(self, success):
self.results.append(success)
if len(self.results) < 20:
return
error_rate = 1 - sum(self.results) / len(self.results)
import time
now = time.time()
if error_rate > self.threshold and (now - self.last_alert) > self.cooldown:
self.last_alert = now
send_slack_alert(
title="High CAPTCHA Error Rate",
message=f"Error rate: {error_rate:.0%} over last {len(self.results)} tasks",
color="#ff0000",
fields={
"Error Rate": f"{error_rate:.1%}",
"Window": f"{len(self.results)} tasks",
"Threshold": f"{self.threshold:.0%}",
},
)
notifier = ErrorRateNotifier()
# After each solve attempt
notifier.record(success=True) # solved
notifier.record(success=False) # failed
ثلاثة أرقام تُضبط حسب حجم التشغيل: النافذة، وعتبة الفشل، ومدة التهدئة. النافذة الصغيرة تجعل التنبيه عصبياً أمام تقلب عابر، والكبيرة تجعله بطيئاً أمام انقطاع حقيقي. واشتراط عشرين نتيجة قبل الحساب يمنع إنذاراً كاذباً بعد النشر مباشرة.
الخطوة 6: المنظومة نفسها في Node.js
إن كانت الأتمتة مبنية على Puppeteer أو Playwright، فالمنطق نفسه ينتقل إلى Axios دون تغيير في البنية — الدالة الموحّدة أولاً، ثم تنبيهات الفشل والرصيد فوقها:
const axios = require('axios');
const SLACK_WEBHOOK = 'https://hooks.slack.com/services/T00/B00/xxx';
async function sendSlackAlert(title, message, color = '#ff0000', fields = {}) {
const attachment = {
color,
title,
text: message,
ts: Math.floor(Date.now() / 1000),
fields: Object.entries(fields).map(([k, v]) => ({
title: k, value: String(v), short: true,
})),
};
await axios.post(SLACK_WEBHOOK, { attachments: [attachment] });
}
// Failure alert
async function notifySolveFailure(taskId, type, error) {
await sendSlackAlert(
'CAPTCHA Solve Failed',
`Task \`${taskId}\` failed: \`${error}\``,
'#ff0000',
{ Type: type, Error: error }
);
}
// Balance alert
async function checkBalance(apiKey, threshold = 5.0) {
const resp = await axios.get('https://ocr.captchaai.com/res.php', {
params: { key: apiKey, action: 'getbalance', json: 1 },
});
const balance = parseFloat(resp.data.request);
if (balance < threshold) {
await sendSlackAlert(
'Low CaptchaAI Balance',
`Balance: $${balance.toFixed(2)}`,
'#ff9900',
{ Balance: `$${balance.toFixed(2)}`, Threshold: `$${threshold.toFixed(2)}` }
);
}
return balance;
}
// Periodic check
setInterval(() => checkBalance('YOUR_API_KEY'), 5 * 60 * 1000);
استخدام setInterval مقبول في عملية طويلة العمر. أما في بيئة serverless أو حاوية تُعاد جدولتها، فاجعل الفحص مهمة مجدولة خارجية، وإلا وصلت نسخ متعددة من التنبيه نفسه.
الخطوة 7: ملخص يومي بدل التنبيهات المتفرقة
الملخص اليومي يعوّض التنبيهات التي أسكتّها عمداً. رسالة واحدة في وقت ثابت تعطي الاتجاه العام: كم مهمة نُفذت، كم نجحت، ما متوسط وقت الحل، وكم بلغت التكلفة:
def send_daily_summary(stats):
"""Send a daily digest to Slack."""
send_slack_alert(
title="Daily CAPTCHA Summary",
message=f"{stats['total']} tasks processed",
color="#36a64f",
fields={
"Solved": stats["solved"],
"Failed": stats["failed"],
"Avg Solve Time": f"{stats['avg_time_ms']}ms",
"Total Cost": f"${stats['total_cost']:.2f}",
"Success Rate": f"{stats['success_rate']:.1%}",
},
)
أرسله في وقت يناسب فريقك — التاسعة صباحاً بتوقيت الرياض أو القاهرة — لا في منتصف الليل بتوقيت UTC. ومع تراكم الأسابيع يصبح لديك خط أساس رقمي تقارن به أي يوم غير طبيعي، بدل الانطباع العام بأن الأمور تبدو أبطأ.
وجّه كل حدث إلى القناة المناسبة
التنبيه الذي يصل إلى القناة الخطأ يعادل تنبيهاً لم يصل. هذا توزيع عملي يصلح لمعظم الفرق:
| الحدث | القناة | التصعيد |
|---|---|---|
| فشل مهمة مفردة | قناة تشخيصية | بلا تصعيد |
| معدل خطأ فوق العتبة | قناة التشغيل | إشعار الهاتف مفعّل |
| رصيد منخفض | التشغيل + الفوترة | متابعة خلال ساعات |
| ملخص يومي | قناة الفريق | للقراءة فقط |
أخطاء شائعة أثناء الإعداد
| العرض | السبب الأرجح | المعالجة |
|---|---|---|
| الـ Webhook يعيد 403 | عنوان غير صالح أو تطبيق محذوف | أعد إنشاءه من إعدادات Slack |
| سيل من التنبيهات | لا توجد فترة تهدئة | فعّل التهدئة واربط التنبيه بمعدل الخطأ |
| وصول متأخر للرسائل | مهلة طويلة أو شبكة بطيئة | حدد المهلة بعشر ثوانٍ وأرسل في خيط منفصل |
| القناة لا تستقبل شيئاً | الـ Webhook مرتبط بقناة أخرى | تحقق من القناة المرتبطة بالعنوان |
الأسئلة الشائعة
أين أحفظ عنوان الـ Webhook بأمان؟
في متغيّر بيئة أو مدير أسرار، وليس في الكود ولا في ملف مرفوع إلى GitHub. أضف ملف الإعدادات إلى .gitignore، واستبدل العنوان فوراً إذا ظهر في سجل commit سابق — إعادة إنشائه من إعدادات التطبيق تستغرق ثوانٍ.
كيف أمنع ظهور مفتاح الـ API داخل نص التنبيه؟
لا تمرّر كائن الاستجابة كاملاً إلى دالة الإرسال. اختر الحقول التي تحتاجها فقط — معرّف المهمة ورمز الخطأ ونوع الـ CAPTCHA — وإن اضطررت لعرض جزء من المفتاح فاكتف بآخر أربعة محارف منه.
هل أحتاج تطبيق Slack كامل أم يكفي Incoming Webhook؟
للإشعارات أحادية الاتجاه يكفي الـ Webhook تماماً. أما إن أردت أزراراً تفاعلية مثل "أعد المحاولة" أو "أوقف الخط مؤقتاً"، فستحتاج عندها تطبيقاً بصلاحيات وBot Token ونقطة نهاية عامة تستقبل تفاعلات المستخدمين.
كيف أعرف نوع الـ CAPTCHA المسبب للمشكلة من الرسالة؟
مرّر نوع الـ CAPTCHA كحقل مستقل في كل تنبيه، ولا تكتفِ بنص الخطأ. عندها يكفي البحث في القناة عن recaptcha_v2 أو turnstile لتعرف هل المشكلة في نوع واحد أم في المسار كله. وفي بداية التكامل ابدأ من دليل البدء السريع مع CaptchaAI.
راقب خط الأتمتة من داخل Slack
أنشئ مفتاح الـ API من captchaai.com، ثم اربط أول تنبيه بقناة فريقك وشغّل مهمة اختبار واحدة للتأكد من وصول الرسالة قبل الاعتماد عليها في الإنتاج.