From aac640ae11ad719a8e3a3df84f45bffaf7c4b2bb Mon Sep 17 00:00:00 2001 From: Sven Eberth Date: Thu, 10 Aug 2023 02:48:34 +0200 Subject: [PATCH] feat: Implement a `PeriodicTask` to check the remaining SIB email quota --- src/viur/core/email.py | 59 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/src/viur/core/email.py b/src/viur/core/email.py index c3d995b67..bd0082f69 100644 --- a/src/viur/core/email.py +++ b/src/viur/core/email.py @@ -1,11 +1,16 @@ -import logging, os -import json, base64 -from urllib import request +import base64 +import json +import logging +import os from abc import ABC, abstractmethod -from typing import Any, Callable, Union, List, Dict +from typing import Any, Callable, Dict, List, Union +from urllib import request + +import requests + +from viur.core import db, utils from viur.core.config import conf -from viur.core import utils, db -from viur.core.tasks import CallDeferred, QueryIter, PeriodicTask, DeleteEntitiesIter +from viur.core.tasks import CallDeferred, DeleteEntitiesIter, PeriodicTask """ This module implements an email delivery system for ViUR. Emails will be queued so that we don't overwhelm @@ -375,3 +380,45 @@ def validateQueueEntity(entity: db.Entity): ext = attachment["filename"].split(".")[-1].lower() if ext not in EmailTransportSendInBlue.allowedExtensions: raise ValueError("The file-extension %s cannot be send using Send in Blue" % ext) + + @PeriodicTask(60 * 60) + @staticmethod + def check_sib_quota() -> None: + """Periodically checks the remaining SendInBlue email quota. + + This task does not have to be enabled. + It automatically checks if the apiKey is configured. + + There are three thresholds: 1000, 500, 100 + An email will be sent for the lowest threshold that has been undercut. + """ + if conf.get("viur.email.sendInBlue.apiKey") is None: + return # no SIB key, we cannot check + + req = requests.get( + "https://api.sendinblue.com/v3/account", + headers={"api-key": conf["viur.email.sendInBlue.apiKey"]}, + ) + if not req.ok: + logging.error("Failed to fetch SIB account information") + return + data = req.json() + logging.debug(f"SIB account data: {data}") + for plan in data["plan"]: + if plan["type"] == "payAsYouGo": + credits = plan["credits"] + break + else: + credits = -1 + + logging.info(f"SIB E-Mail credits: {credits}") + + for idx, limit in list(enumerate((1000, 500, 100), 1))[::-1]: + if credits < limit: + sendEMailToAdmins( + f"SendInBlue email budget for {conf['viur.instance.project_id']}: " + f"{credits} ({idx}. warning)", + f"The SendInBlue email budget reached {credits} credits " + f"for {data['email']}. Please increase soon.", + ) + break