fix(bounce-watchers): QID regex broke on out05-09/hcout transports

Both Postfix bounce watchers extracted the queue-ID with regex
postfix/[a-z]*[ (main) and postfix/[a-z0-9]*[ (hc), which only matched simple
transports like qmgr/cleanup. When the MTA started rotating sends through the
numbered warmup transports (out05-09/smtp, hcout1-3/smtp -- a transport/subprocess
name WITH a slash), the QID extraction returned empty, so status=bounced lines
never matched a tracked campaign QID and NO bounces were posted to listmonk since
~May 31. Result: dead mailboxes never got blocklisted and kept being retried,
dragging the warmup bounce rate.

Fix: regex postfix/[^[]*[ matches any transport/subprocess name up to the [pid].
Verified live on both watchers (out07/smtp and hcout1/smtp test bounces now
detected + posted).

Separately (server-side, not in repo): listmonk bounce.actions was null (no
auto-action), so even posted bounces did nothing. Set hard=1->blocklist,
complaint=1->blocklist; verified a real bounce now records + blocklists.
This commit is contained in:
justin 2026-06-08 15:02:32 -05:00
parent b973c6c132
commit 09b32d6e75
2 changed files with 10 additions and 10 deletions

View file

@ -25,7 +25,7 @@ tail -F "$LOG" 2>/dev/null | while IFS= read -r line; do
# Track queue IDs originating from campaign sender
if echo "$line" | grep -q "from=<noreply@performancewest.net>"; then
QID=$(echo "$line" | sed -n 's/.*postfix\/[a-z]*\[\([0-9]*\)\]: \([A-Z0-9]*\):.*/\2/p')
QID=$(echo "$line" | sed -n 's/.*postfix\/[^[]*\[\([0-9]*\)\]: \([A-Z0-9]*\):.*/\2/p')
if [ -n "$QID" ]; then
CAMPAIGN_QIDS[$QID]=1
fi
@ -33,7 +33,7 @@ tail -F "$LOG" 2>/dev/null | while IFS= read -r line; do
# Also track from=<info@performancewest.net>
if echo "$line" | grep -q "from=<info@performancewest.net>"; then
QID=$(echo "$line" | sed -n 's/.*postfix\/[a-z]*\[\([0-9]*\)\]: \([A-Z0-9]*\):.*/\2/p')
QID=$(echo "$line" | sed -n 's/.*postfix\/[^[]*\[\([0-9]*\)\]: \([A-Z0-9]*\):.*/\2/p')
if [ -n "$QID" ]; then
CAMPAIGN_QIDS[$QID]=1
fi
@ -41,7 +41,7 @@ tail -F "$LOG" 2>/dev/null | while IFS= read -r line; do
# Hard bounce: status=bounced
if echo "$line" | grep -q "status=bounced"; then
QID=$(echo "$line" | sed -n 's/.*postfix\/[a-z]*\[\([0-9]*\)\]: \([A-Z0-9]*\):.*/\2/p')
QID=$(echo "$line" | sed -n 's/.*postfix\/[^[]*\[\([0-9]*\)\]: \([A-Z0-9]*\):.*/\2/p')
RCPT=$(echo "$line" | sed -n 's/.*to=<\([^>]*\)>.*/\1/p')
# Only report if this was a campaign email
@ -65,7 +65,7 @@ tail -F "$LOG" 2>/dev/null | while IFS= read -r line; do
# Soft bounce with permanent 5xx in the response
if echo "$line" | grep -q "status=deferred" && echo "$line" | grep -qE "said: 5[0-9][0-9]"; then
QID=$(echo "$line" | sed -n 's/.*postfix\/[a-z]*\[\([0-9]*\)\]: \([A-Z0-9]*\):.*/\2/p')
QID=$(echo "$line" | sed -n 's/.*postfix\/[^[]*\[\([0-9]*\)\]: \([A-Z0-9]*\):.*/\2/p')
RCPT=$(echo "$line" | sed -n 's/.*to=<\([^>]*\)>.*/\1/p')
if [ -n "$QID" ] && [ -n "$RCPT" ] && [ -n "${CAMPAIGN_QIDS[$QID]+x}" ]; then
@ -86,7 +86,7 @@ tail -F "$LOG" 2>/dev/null | while IFS= read -r line; do
# Clean up old QIDs when they're removed from queue
if echo "$line" | grep -q "removed$"; then
QID=$(echo "$line" | sed -n 's/.*postfix\/[a-z]*\[\([0-9]*\)\]: \([A-Z0-9]*\):.*/\2/p')
QID=$(echo "$line" | sed -n 's/.*postfix\/[^[]*\[\([0-9]*\)\]: \([A-Z0-9]*\):.*/\2/p')
if [ -n "$QID" ]; then
unset CAMPAIGN_QIDS[$QID] 2>/dev/null
fi