mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	push_notification: Add support to configure priority and push_type.
This commit adds support to let server configure: * fcm_priority * apns_priority * apns_push_type while sending E2EE push notifications. The values of these fields will vary depending on whether the send request is to send push notification for a message or revoke an already sent notification. Since, the bouncer receives encrypted data so it can't inspect the payload to determine whether it is a removal request or not, hence can't configure priority on its own. The server needs to specify explicitly. We're not simply sending a single 'is_removal' flag because allowing the server to configure them separately will help in future to support other types of notifications with a different combination of priority and push_type, like whose aim is to notify user about information other than a new message or removal request. Fixes part of #35368.
This commit is contained in:
		
				
					committed by
					
						
						Tim Abbott
					
				
			
			
				
	
			
			
			
						parent
						
							945f27f099
						
					
				
				
					commit
					29ea8a07c2
				
			@@ -8,11 +8,11 @@ from collections.abc import Iterable, Mapping, Sequence
 | 
			
		||||
from dataclasses import dataclass
 | 
			
		||||
from email.headerregistry import Address
 | 
			
		||||
from functools import cache
 | 
			
		||||
from typing import TYPE_CHECKING, Any, Literal, Optional, TypeAlias, Union
 | 
			
		||||
from typing import TYPE_CHECKING, Any, Final, Literal, Optional, TypeAlias, Union
 | 
			
		||||
 | 
			
		||||
import lxml.html
 | 
			
		||||
import orjson
 | 
			
		||||
from aioapns.common import NotificationResult
 | 
			
		||||
from aioapns.common import NotificationResult, PushType
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from django.db import transaction
 | 
			
		||||
from django.db.models import F, Q
 | 
			
		||||
