#!/bin/bash # Daily warmup IP-reputation check. Sends a Telegram alert ONLY if there is a # reputation problem (silent on healthy days). Creds from /opt/performancewest/.env. set -uo pipefail LOG=/var/log/mail.log TODAY=$(date '+%b %d') REPORT=/var/log/pw-warmup-healthcheck.log MIN_DELIVERY=65 MAX_SPAMBLOCK=150 MIN_SENT=50 BOT=$(grep -E '^TELEGRAM_BOT_TOKEN=' /opt/performancewest/.env | head -1 | cut -d= -f2-) CHAT=$(grep -E '^TELEGRAM_CHAT_ID=' /opt/performancewest/.env | head -1 | cut -d= -f2-) tg() { [ -n "$BOT" ] && [ -n "$CHAT" ] || return 0 curl -s --max-time 10 "https://api.telegram.org/bot${BOT}/sendMessage" --data-urlencode "chat_id=${CHAT}" --data-urlencode "text=$1" >/dev/null 2>&1 } mlog() { sudo grep -E "$1" "$LOG" 2>/dev/null | grep "^$TODAY"; } EXT='to=<[^>]*@(performancewest|carrierone|perfwest)' NOISE='probe|example.org|rt-252' MSENT=$(mlog 'out0[5-9]/smtp' | grep 'status=sent' | grep -vE "$EXT" | grep -viE "$NOISE" | wc -l) MBOUNCE=$(mlog 'out0[5-9]/smtp' | grep 'status=bounced' | grep -vE "$EXT" | grep -viE "$NOISE" | wc -l) MSPAM=$(mlog 'out0[5-9]/smtp' | grep 'status=bounced' | grep -c '550-5.7.1') MTOTAL=$((MSENT + MBOUNCE)); MDEL=0 [ "$MTOTAL" -gt 0 ] && MDEL=$(python3 -c "print(round(100*$MSENT/$MTOTAL))") HSENT=$(mlog 'hcout[0-9]/smtp' | grep -c 'status=sent') HBOUNCE=$(mlog 'hcout[0-9]/smtp' | grep -c 'status=bounced') HSPAM=$(mlog 'hcout[0-9]/smtp' | grep 'status=bounced' | grep -c '550-5.7.1') HTOTAL=$((HSENT + HBOUNCE)); HDEL=0 [ "$HTOTAL" -gt 0 ] && HDEL=$(python3 -c "print(round(100*$HSENT/$HTOTAL))") # IP rehab pool (.91-.93 / rehab02-04) — recovering after the May 30-31 blast. # A recovering IP naturally bounces more on a cold list, so the rehab bounce # threshold is lenient; we only flag if it is alarmingly high (a sign the rehab # recipient quality regressed) or if a rehab IP lands on a DNSBL. RSENT=$(mlog 'rehab0[234]/smtp' | grep -c 'status=sent') RBOUNCE=$(mlog 'rehab0[234]/smtp' | grep -c 'status=bounced') RTOTAL=$((RSENT + RBOUNCE)); RDEL=0 [ "$RTOTAL" -gt 0 ] && RDEL=$(python3 -c "print(round(100*$RSENT/$RTOTAL))") # DNSBL check for the rehab IPs (reuse Spamhaus ZEN — the one that matters most). RBL='' for ip in 91 92 93; do hit=$(dig +short +time=3 +tries=1 ${ip}.124.174.207.zen.spamhaus.org 2>/dev/null | head -1) [ -n "$hit" ] && RBL="${RBL}.${ip} on Spamhaus ZEN ($hit); " done PROBLEMS='' if [ "$MSENT" -ge "$MIN_SENT" ]; then [ "$MDEL" -lt "$MIN_DELIVERY" ] && PROBLEMS="${PROBLEMS}- Main pool delivery ${MDEL}% (below ${MIN_DELIVERY}%)\n" [ "$MSPAM" -gt "$MAX_SPAMBLOCK" ] && PROBLEMS="${PROBLEMS}- Main pool spam/policy blocks: ${MSPAM}\n" fi if [ "$HSENT" -ge "$MIN_SENT" ]; then [ "$HDEL" -lt "$MIN_DELIVERY" ] && PROBLEMS="${PROBLEMS}- HC stream delivery ${HDEL}%\n" [ "$HSPAM" -gt "$MAX_SPAMBLOCK" ] && PROBLEMS="${PROBLEMS}- HC stream spam/policy blocks: ${HSPAM}\n" fi # Rehab problems: DNSBL listing is always a problem; bounce >60% with real # volume means the recipient quality slipped (rehab should be on clean domains). [ -n "$RBL" ] && PROBLEMS="${PROBLEMS}- IP rehab DNSBL: ${RBL}\n" if [ "$RSENT" -ge 10 ] && [ "$RDEL" -lt 40 ]; then PROBLEMS="${PROBLEMS}- IP rehab delivery ${RDEL}% (recipient quality slipped)\n" fi { echo "==== TG WARMUP CHECK $(date) ====" echo "MAIN: sent=$MSENT bounced=$MBOUNCE delivery=${MDEL}% spamblock=$MSPAM" echo "HC: sent=$HSENT bounced=$HBOUNCE delivery=${HDEL}% spamblock=$HSPAM" echo "REHAB(.91-.93): sent=$RSENT bounced=$RBOUNCE delivery=${RDEL}% dnsbl=${RBL:-clean}" echo "problems: ${PROBLEMS:-none}" } >> "$REPORT" 2>&1 if [ -n "$PROBLEMS" ]; then MSG=$(printf '⚠️ Performance West IP reputation alert (%s)\n\nMain pool: %d%% delivery, %d sent, %d bounced, %d spam-blocks\nHC stream: %d%% delivery, %d sent, %d spam-blocks\nRehab (.91-.93): %d%% delivery, %d sent, %d bounced (dnsbl: %s)\n\nIssues:\n%b' "$TODAY" "$MDEL" "$MSENT" "$MBOUNCE" "$MSPAM" "$HDEL" "$HSENT" "$HSPAM" "$RDEL" "$RSENT" "$RBOUNCE" "${RBL:-clean}" "$PROBLEMS") tg "$MSG" fi