74 lines
2.5 KiB
Python
74 lines
2.5 KiB
Python
# 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)
|