76 lines
2.1 KiB
Python
76 lines
2.1 KiB
Python
"""Envoi des notifications Web Push."""
|
|
import asyncio
|
|
import base64
|
|
import json
|
|
from tempfile import NamedTemporaryFile
|
|
from typing import Iterable
|
|
|
|
from pywebpush import WebPushException, webpush
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.core.config import settings
|
|
from app.models.push_subscription import PushSubscription
|
|
|
|
|
|
def notifications_configured() -> bool:
|
|
return bool(settings.VAPID_PUBLIC_KEY and settings.VAPID_PRIVATE_KEY_B64)
|
|
|
|
|
|
def _private_key_pem() -> str:
|
|
if not settings.VAPID_PRIVATE_KEY_B64:
|
|
return ""
|
|
return base64.b64decode(settings.VAPID_PRIVATE_KEY_B64).decode()
|
|
|
|
|
|
def _send_sync(abonnement: dict, payload: dict) -> None:
|
|
private_key = _private_key_pem()
|
|
with NamedTemporaryFile("w", suffix=".pem") as key_file:
|
|
key_file.write(private_key)
|
|
key_file.flush()
|
|
webpush(
|
|
subscription_info=abonnement,
|
|
data=json.dumps(payload, ensure_ascii=False),
|
|
vapid_private_key=key_file.name,
|
|
vapid_claims={"sub": settings.VAPID_SUBJECT},
|
|
)
|
|
|
|
|
|
async def send_to_subscriptions(db: AsyncSession, subscriptions: Iterable[PushSubscription], payload: dict) -> int:
|
|
if not notifications_configured():
|
|
return 0
|
|
sent = 0
|
|
for sub in subscriptions:
|
|
try:
|
|
await asyncio.to_thread(_send_sync, sub.abonnement, payload)
|
|
sent += 1
|
|
except WebPushException as exc:
|
|
if getattr(exc.response, "status_code", None) in {404, 410}:
|
|
await db.delete(sub)
|
|
except Exception:
|
|
continue
|
|
return sent
|
|
|
|
|
|
async def notify_group_change(
|
|
db: AsyncSession,
|
|
*,
|
|
groupe_id,
|
|
actor_id,
|
|
title: str,
|
|
body: str,
|
|
url: str = "/",
|
|
) -> int:
|
|
if not notifications_configured() or not groupe_id:
|
|
return 0
|
|
result = await db.execute(
|
|
select(PushSubscription).where(
|
|
PushSubscription.groupe_id == groupe_id,
|
|
PushSubscription.membre_id != actor_id,
|
|
)
|
|
)
|
|
return await send_to_subscriptions(db, result.scalars().all(), {
|
|
"title": title,
|
|
"body": body,
|
|
"url": url,
|
|
})
|