gitscope
← marzneshin / v0.5.2 / app/tasks/send_notifications.py
https://dev.kronoz.win/r/marzneshin.git
app/tasks/send_notifications.py · Python
Blame Raw
 1import logging
 2from datetime import datetime as dt
 3from datetime import timedelta as td
 4from typing import Any, Dict, List
 5
 6import aiohttp
 7from fastapi.encoders import jsonable_encoder
 8from requests import Session
 9
10from app import config
11from app.db import GetDB
12from app.db.models import NotificationReminder
13from app.utils.notification import queue
14
15logger = logging.getLogger(__name__)
16
17session = Session()
18
19headers = (
20    {"x-webhook-secret": config.WEBHOOK_SECRET}
21    if config.WEBHOOK_SECRET
22    else None
23)
24
25
26async def send(data: List[Dict[Any, Any]]) -> bool:
27    """Send the notification to the webhook address provided by config.WEBHOOK_ADDRESS
28
29    Args:
30        data (List[Dict[Any, Any]]): list of json encoded notifications
31
32    Returns:
33        bool: returns True if an ok response received
34    """
35    try:
36        logger.debug(
37            f"Sending {len(data)} webhook updates to {config.WEBHOOK_ADDRESS}"
38        )
39
40        r = await aiohttp.request(
41            "post", config.WEBHOOK_ADDRESS, json=data, headers=headers
42        )
43        if r.ok:
44            return True
45        logger.error(r)
46    except Exception as err:
47        logger.error(err)
48    return False
49
50
51async def send_notifications():
52    if not queue:
53        return
54
55    notifications_to_send = list()
56    try:
57        while notification := queue.popleft():
58            if notification.tries > config.NUMBER_OF_RECURRENT_NOTIFICATIONS:
59                continue
60            if notification.send_at > dt.utcnow().timestamp():
61                queue.append(
62                    notification
63                )  # add it to the queue again for the next check
64                continue
65            notifications_to_send.append(notification)
66    except IndexError:  # if the queue is empty
67        pass
68
69    if not notifications_to_send:
70        return
71    if not await send(
72        [jsonable_encoder(notif) for notif in notifications_to_send]
73    ):
74        for notification in notifications_to_send:
75            if (
76                notification.tries + 1
77            ) > config.NUMBER_OF_RECURRENT_NOTIFICATIONS:
78                continue
79            notification.tries += 1
80            notification.send_at = (
81                (  # schedule notification for n seconds later
82                    dt.utcnow()
83                    + td(seconds=config.RECURRENT_NOTIFICATIONS_TIMEOUT)
84                ).timestamp()
85            )
86            queue.append(notification)
87
88
89def delete_expired_reminders() -> None:
90    with GetDB() as db:
91        db.query(NotificationReminder).filter(
92            NotificationReminder.expires_at < dt.utcnow()
93        ).delete()
94        db.commit()