2026-09-07 19:43:48 +03:00

79 lines
2.8 KiB
Python

"""Invisible Web Push commands that keep browser notifications in sync."""
import json
import logging
from push_notifications.models import WebPushDevice
from notifications.models import Notification
from notifications.senders.push import PushNotificationSender
logger = logging.getLogger(__name__)
class PushControlSender:
"""Send lifecycle commands to every active installation of one user."""
def send(
self,
*,
user_id: int,
action: str,
scope: dict,
replacement_notification_id: int | None = None,
) -> bool:
payload = {
"type": "notification_control",
"action": action,
"scope": scope,
}
if replacement_notification_id is not None:
notification = Notification.objects.filter(
id=replacement_notification_id,
recipient_id=user_id,
).first()
if notification is None:
return False
payload["notification"] = PushNotificationSender().build_message_data(
notification
)
message = json.dumps(payload)
delivered = False
push_sender = PushNotificationSender()
for device in WebPushDevice.objects.filter(user_id=user_id, active=True):
try:
result = device.send_message(message)
if not push_sender._push_result_succeeded(result):
device.refresh_from_db(fields=["active"])
if not device.active:
push_sender._deactivate_device(
device,
"provider rejected control command",
)
logger.warning(
"Push provider rejected control command",
extra={
"event": "notification.push.control_rejected",
"user_id": user_id,
"device_id": device.id,
"action": action,
},
)
continue
delivered = True
except Exception as error: # noqa: BLE001 - push clients raise vendor exceptions
logger.warning(
"Push control delivery failed",
extra={
"event": "notification.push.control_failed",
"user_id": user_id,
"device_id": device.id,
"action": action,
"error_type": type(error).__name__,
},
)
if push_sender._is_permanent_device_error(error):
push_sender._deactivate_device(device, str(error))
return delivered