Biarkan pipeline CAPTCHA Anda yang melapor sendiri ke Slack. Satu incoming webhook dan sekitar tiga puluh baris Python sudah cukup untuk memasang tiga alert yang menutup hampir semua insiden nyata: solve yang gagal, saldo CaptchaAI yang menipis, dan error rate yang melonjak di luar batas wajar.
Tanpa itu, pola kegagalannya selalu sama. Worker scraping berjalan semalaman, saldo akun habis pukul dua pagi, dan Anda baru menyadarinya besok siang saat klien menanyakan kenapa data harga hari ini kosong. Yang mahal bukan kegagalannya, melainkan jarak waktu antara "gagal" dan "tahu bahwa gagal".
Langkah 1: buat incoming webhook Slack
Slack tidak memerlukan bot berizin penuh di sini. Incoming webhook sudah cukup, dan pembuatannya kurang dari dua menit.
- Buka api.slack.com/apps lalu pilih Create New App
- Aktifkan Incoming Webhooks pada aplikasi tersebut
- Klik Add New Webhook to Workspace, lalu pilih channel tujuan alert
- Salin URL webhook yang muncul
Simpan URL itu sebagai environment variable, jangan di repo: siapa pun yang memegangnya bisa mengirim pesan ke channel Anda. Buat juga channel terpisah seperti #captcha-alerts, karena alert yang tenggelam di antara obrolan harian sama saja dengan tidak ada alert.
Langkah 2: satu helper untuk semua alert
Semua notifikasi berikutnya memakai fungsi yang sama. Helper ini memformat pesan sebagai attachment Slack dengan warna, judul, dan field yang terbaca sekilas dari notifikasi ponsel. Perhatikan batas waktu 10 detik pada request: tanpa itu, panggilan webhook yang menggantung bisa menahan worker lebih lama daripada penyelesaian CAPTCHA itu sendiri.
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
Konvensi warna yang membantu: merah untuk kegagalan yang perlu tindakan, oranye untuk peringatan dini seperti saldo, hijau untuk ringkasan rutin.
Langkah 3: alert saat solve gagal
Alert paling dasar. Setiap kali res.php mengembalikan kode error alih-alih token, kirim satu pesan berisi task ID, tipe CAPTCHA, kode error, dan URL target.
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")
Satu catatan sebelum memasangnya di produksi: jangan kirim alert untuk setiap kegagalan individual. Solve yang gagal sesekali adalah hal normal, dan channel yang berbunyi lima puluh kali per jam akan di-mute dalam seminggu. Pakai pola ini di staging atau job batch kecil; untuk trafik produksi, langsung gunakan alert error rate di Langkah 5.
Langkah 4: alert saat saldo CaptchaAI menipis
Alert yang paling sering menyelamatkan tim kecil. Endpoint res.php dengan action=getbalance mengembalikan saldo akun Anda, dan timer sederhana memeriksanya setiap lima menit.
def check_balance_alert(api_key, threshold=5.0):
"""Alert when balance rilis pengujian 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")
Ambang batas $5.00 pada contoh di atas hanya titik awal; sesuaikan dengan pola pemakaian Anda. Perlu dicatat bahwa paket CaptchaAI ditagih per thread yang berjalan bersamaan, bukan per solve. BASIC $15/bulan dengan 5 thread, STANDARD $30/bulan dengan 15 thread, ADVANCE $90/bulan dengan 50 thread, naik sampai VIP-3 $7,500/bulan dengan 5.000 thread, dan setiap paket memberi solve tanpa batas per thread. Karena itu alert saldo di sini berperan sebagai pengingat siklus pembayaran, bukan penghitung sisa kuota solve. Semua harga dalam USD.
Untuk pekerja lepas dan agensi price-monitoring yang menagih klien per proyek, satu alert saldo plus satu ringkasan harian sudah memberi gambaran biaya tanpa perlu membangun sistem pelaporan sendiri.
Langkah 5: alert saat error rate melonjak
Alert yang paling berharga di produksi. Alih-alih bereaksi pada satu kegagalan, kelas berikut menyimpan hasil 50 solve terakhir dalam jendela geser dan hanya berbunyi ketika proporsi kegagalan melewati ambang batas, dengan cooldown supaya satu insiden tidak memicu puluhan pesan.
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
Yang ditangkap pola ini biasanya bukan masalah di sisi solver, melainkan perubahan di sisi target: sitekey berganti, halaman berpindah dari reCAPTCHA v2 ke Turnstile, atau parameter yang Anda kirim ke in.php sudah tidak cocok lagi. Ketika lonjakan terjadi, cek dulu parameter permintaan Anda terhadap dokumentasi tipe yang bersangkutan.
Langkah 6: versi Node.js
Kalau worker Anda berjalan di Node.js, logikanya identik: requests menjadi axios, threading.Timer menjadi setInterval.
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);
Langkah 7: ringkasan harian sebagai penyeimbang
Alert memberi tahu Anda saat ada yang rusak; ringkasan harian memberi tahu Anda saat ada yang perlahan memburuk. Tingkat keberhasilan yang turun dari 96% ke 88% selama dua minggu tidak akan memicu satu pun alert, tetapi jelas terlihat di digest.
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%}",
},
)
Kirim ringkasan ini sekali sehari lewat cron, pada jam ketika tim Anda benar-benar membaca Slack. Untuk tim di Jakarta, pukul 09.00 WIB jauh lebih efektif daripada tengah malam UTC.
Alert mana untuk situasi apa
| Alert | Pemicu | Frekuensi wajar | Cocok untuk |
|---|---|---|---|
| Solve gagal | satu task mengembalikan error | per event | staging, batch kecil |
| Saldo menipis | saldo di bawah ambang batas | tiap 5 menit, dengan cooldown | semua tim |
| Error rate tinggi | lebih dari 30% gagal dalam 50 task terakhir | maksimum tiap 5 menit | produksi |
| Ringkasan harian | jadwal cron | sekali sehari | pelaporan ke klien |
Masalah umum dan solusinya
| Masalah | Penyebab | Solusi |
|---|---|---|
| Webhook mengembalikan 403 | URL webhook tidak valid atau sudah dicabut | Buat ulang webhook di Slack |
| Terlalu banyak alert | Tidak ada cooldown | Tambahkan periode cooldown antar alert |
| Alert datang terlambat | Request webhook menggantung tanpa batas waktu | Tetapkan batas waktu 10 detik pada setiap request |
| Channel tidak menerima apa pun | Channel yang dipilih salah saat pembuatan webhook | Periksa konfigurasi channel di halaman webhook |
| Alert saldo berbunyi terus | Ambang batas terlalu tinggi | Turunkan ambang batas dan tambahkan cooldown harian |
Pertanyaan Umum
Berapa ambang batas error rate yang masuk akal untuk mulai?
Mulai dari 30% pada jendela 50 task seperti contoh di atas, lalu sesuaikan setelah Anda mengetahui baseline normal pipeline Anda. Ambang batas yang terlalu ketat sejak hari pertama membuat tim cepat mengabaikan channel alert.
Apakah alert ini berlaku untuk semua tipe CAPTCHA yang didukung CaptchaAI?
Ya, karena alert bekerja pada respons res.php, bukan pada tipe tantangan tertentu. Pola yang sama berlaku untuk reCAPTCHA v2 dan v3, Cloudflare Turnstile dan Cloudflare Challenge, GeeTest v3, serta CAPTCHA gambar dan grid. Untuk CaptchaFox (beta), Friendly Captcha (beta), dan Lemin (beta) alert tetap berjalan, hanya saja jangan jadikan angkanya sebagai baseline karena statusnya masih beta.
Perlukah monitor saldo dijalankan di server terpisah?
Tidak untuk skala kecil; threading.Timer di dalam proses worker sudah memadai. Begitu ada lebih dari satu worker, pindahkan pemeriksaan saldo ke satu cron job tersendiri, karena kalau tidak sepuluh worker akan mengirim sepuluh alert identik.
Bagaimana menghubungkan alert ini dengan log yang sudah ada?
Sertakan task ID di setiap pesan Slack dan pastikan task ID yang sama muncul di log terstruktur Anda, sehingga satu alert langsung bisa ditelusuri ke satu baris log. Tanpa ID yang konsisten, alert hanya memberi tahu bahwa ada masalah, bukan di mana letaknya.
Bisakah pola yang sama dipakai di Discord atau Telegram?
Bisa. Webhook Discord menerima JSON serupa, cukup ganti struktur payload dari attachments menjadi embeds. Untuk Telegram, ganti pemanggilan webhook dengan Bot API sendMessage. Struktur helper dan logika ambang batasnya tidak berubah.
Pasang alert pertama Anda hari ini
Ambil API key CaptchaAI di captchaai.com, tempelkan URL webhook Slack Anda, lalu jalankan monitor saldo. Dua alert pertama sudah aktif sebelum kopi Anda dingin.