"""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