===
7:  suppressed / skipped -> dropped pre-send
46:def classify_error(msg: str) -> str:
57:def now():
62:    def on_failure(self, exc, task_id, args, kwargs, einfo):
68:def send_one(self, campaign_id: int, campaign_recipient_id: int):
80:        if cr.status in ("sent", "suppressed", "perm_failed"):
87:        # Re-check suppression at send time (recipient may have unsubscribed since enqueue)
91:            cr.status = "suppressed"
92:            campaign.suppressed_count += 1
95:                              event_type="suppressed", detail="Suppressed at send time"))
98:            return {"ok": True, "status": "suppressed", "reason": "suppression hit"}
291:def _recipient_dict(r) -> dict:
297:def _plain_fallback(html: str) -> str:
304:def _unsubscribe_url(recipient, campaign) -> str:
311:def _sanitize_response(resp) -> str:
318:def _rate_window(key: str, interval: float):
340:def _reschedule(db, task, campaign_id, cr, job, delay: timedelta, reason: str):
351:def _handle_send_error(db, task, campaign, cr, job, account, attempt, error: str):
407:def _push_dlq(campaign_id, cr_id, error):
416:def _publish_control(campaign_id, action, reason=""):
425:def _publish_progress(campaign_id):
