Eine CAPTCHA-Pipeline fällt selten mit einem lauten Knall aus: Das Guthaben läuft leer, die Fehlerrate steigt schleichend, die Warteschlange staut sich – und niemand bemerkt es, bis die ersten Aufträge scheitern. Ein Discord-Webhook schließt diese Lücke. Er schiebt Guthabenwarnungen, Fehlerspitzen und Tagesberichte direkt in den Kanal, in dem Ihr Team ohnehin arbeitet – ohne eigenes Dashboard, mit wenigen Zeilen Code.
Webhook in Discord anlegen
- Öffnen Sie die Einstellungen Ihres Discord-Servers
- Gehen Sie zu Integrationen → Webhooks
- Klicken Sie auf Neuer Webhook und benennen Sie ihn „CaptchaAI Alerts“
- Kopieren Sie die Webhook-URL
- Hinterlegen Sie sie als Umgebungsvariable
DISCORD_WEBHOOK_URL
Tipp: pro Umgebung ein eigener Webhook – ein Kanal für Staging, einer für die Produktion. So bleiben Testrauschen und echte Vorfälle getrennt.
Python: Alert-System für die Pipeline
Die Klasse DiscordCaptchaAlerts kapselt vier Meldungstypen – Guthaben, Fehlerspitze, Warteschlange und Tagesbericht – in farbcodierte Embeds. Jeder Typ hat eine eigene Methode, sodass Ihr Code nur die passende aufruft und sich nicht um das Payload-Format kümmern muss. Der PipelineMonitor sammelt die Ergebnisse der letzten fünf Minuten, prüft Guthaben und Fehlerrate und drosselt Wiederholungen über einen Cooldown.
import os
import time
import requests
from datetime import datetime
API_KEY = os.environ["CAPTCHAAI_API_KEY"]
DISCORD_WEBHOOK = os.environ["DISCORD_WEBHOOK_URL"]
session = requests.Session()
class DiscordCaptchaAlerts:
COLORS = {
"success": 0x2ECC71, # Green
"warning": 0xF39C12, # Orange
"error": 0xE74C3C, # Red
"info": 0x3498DB, # Blue
}
def __init__(self, webhook_url):
self.webhook_url = webhook_url
def send_embed(self, title, description, color_key="info", fields=None):
embed = {
"title": title,
"description": description,
"color": self.COLORS.get(color_key, self.COLORS["info"]),
"timestamp": datetime.utcnow().isoformat() + "Z",
"footer": {"text": "CaptchaAI Pipeline Monitor"}
}
if fields:
embed["fields"] = fields
payload = {"embeds": [embed]}
resp = requests.post(
self.webhook_url, json=payload, timeout=10
)
resp.raise_for_status()
def balance_alert(self, balance, threshold):
severity = "error" if balance < 2 else "warning"
self.send_embed(
title="💰 Balance Alert",
description=f"CaptchaAI balance is **${balance:.2f}**",
color_key=severity,
fields=[
{"name": "Threshold", "value": f"${threshold:.2f}", "inline": True},
{"name": "Severity", "value": severity.upper(), "inline": True},
{"name": "Action", "value": "Top up your balance at captchaai.com", "inline": False}
]
)
def error_spike(self, error_rate, error_count, total_count, top_errors):
error_list = "\n".join(
f"• `{code}`: {count}" for code, count in top_errors.items()
)
self.send_embed(
title="⚠️ Error Rate Spike",
description=f"Error rate: **{error_rate:.1%}** ({error_count}/{total_count})",
color_key="error",
fields=[
{"name": "Error Breakdown", "value": error_list or "No details", "inline": False},
{"name": "Window", "value": "Last 5 minutes", "inline": True}
]
)
def queue_alert(self, depth, workers_active):
self.send_embed(
title="📊 Queue Backup",
description=f"Queue depth: **{depth}** pending tasks",
color_key="warning",
fields=[
{"name": "Active Workers", "value": str(workers_active), "inline": True},
{"name": "Est. Drain Time", "value": f"{depth // max(workers_active, 1)} min", "inline": True}
]
)
def daily_summary(self, stats):
self.send_embed(
title="📈 Daily CAPTCHA Summary",
description=f"**{stats['total']}** tasks processed",
color_key="success" if stats["success_rate"] > 0.92 else "warning",
fields=[
{"name": "Success Rate", "value": f"{stats['success_rate']:.1%}", "inline": True},
{"name": "Avg Latency", "value": f"{stats['avg_latency']:.1f}s", "inline": True},
{"name": "Total Cost", "value": f"${stats['cost']:.2f}", "inline": True},
{"name": "Errors", "value": str(stats["errors"]), "inline": True},
{"name": "Balance", "value": f"${stats['balance']:.2f}", "inline": True},
{"name": "Peak Queue", "value": str(stats["peak_queue"]), "inline": True},
]
)
def solve_recovered(self, previous_rate, current_rate):
self.send_embed(
title="✅ Pipeline Recovered",
description=f"Solve rate recovered: {previous_rate:.1%} → {current_rate:.1%}",
color_key="success"
)
alerts = DiscordCaptchaAlerts(DISCORD_WEBHOOK)
class PipelineMonitor:
def __init__(self, check_interval=60):
self.check_interval = check_interval
self.results = [] # (timestamp, success, error_code)
self.last_balance_alert = 0
self.last_error_alert = 0
self.cooldown = 300 # 5 minutes between alerts
def record(self, success, error_code=None):
self.results.append((time.time(), success, error_code))
# Keep last 5 min
cutoff = time.time() - 300
self.results = [r for r in self.results if r[0] > cutoff]
def run_checks(self):
now = time.time()
# Balance check
if now - self.last_balance_alert > self.cooldown:
balance = self._check_balance()
if balance is not None and balance < 10:
alerts.balance_alert(balance, threshold=10)
self.last_balance_alert = now
# Error rate check
if now - self.last_error_alert > self.cooldown and len(self.results) > 10:
total = len(self.results)
errors = [r for r in self.results if not r[1]]
error_rate = len(errors) / total
if error_rate > 0.15:
error_breakdown = {}
for _, _, code in errors:
if code:
error_breakdown[code] = error_breakdown.get(code, 0) + 1
alerts.error_spike(error_rate, len(errors), total, error_breakdown)
self.last_error_alert = now
def _check_balance(self):
try:
resp = session.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "getbalance", "json": 1
})
data = resp.json()
if data.get("status") == 1:
return float(data["request"])
except Exception:
pass
return None
monitor = PipelineMonitor()
JavaScript: Webhook-Client für Node.js
Dieselbe Logik als schlanker Node.js-Client: sendDiscordEmbed baut das Embed, die Guthabenprüfung läuft über setInterval alle fünf Minuten.
const axios = require("axios");
const API_KEY = process.env.CAPTCHAAI_API_KEY;
const DISCORD_WEBHOOK = process.env.DISCORD_WEBHOOK_URL;
const COLORS = {
success: 0x2ecc71,
warning: 0xf39c12,
error: 0xe74c3c,
info: 0x3498db,
};
async function sendDiscordEmbed(title, description, colorKey = "info", fields = []) {
await axios.post(DISCORD_WEBHOOK, {
embeds: [
{
title,
description,
color: COLORS[colorKey] || COLORS.info,
timestamp: new Date().toISOString(),
footer: { text: "CaptchaAI Pipeline Monitor" },
fields,
},
],
}, { timeout: 10000 });
}
async function alertBalance(balance, threshold = 10) {
const severity = balance < 2 ? "error" : "warning";
await sendDiscordEmbed(
"💰 Balance Alert",
`CaptchaAI balance is **$${balance.toFixed(2)}**`,
severity,
[
{ name: "Threshold", value: `$${threshold.toFixed(2)}`, inline: true },
{ name: "Severity", value: severity.toUpperCase(), inline: true },
]
);
}
async function alertErrorSpike(errorRate, details = {}) {
await sendDiscordEmbed(
"⚠️ Error Rate Spike",
`Error rate: **${(errorRate * 100).toFixed(1)}%**`,
"error",
[
{ name: "Total Tasks", value: String(details.total || 0), inline: true },
{ name: "Errors", value: String(details.errors || 0), inline: true },
]
);
}
async function sendDailySummary(stats) {
const color = stats.successRate > 0.92 ? "success" : "warning";
await sendDiscordEmbed(
"📈 Daily CAPTCHA Summary",
`**${stats.total}** tasks processed`,
color,
[
{ name: "Success Rate", value: `${(stats.successRate * 100).toFixed(1)}%`, inline: true },
{ name: "Avg Latency", value: `${stats.avgLatency.toFixed(1)}s`, inline: true },
{ name: "Balance", value: `$${stats.balance.toFixed(2)}`, inline: true },
]
);
}
// Balance monitoring loop
async function monitorBalance() {
try {
const resp = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: API_KEY, action: "getbalance", json: 1 },
});
if (resp.data.status === 1) {
const balance = parseFloat(resp.data.request);
if (balance < 10) await alertBalance(balance);
}
} catch (err) {
console.error("Balance check failed:", err.message);
}
}
setInterval(monitorBalance, 300000); // Every 5 minutes
module.exports = { alertBalance, alertErrorSpike, sendDailySummary };
Den Monitor dauerhaft betreiben
Damit die Prüfungen greifen, rufen Sie run_checks() in einer Schleife im Abstand von check_interval Sekunden auf – als eigener Prozess, als systemd-Timer oder als geplanter GitLab-CI-Job. Jede gelöste oder gescheiterte CAPTCHA-Abfrage meldet Ihr Code über record(success, error_code) an den PipelineMonitor; dieser behält nur die letzten fünf Minuten und berechnet daraus die Fehlerrate.
Der cooldown von 300 Sekunden stellt sicher, dass Guthaben- und Fehleralarme höchstens alle fünf Minuten erneut feuern – lange genug, damit ein kurzer Ausschlag nicht den halben Kanal füllt. Den Tagesbericht lösen Sie separat einmal täglich aus, etwa per Cron um 8 Uhr, und übergeben die aggregierten Kennzahlen an daily_summary().
So sehen die Meldungen im Kanal aus
Discord rendert die Embeds als kompakte, farbige Karten:
Guthabenwarnung:
Balance-Alarm Das CaptchaAI-Guthaben beträgt 8,42 $ Schwellenwert: 10,00 $ | Schweregrad: WARNUNG
Fehlerspitze:
Spitze der Fehlerrate Fehlerrate: 22,5 % (45/200) •
ERROR_CAPTCHA_UNSOLVABLE: 30 •TIMEOUT: 15
Tägliche Zusammenfassung:
📈 Tägliche CAPTCHA-Zusammenfassung 12.450 Aufgaben bearbeitet Erfolgsquote: 95,2 % | Durchschnittliche Latenz: 22,4 s | Guthaben: 142,30 $
Die Farbe signalisiert den Schweregrad auf einen Blick: Grün steht für Erholung und eine gesunde Tagesbilanz, Orange für Warnungen wie ein knappes Guthaben, Rot für Fehlerspitzen. So erkennt das Team die Dringlichkeit schon am farbigen Rand der Karte, noch bevor es den Text liest.
Welche Alarme sich in der Praxis lohnen
Nicht jede Zahl gehört in den Kanal – sonst klickt das Team Meldungen nur noch weg. Vier Signale reichen:
- Guthaben unter dem Schwellenwert
- Fehlerrate über 15 % im Fünf-Minuten-Fenster
- Warteschlange, die sich sichtbar staut
- Tagesbericht mit Erfolgsquote, Latenz und Kosten
In vielen DACH-Teams läuft die Automatisierung auf einem eigenen Hetzner- oder netcup-Server, ausgerollt über GitLab CI; der Tagesbericht um 8 Uhr ersetzt dort das Nachsehen im Dashboard.
Fehlerbehebung
| Problem | Ursache | Lösung |
|---|---|---|
| Discord-Webhook liefert HTTP 400 | Payload-Format falsch oder Embed zu groß | Nachricht kürzen und Embed-Struktur validieren |
| Rate-Limit (HTTP 429) | Zu viele Nachrichten pro Minute | Cooldown von mindestens 5 Minuten zwischen Alarmen einhalten |
| Keine Benachrichtigung bei CAPTCHA-Fehler | Fehler löst keine Bedingung aus | Schwellwerte für die Fehlerrate und Logging-Pfad prüfen |
| Zu viele Benachrichtigungen | Alarmierung ohne Hysterese | Mindestabstand zwischen gleichartigen Alarmen einfügen |
| Webhook liefert 404 | Webhook wurde vom Server gelöscht | Neuen Webhook anlegen, DISCORD_WEBHOOK_URL aktualisieren |
Häufige Fragen
Wie verhindere ich, dass mich der Webhook mit Nachrichten überflutet?
Über einen Cooldown. Der PipelineMonitor merkt sich pro Alarmtyp die letzte Meldung und unterdrückt Wiederholungen für mindestens fünf Minuten – so löst eine Fehlerwelle genau eine Warnung aus, nicht eine pro Durchlauf.
Kann ich bei kritischen Alarmen eine Rolle oder Person taggen?
Ja. Ergänzen Sie im Payload das Feld "content" mit <@USER_ID> oder <@&ROLE_ID>. Discord löst die Erwähnung als Push-Benachrichtigung aus – sinnvoll nur für echte Vorfälle wie ein aufgebrauchtes Guthaben.
Wie oft sollte ich das Guthaben abfragen?
Fünf Minuten reichen in fast allen Fällen. Häufigere Abfragen bringen kaum Nutzen, da sich das Guthaben nur pro gelöster CAPTCHA-Abfrage ändert. Der Schwellenwert (im Beispiel 10 $) sollte früh genug vor dem Leerlauf warnen.
Werden über den Webhook sensible Kontodaten offengelegt?
Nein, solange Sie nur aufbereitete Kennzahlen senden. CAPTCHAAI_API_KEY und Webhook-URL gehören ausschließlich in Umgebungsvariablen, niemals in den Nachrichtentext.
Kann ich Guthaben, Fehler und Tagesbericht in getrennte Kanäle schicken?
Ja. Legen Sie mehrere Webhooks an und übergeben Sie jede URL an eine eigene DiscordCaptchaAlerts-Instanz. So landen laute Betriebsmeldungen und der ruhige Tagesbericht nicht im selben Kanal – praktisch, wenn nur das Bereitschaftsteam die roten Fehleralarme sehen soll.
Verwandte Leitfäden
- CAPTCHA-Benachrichtigungen per Slack-Bot
- CaptchaAI-Alarme mit PagerDuty verbinden
- Strukturiertes Logging für CAPTCHA-Vorgänge