def test_receivers_online_notifications_when_user_is_not_a_bot(self): # type: () -> None self.user.is_bot = False self.user.enable_online_push_notifications = True self.assertTrue(receives_online_notifications(self.user)) self.user.enable_online_push_notifications = False self.assertFalse(receives_online_notifications(self.user))
def handle_push_notification(user_profile_id, missed_message): # type: (int, Dict[str, Any]) -> None """ missed_message is the event received by the zerver.worker.queue_processors.PushNotificationWorker.consume function. """ try: user_profile = get_user_profile_by_id(user_profile_id) if not (receives_offline_notifications(user_profile) or receives_online_notifications(user_profile)): return umessage = UserMessage.objects.get( user_profile=user_profile, message__id=missed_message['message_id']) message = umessage.message if umessage.flags.read: return apns_payload = get_apns_payload(message) gcm_payload = get_gcm_payload(user_profile, message) if uses_notification_bouncer(): try: send_notifications_to_bouncer(user_profile_id, apns_payload, gcm_payload) except requests.ConnectionError: if 'failed_tries' not in missed_message: missed_message['failed_tries'] = 0 def failure_processor(event): # type: (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) except UserMessage.DoesNotExist: logging.error("Could not find UserMessage with message_id %s" % (missed_message['message_id'], ))
def handle_push_notification(user_profile_id, missed_message): # type: (int, Dict[str, Any]) -> None try: user_profile = get_user_profile_by_id(user_profile_id) if not (receives_offline_notifications(user_profile) or receives_online_notifications(user_profile)): return umessage = UserMessage.objects.get( user_profile=user_profile, message__id=missed_message['message_id']) message = umessage.message if umessage.flags.read: return apns_payload = get_apns_payload(message) gcm_payload = get_gcm_payload(user_profile, message) if uses_notification_bouncer(): send_notifications_to_bouncer(user_profile_id, apns_payload, gcm_payload) 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)) # TODO: set badge count in a better way if apple_devices: send_apple_push_notification(user_profile.id, apple_devices, badge=1, zulip=apns_payload) if android_devices: send_android_push_notification(android_devices, gcm_payload) except UserMessage.DoesNotExist: logging.error("Could not find UserMessage with message_id %s" % (missed_message['message_id'], ))
def handle_push_notification(user_profile_id, missed_message): # type: (int, Dict[str, Any]) -> None try: user_profile = get_user_profile_by_id(user_profile_id) if not (receives_offline_notifications(user_profile) or receives_online_notifications(user_profile)): return umessage = UserMessage.objects.get(user_profile=user_profile, message__id=missed_message['message_id']) message = umessage.message if umessage.flags.read: return apns_payload = get_apns_payload(message) gcm_payload = get_gcm_payload(user_profile, message) if uses_notification_bouncer(): send_notifications_to_bouncer(user_profile_id, apns_payload, gcm_payload) 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)) # TODO: set badge count in a better way if apple_devices: send_apple_push_notification(user_profile.id, apple_devices, badge=1, zulip=apns_payload) if android_devices: send_android_push_notification(android_devices, gcm_payload) except UserMessage.DoesNotExist: logging.error("Could not find UserMessage with message_id %s" % (missed_message['message_id'],))
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 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. """ 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. """ 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)
def handle_push_notification(user_profile_id, missed_message): # type: (int, Dict[str, Any]) -> None try: user_profile = get_user_profile_by_id(user_profile_id) if not (receives_offline_notifications(user_profile) or receives_online_notifications(user_profile)): return umessage = UserMessage.objects.get(user_profile=user_profile, message__id=missed_message['message_id']) message = umessage.message if umessage.flags.read: return sender_str = message.sender.full_name android_devices = [device for device in PushDeviceToken.objects.filter(user=user_profile, kind=PushDeviceToken.GCM)] apple_devices = list(PushDeviceToken.objects.filter(user=user_profile, kind=PushDeviceToken.APNS)) if apple_devices or android_devices: # TODO: set badge count in a better way # Determine what alert string to display based on the missed messages if message.recipient.type == Recipient.HUDDLE: alert = "New private group message from %s" % (sender_str,) elif message.recipient.type == Recipient.PERSONAL: alert = "New private message from %s" % (sender_str,) elif message.recipient.type == Recipient.STREAM: alert = "New mention from %s" % (sender_str,) else: alert = "New Zulip mentions and private messages from %s" % (sender_str,) if apple_devices: apple_extra_data = { 'alert': alert, 'message_ids': [message.id], } send_apple_push_notification(user_profile.id, apple_devices, badge=1, zulip=apple_extra_data) if android_devices: content = message.content content_truncated = (len(content) > 200) if content_truncated: content = content[:200] + "..." android_data = { 'user': user_profile.email, 'event': 'message', 'alert': alert, 'zulip_message_id': message.id, # message_id is reserved for CCS 'time': datetime_to_timestamp(message.pub_date), 'content': content, 'content_truncated': content_truncated, 'sender_email': message.sender.email, 'sender_full_name': message.sender.full_name, 'sender_avatar_url': avatar_url(message.sender), } if message.recipient.type == Recipient.STREAM: android_data['recipient_type'] = "stream" android_data['stream'] = get_display_recipient(message.recipient) android_data['topic'] = message.subject elif message.recipient.type in (Recipient.HUDDLE, Recipient.PERSONAL): android_data['recipient_type'] = "private" send_android_push_notification(android_devices, android_data) except UserMessage.DoesNotExist: logging.error("Could not find UserMessage with message_id %s" % (missed_message['message_id'],))
def handle_push_notification(user_profile_id, missed_message): # type: (int, Dict[str, Any]) -> None try: user_profile = get_user_profile_by_id(user_profile_id) if not (receives_offline_notifications(user_profile) or receives_online_notifications(user_profile)): return umessage = UserMessage.objects.get(user_profile=user_profile, message__id=missed_message['message_id']) message = umessage.message if umessage.flags.read: return sender_str = message.sender.full_name android_devices = [device for device in PushDeviceToken.objects.filter(user=user_profile, kind=PushDeviceToken.GCM)] apple_devices = list(PushDeviceToken.objects.filter(user=user_profile, kind=PushDeviceToken.APNS)) if apple_devices or android_devices: # TODO: set badge count in a better way # Determine what alert string to display based on the missed messages if message.recipient.type == Recipient.HUDDLE: alert = "New private group message from %s" % (sender_str,) elif message.recipient.type == Recipient.PERSONAL: alert = "New private message from %s" % (sender_str,) elif message.recipient.type == Recipient.STREAM: alert = "New mention from %s" % (sender_str,) else: alert = "New Zulip mentions and private messages from %s" % (sender_str,) if apple_devices: apple_extra_data = { 'alert': alert, 'message_ids': [message.id], } send_apple_push_notification(user_profile.id, apple_devices, badge=1, zulip=apple_extra_data) if android_devices: content = message.content content_truncated = (len(content) > 200) if content_truncated: content = content[:200] + "..." android_data = { 'user': user_profile.email, 'event': 'message', 'alert': alert, 'zulip_message_id': message.id, # message_id is reserved for CCS 'time': datetime_to_timestamp(message.pub_date), 'content': content, 'content_truncated': content_truncated, 'sender_email': message.sender.email, 'sender_full_name': message.sender.full_name, 'sender_avatar_url': avatar_url(message.sender), } if message.recipient.type == Recipient.STREAM: android_data['recipient_type'] = "stream" android_data['stream'] = get_display_recipient(message.recipient) android_data['topic'] = message.subject elif message.recipient.type in (Recipient.HUDDLE, Recipient.PERSONAL): android_data['recipient_type'] = "private" send_android_push_notification(android_devices, android_data) except UserMessage.DoesNotExist: logging.error("Could not find UserMessage with message_id %s" % (missed_message['message_id'],))