mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	notifications: Dedupe APNs tokens case-insensitively.
Fixes zulip/zulip-flutter#1617. It turns out that an APNs token (which is a hex string) is equally valid in lower or upper case. The old app would send the server the lower-case form of the token, but the new app sends the upper-case form. Because we've been treating tokens case-sensitively, if the user upgrades from the old app to the new, that results in the server and bouncer each having two copies of the token (one lower-case and one upper-case), and therefore sending that device two copies of each notification: zulip/zulip-flutter#1617. To fix that immediately, have the bouncer drop duplicate tokens before sending the notifications to APNs. Work is also in progress on fixing this in a better-structured way, by having the database correctly treat tokens as the same when they differ only in case.
This commit is contained in:
		@@ -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}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user