diff --git a/zerver/lib/push_notifications.py b/zerver/lib/push_notifications.py index 11f9a153a7..ffedad23dc 100644 --- a/zerver/lib/push_notifications.py +++ b/zerver/lib/push_notifications.py @@ -241,6 +241,20 @@ def modernize_apns_payload(data: Mapping[str, Any]) -> Mapping[str, Any]: APNS_MAX_RETRIES = 3 +def dedupe_device_tokens( + devices: Sequence[DeviceToken], +) -> Sequence[DeviceToken]: + device_tokens: set[str] = set() + result: list[DeviceToken] = [] + for device in devices: + lower_token = device.token.lower() + if lower_token in device_tokens: # nocoverage + continue + device_tokens.add(lower_token) + result.append(device) + return result + + def send_apple_push_notification( user_identity: UserPushIdentityCompat, devices: Sequence[DeviceToken], @@ -270,18 +284,24 @@ def send_apple_push_notification( else: DeviceTokenClass = PushDeviceToken + orig_devices = devices + devices = dedupe_device_tokens(devices) + num_duplicate_tokens = len(orig_devices) - len(devices) + if remote: logger.info( - "APNs: Sending notification for remote user %s:%s to %d devices", + "APNs: Sending notification for remote user %s:%s to %d devices (skipped %d duplicates)", remote.uuid, user_identity, len(devices), + num_duplicate_tokens, ) else: logger.info( - "APNs: Sending notification for local user %s to %d devices", + "APNs: Sending notification for local user %s to %d devices (skipped %d duplicates)", user_identity, len(devices), + num_duplicate_tokens, ) payload_data = dict(modernize_apns_payload(payload_data)) message = {**payload_data.pop("custom", {}), "aps": payload_data}