Webhook ile Discord Bot Bildirimleri Nasıl Kurulur
Sunucu monitoring altyapısı kurarken en çok canımı sıkan şey şuydu: kritik bir alarm çaldığında, o alarmın önüne geçmesi için ayrı bir araç daha kurmak zorunda kalmak. Nagios alarm gönderdi, Zabbix alert fırlattı, ama sen o anda bilgisayar başında değilsin. E-posta mı? Kim bakıyor e-postaya artık. İşte tam bu noktada Discord webhook’ları hayat kurtarıcı oluyor. Ekibinle zaten Discord’da iletişim kuruyorsanız, neden alarm bildirimlerini de oraya taşımayasın?
Bu yazıda sıfırdan Discord webhook kurulumu yapacak, Bash ve Python ile notification script’leri yazacak, gerçek monitoring senaryolarında kullanacak ve sonunda production’da kullanılabilir bir sistem ortaya çıkaracağız.
Discord Webhook Nedir, Nasıl Çalışır?
Discord webhook’u, bir Discord kanalına dışarıdan mesaj göndermenizi sağlayan özel bir URL’dir. Bot token’ı, OAuth akışı, API anahtarı yok. Sadece o URL’e HTTP POST isteği atıyorsunuz, mesaj kanala düşüyor. Bu kadar basit.
Teknik olarak bakınca: Discord, her webhook için benzersiz bir endpoint oluşturuyor. Bu endpoint’e JSON payload gönderdiğinizde, Discord bunu ilgili kanala bot mesajı olarak basıyor. Rate limit var tabii (saniyede 5 istek, kanal başına dakikada 30 mesaj) ama monitoring amaçlı kullanım için bu limitler yeterince geniş.
Webhook URL’sinin formatı şöyle görünür:
https://discord.com/api/webhooks/{webhook.id}/{webhook.token}
Bu URL’yi kimseyle paylaşmayın. Ele geçiren herkes kanalınıza mesaj atabilir.
Discord Webhook Oluşturma
Önce Discord tarafında webhook’u oluşturmamız lazım.
- Discord’da bildirim göndermek istediğiniz sunucuya ve kanala gidin
- Kanalın yanındaki dişli ikonuna tıklayın (Kanal Ayarları)
- Sol menüden Entegrasyonlar seçin
- Webhook Oluştur butonuna tıklayın
- Webhook’a bir isim verin (örneğin “Server Monitor” veya “Alert Bot”)
- İsteğe bağlı olarak bir avatar resmi ekleyebilirsiniz
- Webhook URL’sini Kopyala diyerek URL’yi alın
Bu URL’yi güvenli bir yerde saklayın. Ben genellikle /etc/monitoring/discord.conf gibi bir dosyaya koyup izinlerini 600 yapıyorum.
# Webhook URL'yi config dosyasına kaydet
mkdir -p /etc/monitoring
cat > /etc/monitoring/discord.conf << 'EOF'
DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/YOUR_ID/YOUR_TOKEN"
EOF
chmod 600 /etc/monitoring/discord.conf
chown root:root /etc/monitoring/discord.conf
İlk Test: Basit curl ile Mesaj Gönderme
Teoride her şey güzel görünüyor. Hemen pratiğe geçelim ve curl ile ilk mesajı gönderelim.
#!/bin/bash
# Basit Discord webhook testi
WEBHOOK_URL="https://discord.com/api/webhooks/YOUR_ID/YOUR_TOKEN"
curl -H "Content-Type: application/json"
-d '{"content": "Merhaba! Bu bir test mesajıdır."}'
"$WEBHOOK_URL"
Bu çalışırsa Discord kanalınızda mesajı göreceksiniz. Ama bu çok temel. Güzel, renkli, embed formatında mesajlar göndermek için biraz daha işin içine girmek gerekiyor.
Embed Formatında Zengin Bildirimler
Discord embed’leri, mesajlarınızı çok daha okunaklı hale getirir. Renk, başlık, açıklama, alanlar, footer ekleyebilirsiniz. Monitoring için bu özellikler altın değerinde.
#!/bin/bash
# Embed formatında Discord bildirimi gönderme scripti
source /etc/monitoring/discord.conf
send_discord_alert() {
local title="$1"
local description="$2"
local color="$3" # Decimal renk kodu (kırmızı: 15158332, sarı: 16776960, yeşil: 3066993)
local hostname=$(hostname -f)
local timestamp=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
local payload=$(cat <<EOF
{
"embeds": [
{
"title": "$title",
"description": "$description",
"color": $color,
"footer": {
"text": "Sunucu: $hostname"
},
"timestamp": "$timestamp"
}
]
}
EOF
)
curl -s -o /dev/null -w "%{http_code}"
-H "Content-Type: application/json"
-d "$payload"
"$DISCORD_WEBHOOK_URL"
}
# Kullanım örnekleri
send_discord_alert "Disk Uyarısı" "/var partition %90 dolu!" 16776960
send_discord_alert "Kritik Hata" "MySQL servisi çöktü!" 15158332
send_discord_alert "Sistem Normale Döndü" "MySQL servisi yeniden başlatıldı." 3066993
Renk kodları için not: Discord decimal renk değeri kullanıyor. #FF0000 kırmızısı için 16711680, #FFFF00 sarısı için 16776960, #2ECC71 yeşili için 3066993 kullanabilirsiniz.
Gelişmiş Script: Çoklu Alan Desteği ile CPU/RAM/Disk Monitoring
Gerçek dünyada tek satır açıklama yetmez. Sunucunun genel durumunu özetleyen, birden fazla metrik içeren bildirimler göndermek istiyoruz.
#!/bin/bash
# /usr/local/bin/discord-monitor.sh
# Sunucu kaynak kullanımını Discord'a gönderir
set -euo pipefail
source /etc/monitoring/discord.conf
# Eşik değerleri
CPU_THRESHOLD=80
MEM_THRESHOLD=85
DISK_THRESHOLD=90
HOSTNAME=$(hostname -f)
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
ALERT_TRIGGERED=false
ALERT_COLOR=3066993 # Varsayılan yeşil
# Metrikleri topla
CPU_USAGE=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1 | cut -d',' -f1)
CPU_USAGE=${CPU_USAGE%.*} # Float'tan integer'a
MEM_TOTAL=$(free -m | awk 'NR==2{print $2}')
MEM_USED=$(free -m | awk 'NR==2{print $3}')
MEM_PERCENT=$((MEM_USED * 100 / MEM_TOTAL))
DISK_INFO=$(df -h / | awk 'NR==2{print $5 " (" $3 "/" $2 ")"}')
DISK_PERCENT=$(df / | awk 'NR==2{print $5}' | tr -d '%')
LOAD_AVG=$(uptime | awk -F'load average:' '{print $2}' | xargs)
# Alert durumunu belirle
if [ "$CPU_USAGE" -gt "$CPU_THRESHOLD" ] ||
[ "$MEM_PERCENT" -gt "$MEM_THRESHOLD" ] ||
[ "$DISK_PERCENT" -gt "$DISK_THRESHOLD" ]; then
ALERT_TRIGGERED=true
ALERT_COLOR=15158332 # Kırmızı
fi
# CPU ve RAM için emoji belirleme
get_status_emoji() {
local value=$1
local threshold=$2
if [ "$value" -gt "$threshold" ]; then
echo "🔴"
elif [ "$value" -gt $((threshold - 15)) ]; then
echo "🟡"
else
echo "🟢"
fi
}
CPU_EMOJI=$(get_status_emoji "$CPU_USAGE" "$CPU_THRESHOLD")
MEM_EMOJI=$(get_status_emoji "$MEM_PERCENT" "$MEM_THRESHOLD")
DISK_EMOJI=$(get_status_emoji "$DISK_PERCENT" "$DISK_THRESHOLD")
TITLE="Sunucu Durum Raporu"
if [ "$ALERT_TRIGGERED" = true ]; then
TITLE="⚠️ Sunucu Uyarısı Tetiklendi"
fi
# Payload oluştur ve gönder
curl -s -X POST "$DISCORD_WEBHOOK_URL"
-H "Content-Type: application/json"
-d "{
"embeds": [{
"title": "$TITLE",
"color": $ALERT_COLOR,
"fields": [
{"name": "${CPU_EMOJI} CPU Kullanımı", "value": "%${CPU_USAGE}", "inline": true},
{"name": "${MEM_EMOJI} RAM Kullanımı", "value": "%${MEM_PERCENT} (${MEM_USED}MB/${MEM_TOTAL}MB)", "inline": true},
{"name": "${DISK_EMOJI} Disk Kullanımı", "value": "${DISK_INFO}", "inline": true},
{"name": "⚙️ Load Average", "value": "${LOAD_AVG}", "inline": false}
],
"footer": {"text": "${HOSTNAME}"},
"timestamp": "${TIMESTAMP}"
}]
}"
echo "Bildirim gönderildi. Alert: $ALERT_TRIGGERED"
Python ile Daha Güçlü Webhook Entegrasyonu
Bash iş görür ama karmaşık senaryolarda Python çok daha rahat. Özellikle hata yönetimi, retry mekanizması ve birden fazla webhook’a yönlendirme gibi durumlarda Python tercih ediyorum.
#!/usr/bin/env python3
# /usr/local/lib/discord_notify.py
# Production'a hazır Discord webhook modülü
import json
import time
import urllib.request
import urllib.error
from datetime import datetime, timezone
class DiscordWebhook:
def __init__(self, webhook_url, max_retries=3, retry_delay=5):
self.webhook_url = webhook_url
self.max_retries = max_retries
self.retry_delay = retry_delay
def send(self, title, description, color=3066993, fields=None, mention_role=None):
"""
Discord'a embed mesaj gönderir.
color: 15158332 (kırmızı), 16776960 (sarı), 3066993 (yeşil)
"""
timestamp = datetime.now(timezone.utc).isoformat()
embed = {
"title": title,
"description": description,
"color": color,
"timestamp": timestamp,
"footer": {"text": "Monitoring System"}
}
if fields:
embed["fields"] = [
{"name": k, "value": str(v), "inline": True}
for k, v in fields.items()
]
payload = {"embeds": }
if mention_role:
payload["content"] = f"<@&{mention_role}>"
return self._post_with_retry(payload)
def send_critical(self, title, description, fields=None):
"""Kritik alertler için - @here mention ile kırmızı embed"""
payload = {
"content": "@here Kritik alarm!",
"embeds": [{
"title": f"🚨 {title}",
"description": description,
"color": 15158332,
"timestamp": datetime.now(timezone.utc).isoformat(),
"fields": [{"name": k, "value": str(v), "inline": True}
for k, v in (fields or {}).items()]
}]
}
return self._post_with_retry(payload)
def _post_with_retry(self, payload):
data = json.dumps(payload).encode('utf-8')
headers = {'Content-Type': 'application/json'}
for attempt in range(1, self.max_retries + 1):
try:
req = urllib.request.Request(
self.webhook_url,
data=data,
headers=headers,
method='POST'
)
with urllib.request.urlopen(req, timeout=10) as response:
return response.status == 204
except urllib.error.HTTPError as e:
if e.code == 429: # Rate limit
retry_after = int(e.headers.get('Retry-After', self.retry_delay))
print(f"Rate limit! {retry_after}s bekleniyor...")
time.sleep(retry_after)
elif attempt == self.max_retries:
print(f"Webhook başarısız (HTTP {e.code}): {e.reason}")
return False
else:
time.sleep(self.retry_delay)
except Exception as e:
if attempt == self.max_retries:
print(f"Webhook gönderilemedi: {e}")
return False
time.sleep(self.retry_delay)
return False
# Kullanım örneği
if __name__ == "__main__":
import configparser
config = configparser.ConfigParser()
config.read('/etc/monitoring/discord.conf')
webhook_url = config.get('DEFAULT', 'DISCORD_WEBHOOK_URL',
fallback='https://discord.com/api/webhooks/...')
notifier = DiscordWebhook(webhook_url)
notifier.send(
title="Backup Tamamlandı",
description="Gece yedeklemesi başarıyla tamamlandı.",
color=3066993,
fields={
"Boyut": "42.3 GB",
"Süre": "23 dakika",
"Hedef": "s3://backups/prod"
}
)
Systemd Service Başarısız Olduğunda Otomatik Bildirim
Bu senaryoyu çok seviyorum. Herhangi bir systemd servisi çöktüğünde otomatik Discord bildirimi almak için systemd’nin OnFailure özelliğini kullanıyoruz.
# /usr/local/bin/discord-systemd-notify.sh
# systemd OnFailure ile kullanım için
#!/bin/bash
source /etc/monitoring/discord.conf
UNIT_NAME="$1"
HOSTNAME=$(hostname -f)
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
# Servis loglarından son 10 satırı al
LAST_LOGS=$(journalctl -u "$UNIT_NAME" -n 10 --no-pager -o short 2>/dev/null |
sed 's/"/\"/g' | tr 'n' '|' | sed 's/|/\n/g')
curl -s -X POST "$DISCORD_WEBHOOK_URL"
-H "Content-Type: application/json"
-d "{
"embeds": [{
"title": "🔴 Servis Çöktü: ${UNIT_NAME}",
"description": "**${HOSTNAME}** üzerinde **${UNIT_NAME}** servisi başarısız oldu.",
"color": 15158332,
"fields": [
{"name": "Son Loglar", "value": "```${LAST_LOGS}```", "inline": false}
],
"timestamp": "${TIMESTAMP}"
}]
}"
Bu script’i systemd ile entegre etmek için özel bir servis dosyası oluşturuyoruz:
# /etc/systemd/system/[email protected]
# Bu dosyayı oluşturduktan sonra 'systemctl daemon-reload' çalıştırın
[Unit]
Description=Discord Failure Notification for %i
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/discord-systemd-notify.sh %i
Artık herhangi bir servis dosyasına şu satırı eklemeniz yeterli:
# İzlemek istediğiniz servisin .service dosyasına ekleyin
# Örnek: /etc/systemd/system/nginx.service veya override dosyası
[Unit]
OnFailure=discord-notify@%n.service
Override için daha temiz yaklaşım:
# nginx için override oluştur
mkdir -p /etc/systemd/system/nginx.service.d/
cat > /etc/systemd/system/nginx.service.d/discord-notify.conf << 'EOF'
[Unit]
OnFailure=discord-notify@%n.service
EOF
systemctl daemon-reload
Crontab ile Periyodik Raporlama
Sabah geldiğinizde geceye ait özet raporu Discord’da görmek güzel bir his. Bunun için basit bir cron job yeterli.
# /usr/local/bin/discord-daily-report.sh
#!/bin/bash
source /etc/monitoring/discord.conf
HOSTNAME=$(hostname -f)
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
REPORT_DATE=$(date '+%d/%m/%Y')
# Uptime
UPTIME=$(uptime -p)
# Disk kullanımları
DISK_REPORT=$(df -h --output=target,pcent,used,size | grep -v "Filesystem" |
awk '{printf "%s: %s (%s/%s)n", $1, $2, $3, $4}' |
head -5 | sed 's/"/\"/g' | tr 'n' '|' | sed 's/|/\n/g')
# Son 24 saatte başarısız login denemeleri
FAILED_LOGINS=$(grep "Failed password" /var/log/auth.log 2>/dev/null |
grep "$(date '+%b %e')" | wc -l)
# Çalışmayan servisler
FAILED_SERVICES=$(systemctl list-units --state=failed --no-legend --no-pager 2>/dev/null |
wc -l)
# Renk belirleme
COLOR=3066993
if [ "$FAILED_SERVICES" -gt 0 ] || [ "$FAILED_LOGINS" -gt 50 ]; then
COLOR=16776960 # Sarı - dikkat gerektiren durum
fi
curl -s -X POST "$DISCORD_WEBHOOK_URL"
-H "Content-Type: application/json"
-d "{
"embeds": [{
"title": "📊 Günlük Rapor - ${REPORT_DATE}",
"color": ${COLOR},
"fields": [
{"name": "🖥️ Sunucu", "value": "${HOSTNAME}", "inline": true},
{"name": "⏱️ Uptime", "value": "${UPTIME}", "inline": true},
{"name": "❌ Başarısız Servisler", "value": "${FAILED_SERVICES}", "inline": true},
{"name": "🔐 Başarısız Login (24s)", "value": "${FAILED_LOGINS}", "inline": true},
{"name": "💾 Disk Durumu", "value": "```${DISK_REPORT}```", "inline": false}
],
"timestamp": "${TIMESTAMP}"
}]
}"
Crontab’a eklemek için:
# Her sabah 08:00'de günlük rapor
0 8 * * * /usr/local/bin/discord-daily-report.sh >> /var/log/discord-notify.log 2>&1
# Her 5 dakikada bir kaynak kontrolü (sadece alert durumunda bildirim gönderir)
*/5 * * * * /usr/local/bin/discord-monitor.sh >> /var/log/discord-notify.log 2>&1
Güvenlik ve Best Practice’ler
Production’da webhook kullanırken dikkat etmeniz gereken birkaç kritik nokta var:
- Webhook URL’yi versiyonlama sistemine eklemeyin:
.gitignoredosyanıza webhook config dosyalarını ekleyin. Bir kez GitHub’a push edip sonra token’ı rotate etmek zorunda kalmayın.
- Config dosyası izinlerini düzgün ayarlayın: Daha önce belirttiğim gibi 600 izni ve root sahipliği şart. Script’leri root olmayan kullanıcıyla çalıştıracaksanız, o kullanıcıyı özel bir gruba alıp group izni 640 yapın.
- Rate limit’e takılmayın: Çok sayıda sunucudan aynı webhook’a yazıyorsanız, her sunucu için ayrı webhook veya merkezi bir notification proxy kullanın. Dakikada 30 mesaj limiti küçük ekipler için yeterli ama büyük infrastructure’da aşılabilir.
- Log tutun: Her webhook isteğinin sonucunu loglamak, sorun yaşandığında debugging için çok değerli.
- Alert fatigue’e dikkat edin: Her 5 dakikada bir aynı alarmı göndermeyin. Bir alert tetiklendikten sonra, aynı alarm için en az 30 dakika bekleyin. Bunun için basit bir lock dosyası mekanizması kurabilirsiniz.
# Lock dosyası ile alert deduplication
LOCK_DIR="/tmp/discord-alerts"
mkdir -p "$LOCK_DIR"
send_deduplicated_alert() {
local alert_key="$1"
local cooldown="${2:-1800}" # Varsayılan 30 dakika
local lock_file="$LOCK_DIR/${alert_key}.lock"
if [ -f "$lock_file" ]; then
local last_sent=$(cat "$lock_file")
local now=$(date +%s)
local diff=$((now - last_sent))
if [ "$diff" -lt "$cooldown" ]; then
echo "Alert '$alert_key' cooldown'da, atlanıyor ($diff/$cooldown saniye)"
return 0
fi
fi
# Alert gönder
# ... send_discord_alert çağrısı ...
date +%s > "$lock_file"
echo "Alert '$alert_key' gönderildi, cooldown başlatıldı."
}
Webhook’u Test Etme ve Sorun Giderme
Webhook’unuz çalışmıyorsa aşağıdaki kontrol listesini takip edin:
- HTTP 401: Webhook URL yanlış veya token geçersiz. Discord panelinden yeni bir URL kopyalayın.
- HTTP 404: Webhook silinmiş. Discord’dan yeni bir tane oluşturun.
- HTTP 429: Rate limit’e takıldınız.
Retry-Afterheader’ına bakın.
- HTTP 400: JSON payload bozuk. curl ile manuel test ederken JSON’ı önce bir validator’dan geçirin.
Hızlı debug için verbose curl:
# Verbose mod ile webhook testi
curl -v -X POST "https://discord.com/api/webhooks/YOUR_ID/YOUR_TOKEN"
-H "Content-Type: application/json"
-d '{"content": "Test mesajı", "embeds": [{"title": "Test", "color": 3066993, "description": "Bu bir test"}]}'
Response 204 dönüyorsa her şey yolunda. Discord, başarılı webhook isteklerinde body döndürmez, sadece 204 No Content.
Sonuç
Discord webhook entegrasyonu, monitoring altyapısına eklediğinizde ekibinizin sistem durumuna olan farkındalığı dramatik şekilde artıyor. Üç ay önce bu kurulumu yaptıktan sonra, ekibimde “sunucu ne zaman çökmüş ki?” diye sorulmaz oldu. Herkes telefona bakıyor, bildirim geliyor, beş dakika içinde müdahale ediliyor.
Başlangıç için önerdiğim yol haritası şu şekilde: önce basit curl tabanlı test scriptini çalıştırın, sonra systemd OnFailure entegrasyonunu kurun, ardından günlük raporu crontab’a ekleyin. Bu üçü bile sizi çok daha rahat bir noktaya taşır.
Webhook URL’lerinizi güvende tutun, rate limit’e dikkat edin, alert fatigue’i önlemek için cooldown mekanizması ekleyin. Bu üç kurala uyarsanız, aylar geçtikçe sizi hiç rahatsız etmeyen ama gerektiğinde hayat kurtaran bir bildirim sisteminiz olur.
Sorularınız varsa veya farklı entegrasyon senaryolarında takıldıysanız, yorum bölümünde yazın. Bir sonraki yazıda Grafana alerting’i de Discord’a bağlamayı planlıyorum.