@@ -1451,18 +1451,33 @@ def send_push_notifications(
 | 
			
		||||
        assert push_device.bouncer_device_id is not None  # for mypy
 | 
			
		||||
        device_id_to_encrypted_data[str(push_device.bouncer_device_id)] = encrypted_data
 | 
			
		||||
 | 
			
		||||
    # TODO: These literals will vary when implementing notification removal.
 | 
			
		||||
    #
 | 
			
		||||
    # Note: The "Final" qualifier serves as a shorthand
 | 
			
		||||
    # for declaring that a variable is effectively Literal.
 | 
			
		||||
    fcm_priority: Final = "high"
 | 
			
		||||
    apns_priority: Final = 10
 | 
			
		||||
    apns_push_type = PushType.ALERT
 | 
			
		||||
 | 
			
		||||
    # Send push notification
 | 
			
		||||
    try:
 | 
			
		||||
        if settings.ZILENCER_ENABLED:
 | 
			
		||||
            from zilencer.lib.push_notifications import send_e2ee_push_notifications
 | 
			
		||||
 | 
			
		||||
            response_data: SendNotificationResponseData = send_e2ee_push_notifications(
 | 
			
		||||
                device_id_to_encrypted_data, realm=user_profile.realm
 | 
			
		||||
                device_id_to_encrypted_data,
 | 
			
		||||
                fcm_priority=fcm_priority,
 | 
			
		||||
                apns_priority=apns_priority,
 | 
			
		||||
                apns_push_type=apns_push_type,
 | 
			
		||||
                realm=user_profile.realm,
 | 
			
		||||
            )
 | 
			
		||||
        else:
 | 
			
		||||
            post_data = {
 | 
			
		||||
                "realm_uuid": str(user_profile.realm.uuid),
 | 
			
		||||
                "device_id_to_encrypted_data": device_id_to_encrypted_data,
 | 
			
		||||
                "fcm_priority": fcm_priority,
 | 
			
		||||
                "apns_priority": apns_priority,
 | 
			
		||||
                "apns_push_type": apns_push_type,
 | 
			
		||||
            }
 | 
			
		||||
            result = send_json_to_push_bouncer("POST", "push/e2ee/notify", post_data)
 | 
			
		||||
            assert isinstance(result["android_successfully_sent_count"], int)  # for mypy
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,9 @@
 | 
			
		||||
import asyncio
 | 
			
		||||
import logging
 | 
			
		||||
from collections.abc import Iterable
 | 
			
		||||
from typing import Literal, TypeAlias
 | 
			
		||||
 | 
			
		||||
from aioapns import NotificationRequest
 | 
			
		||||
from aioapns import NotificationRequest, PushType
 | 
			
		||||
from django.utils.timezone import now as timezone_now
 | 
			
		||||
from firebase_admin import exceptions as firebase_exceptions
 | 
			
		||||
from firebase_admin import messaging as firebase_messaging
 | 
			
		||||
@@ -19,6 +20,9 @@ from zilencer.models import RemotePushDevice, RemoteRealm
 | 
			
		||||
 | 
			
		||||
logger = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
FCMPriority: TypeAlias = Literal["high", "normal"]
 | 
			
		||||
APNsPriority: TypeAlias = Literal[10, 5, 1]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def send_e2ee_push_notification_apple(
 | 
			
		||||
    apns_requests: list[NotificationRequest],
 | 
			
		||||
@@ -117,6 +121,9 @@ def send_e2ee_push_notification_android(
 | 
			
		||||
def send_e2ee_push_notifications(
 | 
			
		||||
    device_id_to_encrypted_data: dict[str, str],
 | 
			
		||||
    *,
 | 
			
		||||
    fcm_priority: FCMPriority,
 | 
			
		||||
    apns_priority: APNsPriority,
 | 
			
		||||
    apns_push_type: PushType,
 | 
			
		||||
    realm: Realm | None = None,
 | 
			
		||||
    remote_realm: RemoteRealm | None = None,
 | 
			
		||||
) -> SendNotificationResponseData:
 | 
			
		||||
@@ -147,18 +154,12 @@ def send_e2ee_push_notifications(
 | 
			
		||||
            "alert": {
 | 
			
		||||
                "title": "New notification",
 | 
			
		||||
            },
 | 
			
		||||
            # TODO: Should we remove `sound` and let the clients add it.
 | 
			
		||||
            # Then we can rename it as `apns_required_message_payload`.
 | 
			
		||||
            "sound": "default",
 | 
			
		||||
        },
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fcm_requests = []
 | 
			
		||||
    fcm_remote_push_devices: list[RemotePushDevice] = []
 | 
			
		||||
 | 
			
		||||
    # TODO: "normal" if remove event.
 | 
			
		||||
    priority = "high"
 | 
			
		||||
 | 
			
		||||
    for remote_push_device in remote_push_devices:
 | 
			
		||||
        message_payload = {
 | 
			
		||||
            "encrypted_data": device_id_to_encrypted_data[str(remote_push_device.device_id)],
 | 
			
		||||
@@ -174,8 +175,8 @@ def send_e2ee_push_notifications(
 | 
			
		||||
                    apns_topic=remote_push_device.ios_app_id,
 | 
			
		||||
                    device_token=remote_push_device.token,
 | 
			
		||||
                    message=apns_message_payload,
 | 
			
		||||
                    time_to_live=24 * 3600,
 | 
			
		||||
                    # TODO: priority
 | 
			
		||||
                    priority=apns_priority,
 | 
			
		||||
                    push_type=apns_push_type,
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
            apns_remote_push_devices.append(remote_push_device)
 | 
			
		||||
@@ -184,7 +185,7 @@ def send_e2ee_push_notifications(
 | 
			
		||||
                firebase_messaging.Message(
 | 
			
		||||
                    data=message_payload,
 | 
			
		||||
                    token=remote_push_device.token,
 | 
			
		||||
                    android=firebase_messaging.AndroidConfig(priority=priority),
 | 
			
		||||
                    android=firebase_messaging.AndroidConfig(priority=fcm_priority),
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
            fcm_remote_push_devices.append(remote_push_device)
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ from uuid import UUID
 | 
			
		||||
 | 
			
		||||
import orjson
 | 
			
		||||
import requests.exceptions
 | 
			
		||||
from aioapns import PushType
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from django.core.exceptions import ValidationError
 | 
			
		||||
from django.core.validators import URLValidator, validate_email
 | 
			
		||||
@@ -92,7 +93,7 @@ from zilencer.auth import (
 | 
			
		||||
    generate_registration_transfer_verification_secret,
 | 
			
		||||
    validate_registration_transfer_verification_secret,
 | 
			
		||||
)
 | 
			
		||||
from zilencer.lib.push_notifications import send_e2ee_push_notifications
 | 
			
		||||
from zilencer.lib.push_notifications import APNsPriority, FCMPriority, send_e2ee_push_notifications
 | 
			
		||||
from zilencer.lib.remote_counts import MissingDataError
 | 
			
		||||
from zilencer.models import (
 | 
			
		||||
    RemoteInstallationCount,
 | 
			
		||||
@@ -1807,6 +1808,9 @@ def remote_server_check_analytics(request: HttpRequest, server: RemoteZulipServe
 | 
			
		||||
class SendE2EEPushNotificationPayload(BaseModel):
 | 
			
		||||
    realm_uuid: str
 | 
			
		||||
    device_id_to_encrypted_data: dict[str, str]
 | 
			
		||||
    fcm_priority: FCMPriority
 | 
			
		||||
    apns_priority: APNsPriority
 | 
			
		||||
    apns_push_type: PushType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@typed_endpoint
 | 
			
		||||
@@ -1844,7 +1848,11 @@ def remote_server_send_e2ee_push_notification(
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    response_data = send_e2ee_push_notifications(
 | 
			
		||||
        device_id_to_encrypted_data, remote_realm=remote_realm
 | 
			
		||||
        device_id_to_encrypted_data,
 | 
			
		||||
        fcm_priority=payload.fcm_priority,
 | 
			
		||||
        apns_priority=payload.apns_priority,
 | 
			
		||||
        apns_push_type=payload.apns_push_type,
 | 
			
		||||
        remote_realm=remote_realm,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    do_increment_logging_stat(
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user