app/services/gmail_service.py:76: "No Google OAuth client configuration available. Import a client_secret.json in " ---ORDER--- # Re-check suppression at send time (recipient may have unsubscribed since enqueue) sup = (db.execute(select(SuppressionEntry) .where(SuppressionEntry.email == cr.email))).scalar_one_or_none() if sup: cr.status = "suppressed" campaign.suppressed_count += 1 cr.last_error = "Suppressed at send time" db.add(EmailEvent(campaign_id=campaign.id, campaign_recipient_id=cr.id, event_type="suppressed", detail="Suppressed at send time")) db.commit() _publish_progress(campaign.id) return {"ok": True, "status": "suppressed", "reason": "suppression hit"} account = db.get(GmailAccount, cr.assigned_account_id) if cr.assigned_account_id else None if account is None or not account.is_active or account.status != "authorized": cr.status = "skipped" campaign.skipped_count += 1 cr.last_error = "Assigned account unavailable/unauthorized" db.commit() _publish_progress(campaign.id) return {"ok": False, "status": "skipped", "reason": "account unavailable"} # Per-account daily cap (never exceed provider limits) today = now().date() if account.last_send_date and account.last_send_date.date() == today \ and account.sends_today >= account.daily_limit: campaign.status = "paused" db.commit() _publish_control(campaign.id, "pause", f"Account {account.email} reached daily limit ({account.daily_limit})") return {"ok": False, "status": "paused", "reason": "daily limit reached"} if account.cooldown_until and account.cooldown_until > now(): _reschedule(db, self, campaign.id, cr, job, account.cooldown_until - now(), "account cooling down") return {"ok": False, "status": "rescheduled", "reason": "cooldown"}