def handle_push_notification(user_profile_id: int, missed_message: Dict[str, Any]) -> None: """ missed_message is the event received by the zerver.worker.queue_processors.PushNotificationWorker.consume function. """ user_profile = get_user_profile_by_id(user_profile_id) if not (receives_offline_push_notifications(user_profile) or receives_online_notifications(user_profile)): return user_profile = get_user_profile_by_id(user_profile_id) (message, user_message) = access_message(user_profile, missed_message['message_id']) if user_message is not None: # If ther user has read the message already, don't push-notify. # # TODO: It feels like this is already handled when things are # put in the queue; maybe we should centralize this logic with # the `zerver/tornado/event_queue.py` logic? if user_message.flags.read: return # Otherwise, we mark the message as having an active mobile # push notification, so that we can send revocation messages # later. user_message.flags.active_mobile_push_notification = True user_message.save(update_fields=["flags"]) else: # Users should only be getting push notifications into this # queue for messages they haven't received if they're # long-term idle; anything else is likely a bug. if not user_profile.long_term_idle: logging.error("Could not find UserMessage with message_id %s and user_id %s" % ( missed_message['message_id'], user_profile_id)) return message.trigger = missed_message['trigger'] message.stream_name = missed_message.get('stream_name', None) apns_payload = get_apns_payload(message) gcm_payload = get_gcm_payload(user_profile, message) logging.info("Sending push notification to user %s" % (user_profile_id,)) if uses_notification_bouncer(): try: send_notifications_to_bouncer(user_profile_id, apns_payload, gcm_payload) except requests.ConnectionError: def failure_processor(event: Dict[str, Any]) -> None: logging.warning( "Maximum retries exceeded for trigger:%s event:push_notification" % ( event['user_profile_id'])) retry_event('missedmessage_mobile_notifications', missed_message, failure_processor) return android_devices = list(PushDeviceToken.objects.filter(user=user_profile, kind=PushDeviceToken.GCM)) apple_devices = list(PushDeviceToken.objects.filter(user=user_profile, kind=PushDeviceToken.APNS)) if apple_devices: send_apple_push_notification(user_profile.id, apple_devices, apns_payload) if android_devices: send_android_push_notification(android_devices, gcm_payload)
def handle_push_notification(user_profile_id: int, missed_message: Dict[str, Any]) -> None: """ missed_message is the event received by the zerver.worker.queue_processors.PushNotificationWorker.consume function. """ if not push_notifications_enabled(): return user_profile = get_user_profile_by_id(user_profile_id) if not (receives_offline_push_notifications(user_profile) or receives_online_notifications(user_profile)): return user_profile = get_user_profile_by_id(user_profile_id) try: (message, user_message) = access_message(user_profile, missed_message['message_id']) except JsonableError: if ArchivedMessage.objects.filter(id=missed_message['message_id']).exists(): # If the cause is a race with the message being deleted, # that's normal and we have no need to log an error. return logging.error("Unexpected message access failure handling push notifications: %s %s" % ( user_profile.id, missed_message['message_id'])) return if user_message is not None: # If the user has read the message already, don't push-notify. # # TODO: It feels like this is already handled when things are # put in the queue; maybe we should centralize this logic with # the `zerver/tornado/event_queue.py` logic? if user_message.flags.read: return # Otherwise, we mark the message as having an active mobile # push notification, so that we can send revocation messages # later. user_message.flags.active_mobile_push_notification = True user_message.save(update_fields=["flags"]) else: # Users should only be getting push notifications into this # queue for messages they haven't received if they're # long-term idle; anything else is likely a bug. if not user_profile.long_term_idle: logger.error("Could not find UserMessage with message_id %s and user_id %s" % ( missed_message['message_id'], user_profile_id)) return message.trigger = missed_message['trigger'] apns_payload = get_apns_payload(user_profile, message) gcm_payload = get_gcm_payload(user_profile, message) gcm_options = {'priority': 'high'} # type: Dict[str, Any] logger.info("Sending push notifications to mobile clients for user %s" % (user_profile_id,)) if uses_notification_bouncer(): try: send_notifications_to_bouncer(user_profile_id, apns_payload, gcm_payload, gcm_options) except requests.ConnectionError: def failure_processor(event: Dict[str, Any]) -> None: logger.warning( "Maximum retries exceeded for trigger:%s event:push_notification" % ( event['user_profile_id'])) retry_event('missedmessage_mobile_notifications', missed_message, failure_processor) return android_devices = list(PushDeviceToken.objects.filter(user=user_profile, kind=PushDeviceToken.GCM)) apple_devices = list(PushDeviceToken.objects.filter(user=user_profile, kind=PushDeviceToken.APNS)) if apple_devices: send_apple_push_notification(user_profile.id, apple_devices, apns_payload) if android_devices: send_android_push_notification(android_devices, gcm_payload, gcm_options)
def handle_push_notification(user_profile_id: int, missed_message: Dict[str, Any]) -> None: """ missed_message is the event received by the zerver.worker.queue_processors.PushNotificationWorker.consume function. """ if not push_notifications_enabled(): return user_profile = get_user_profile_by_id(user_profile_id) if not (receives_offline_push_notifications(user_profile) or receives_online_notifications(user_profile)): return try: (message, user_message) = access_message(user_profile, missed_message['message_id']) except JsonableError: if ArchivedMessage.objects.filter(id=missed_message['message_id']).exists(): # If the cause is a race with the message being deleted, # that's normal and we have no need to log an error. return logging.info( "Unexpected message access failure handling push notifications: %s %s", user_profile.id, missed_message['message_id'], ) return if user_message is not None: # If the user has read the message already, don't push-notify. # # TODO: It feels like this is already handled when things are # put in the queue; maybe we should centralize this logic with # the `zerver/tornado/event_queue.py` logic? if user_message.flags.read or user_message.flags.active_mobile_push_notification: return # Otherwise, we mark the message as having an active mobile # push notification, so that we can send revocation messages # later. user_message.flags.active_mobile_push_notification = True user_message.save(update_fields=["flags"]) else: # Users should only be getting push notifications into this # queue for messages they haven't received if they're # long-term idle; anything else is likely a bug. if not user_profile.long_term_idle: logger.error( "Could not find UserMessage with message_id %s and user_id %s", missed_message['message_id'], user_profile_id, ) return message.trigger = missed_message['trigger'] apns_payload = get_message_payload_apns(user_profile, message) gcm_payload, gcm_options = get_message_payload_gcm(user_profile, message) logger.info("Sending push notifications to mobile clients for user %s", user_profile_id) if uses_notification_bouncer(): send_notifications_to_bouncer(user_profile_id, apns_payload, gcm_payload, gcm_options) return android_devices = list(PushDeviceToken.objects.filter(user=user_profile, kind=PushDeviceToken.GCM)) apple_devices = list(PushDeviceToken.objects.filter(user=user_profile, kind=PushDeviceToken.APNS)) if apple_devices: send_apple_push_notification(user_profile.id, apple_devices, apns_payload) if android_devices: send_android_push_notification(android_devices, gcm_payload, gcm_options)
def handle_push_notification(user_profile_id: int, missed_message: Dict[str, Any]) -> None: """ missed_message is the event received by the zerver.worker.queue_processors.PushNotificationWorker.consume function. """ user_profile = get_user_profile_by_id(user_profile_id) if not (receives_offline_push_notifications(user_profile) or receives_online_notifications(user_profile)): return user_profile = get_user_profile_by_id(user_profile_id) (message, user_message) = access_message(user_profile, missed_message['message_id']) if user_message is not None: # If ther user has read the message already, don't push-notify. # # TODO: It feels like this is already handled when things are # put in the queue; maybe we should centralize this logic with # the `zerver/tornado/event_queue.py` logic? if user_message.flags.read: return else: # Users should only be getting push notifications into this # queue for messages they haven't received if they're # long-term idle; anything else is likely a bug. if not user_profile.long_term_idle: logging.error("Could not find UserMessage with message_id %s and user_id %s" % ( missed_message['message_id'], user_profile_id)) return message.trigger = missed_message['trigger'] message.stream_name = missed_message.get('stream_name', None) apns_payload = get_apns_payload(message) gcm_payload = get_gcm_payload(user_profile, message) logging.info("Sending push notification to user %s" % (user_profile_id,)) if uses_notification_bouncer(): try: send_notifications_to_bouncer(user_profile_id, apns_payload, gcm_payload) except requests.ConnectionError: def failure_processor(event: Dict[str, Any]) -> None: logging.warning( "Maximum retries exceeded for trigger:%s event:push_notification" % ( event['user_profile_id'])) retry_event('missedmessage_mobile_notifications', missed_message, failure_processor) return android_devices = list(PushDeviceToken.objects.filter(user=user_profile, kind=PushDeviceToken.GCM)) apple_devices = list(PushDeviceToken.objects.filter(user=user_profile, kind=PushDeviceToken.APNS)) if apple_devices: send_apple_push_notification(user_profile.id, apple_devices, apns_payload) if android_devices: send_android_push_notification(android_devices, gcm_payload)