Quand votre pipeline de résolution CAPTCHA ralentit ou que le solde tombe à sec, personne ne pense à ouvrir un tableau de bord de plus — mais tout le monde garde Discord ouvert. Un webhook Discord vous laisse pousser l'état du pipeline (solde bas, pics d'erreurs, file qui s'accumule, résumé quotidien) dans le canal où votre équipe travaille déjà, sans interface d'alerte dédiée. Ce guide montre comment câbler ces alertes avec CaptchaAI, en Python puis en Node.js.
Quels signaux méritent une alerte
Tout n'est pas urgent. Quatre signaux couvrent l'essentiel d'un pipeline de résolution CAPTCHA :
- Solde bas. Un solde qui approche de zéro interrompt toutes les résolutions ; c'est l'alerte à ne jamais rater.
- Pic d'erreurs. Une hausse soudaine des échecs trahit souvent un changement côté cible ou un proxy hors service.
- File qui s'accumule. Une file qui gonfle plus vite qu'elle ne se vide révèle un manque de threads.
- Résumé quotidien. Un récapitulatif du taux de réussite, de la latence et du coût donne la tendance sans surveillance active.
À quoi ressemblent les alertes
Avant le code, voici le résultat visé — trois embeds tels qu'ils s'affichent dans le canal. Les alertes de solde et de pics d'erreurs sont câblées plus bas ; la file et le résumé quotidien suivent le même modèle.
Alerte de solde :
💰 Balance Alert Le solde CaptchaAI est de $8.42 Seuil : $10.00 — Gravité : WARNING
Pic d'erreurs :
⚠️ Error Rate Spike Taux d'erreur : 22,5 % (45/200) ·
ERROR_CAPTCHA_UNSOLVABLE: 30 ·TIMEOUT: 15
Résumé quotidien :
📈 Daily CAPTCHA Summary 12 450 tâches traitées Taux de réussite : 95,2 % — Latence moyenne : 22,4 s — Solde : $142.30
Créer le webhook Discord
Un webhook est une simple URL sur laquelle vous envoyez vos messages en POST — aucune application ni bot à héberger côté Discord.
- Ouvrez les paramètres de votre serveur Discord.
- Allez dans Intégrations → Webhooks.
- Cliquez sur Nouveau webhook et nommez-le « Alertes CaptchaAI ».
- Copiez l'URL du webhook.
- Stockez-la dans la variable d'environnement
DISCORD_WEBHOOK_URL— traitez-la comme un secret, au même titre que votre clé API.
Python : surveiller le pipeline et alerter
La classe DiscordCaptchaAlerts construit des embeds colorés — vert, orange, rouge selon la gravité — et PipelineMonitor interroge le solde et le taux d'erreur. Un délai anti-spam (cooldown) de cinq minutes évite qu'un même incident déclenche une avalanche de messages.
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 : le client webhook Discord
La version Node.js reprend la même logique avec axios : elle vérifie le solde toutes les cinq minutes et alerte dès qu'il passe sous $10.
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 };
Réduire le bruit et protéger le webhook
Trois réflexes évitent qu'un canal d'alertes finisse ignoré :
- Anti-spam. Le
cooldownde cinq minutes empêche qu'un incident unique génère cent messages. Regroupez les erreurs sur une fenêtre glissante plutôt que d'alerter à chaque échec. - Secrets. L'URL du webhook donne un accès en écriture à votre canal : ne la committez jamais et faites-la tourner si elle fuite.
- RGPD. N'insérez pas de données personnelles dans les embeds. Un canal Discord reste un traitement de données ; limitez les alertes à des métriques techniques — solde, taux d'erreur, profondeur de file.
Dépannage
| Problème | Cause probable | Correctif |
|---|---|---|
400 Bad Request |
Structure d'embed invalide | Vérifiez le format du tableau fields ; toutes les valeurs doivent être des chaînes de caractères. |
Limite atteinte (429) |
Trop de messages par minute | Espacez les alertes (cooldown de 5 min minimum) et regroupez les événements. |
| Webhook supprimé | Quelqu'un l'a retiré du serveur | Recréez un webhook et mettez à jour DISCORD_WEBHOOK_URL. |
| Les embeds ne s'affichent pas | Tableau embeds absent |
Enveloppez l'objet dans {"embeds": [...]}. |
FAQ
Comment éviter d'être submergé d'alertes ?
Fixez un délai anti-spam — le cooldown de cinq minutes de l'exemple — et regroupez les erreurs sur une fenêtre glissante. Réservez la couleur rouge aux incidents qui exigent une action immédiate ; le reste reste en orange.
Les alertes Discord peuvent-elles poser un problème RGPD ?
Oui, si elles transportent des données personnelles. Un canal Discord est un traitement de données : limitez les embeds à des métriques techniques (solde, taux d'erreur, latence), sans identifiants d'utilisateurs ni URL nominatives.
Où héberger le moniteur qui envoie les alertes ?
N'importe quel petit worker qui tourne en continu convient : une VM OVHcloud ou Scaleway, un conteneur, ou une tâche planifiée. Il lui faut seulement un accès à votre clé API CaptchaAI et à l'URL du webhook.
Discord suffit-il pour les alertes de production ?
Comme canal de visibilité d'équipe, oui. Pour l'astreinte, couplez-le à un outil de paging comme PagerDuty ou Opsgenie : Discord notifie, le paging réveille la bonne personne.
Prochaines étapes
Recevez l'état de votre pipeline là où votre équipe travaille déjà : créez une clé API CaptchaAI et connectez votre premier webhook Discord en quelques minutes.
Guides associés :