# Copyright (c) 2025 Patrick Motsch # All rights reserved. """ When the shared mandate pool (PREPAY_MANDATE) is exhausted, notify mandate admins. Uses the central notifyMandateAdmins() function for recipient resolution and delivery. Emails are throttled per mandate to avoid spam (one notification per cooldown window). """ from __future__ import annotations import logging import time from typing import Dict logger = logging.getLogger(__name__) # mandate_id -> unix timestamp of last pool-exhausted notification email _poolExhaustedEmailLastSent: Dict[str, float] = {} _DEFAULT_COOLDOWN_SEC = 3600 def maybeEmailMandatePoolExhausted( mandateId: str, triggeringUserId: str, triggeringUserLabel: str, currentBalance: float, requiredAmount: float, cooldownSec: float = _DEFAULT_COOLDOWN_SEC, ) -> None: """ Send one notification per mandate per cooldown window when the pool is exhausted. Args: mandateId: Mandate whose pool is empty. triggeringUserId: User who hit the block. triggeringUserLabel: Display (e.g. email or username). currentBalance: Pool balance (CHF). requiredAmount: Minimum required (CHF). cooldownSec: Minimum seconds between emails for this mandate. """ if not mandateId: return now = time.time() last = _poolExhaustedEmailLastSent.get(mandateId, 0.0) if last and (now - last) < cooldownSec: logger.debug( "Skip mandate pool exhausted email (cooldown): mandate=%s last=%.0fs ago", mandateId, now - last, ) return try: from modules.shared.notifyMandateAdmins import notifyMandateAdmins sent = notifyMandateAdmins( mandateId, "[PowerOn] Mandanten-Budget aufgebraucht", "Budget aufgebraucht", [ "Das gemeinsame Guthaben (Prepaid-Pool) für diesen Mandanten ist nicht mehr ausreichend.", f"Aktuelles Guthaben: CHF {currentBalance:.2f}\n" f"Benötigt (mindestens): CHF {requiredAmount:.2f}", f"Ausgelöst durch: {triggeringUserLabel}", "Bitte laden Sie das Mandats-Guthaben in der Billing-Verwaltung auf, " "damit Benutzer wieder AI-Funktionen nutzen können.", ], ) if sent > 0: _poolExhaustedEmailLastSent[mandateId] = now except Exception as e: logger.error("maybeEmailMandatePoolExhausted failed: %s", e, exc_info=True)