diff --git a/zerver/lib/actions.py b/zerver/lib/actions.py index 52f2f3a790..f99158aa2c 100644 --- a/zerver/lib/actions.py +++ b/zerver/lib/actions.py @@ -1968,6 +1968,7 @@ def do_send_messages( stream_push_notify=(user_id in send_request.stream_push_user_ids), stream_email_notify=(user_id in send_request.stream_email_user_ids), wildcard_mention_notify=(user_id in send_request.wildcard_mention_user_ids), + sender_is_muted=(user_id in send_request.muted_sender_user_ids), ) for user_id in user_list ] @@ -5869,6 +5870,7 @@ def do_update_message( event["online_push_user_ids"] = list(info["online_push_user_ids"]) event["stream_push_user_ids"] = list(info["stream_push_user_ids"]) event["stream_email_user_ids"] = list(info["stream_email_user_ids"]) + event["muted_sender_user_ids"] = list(info["muted_sender_user_ids"]) event["prior_mention_user_ids"] = list(prior_mention_user_ids) event["mention_user_ids"] = list(mention_user_ids) event["presence_idle_user_ids"] = filter_presence_idle_user_ids(info["active_user_ids"]) diff --git a/zerver/tests/test_muting_users.py b/zerver/tests/test_muting_users.py index ff1fc77a5b..2ff5c63abf 100644 --- a/zerver/tests/test_muting_users.py +++ b/zerver/tests/test_muting_users.py @@ -265,3 +265,75 @@ class MutedUsersTests(ZulipTestCase): self.assert_usermessage_read_flag(othello, stream_message, False) self.assert_usermessage_read_flag(othello, huddle_message, False) self.assert_usermessage_read_flag(othello, pm_to_othello, False) + + def test_muted_message_send_notifications_not_enqueued(self) -> None: + hamlet = self.example_user("hamlet") + self.login_user(hamlet) + cordelia = self.example_user("cordelia") + + # No muting involved. Notification about to be enqueued for Hamlet. + with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as m: + self.send_personal_message(cordelia, hamlet) + m.assert_called_once() + + # Hamlet mutes Cordelia. + url = "/api/v1/users/me/muted_users/{}".format(cordelia.id) + result = self.api_post(hamlet, url) + self.assert_json_success(result) + + # Cordelia has been muted. Notification will not be enqueued for Hamlet. + with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as m: + self.send_personal_message(cordelia, hamlet) + m.assert_not_called() + + def test_muted_message_edit_notifications_not_enqueued(self) -> None: + hamlet = self.example_user("hamlet") + self.login_user(hamlet) + cordelia = self.example_user("cordelia") + self.make_stream("general") + self.subscribe(hamlet, "general") + + # No muting. Only Hamlet is subscribed to #general, so only he can potentially recieve + # notifications. + with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as m: + message_id = self.send_stream_message(cordelia, "general") + # Message does not mention Hamlet, so no notification. + m.assert_not_called() + + self.login("cordelia") + with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as m: + result = self.client_patch( + "/json/messages/" + str(message_id), + dict( + message_id=message_id, + content="@**King Hamlet**", + ), + ) + self.assert_json_success(result) + m.assert_called_once() + # `maybe_enqueue_notificaions` was called for Hamlet after message edit mentioned him. + self.assertEqual(m.call_args_list[0][1]["user_profile_id"], hamlet.id) + + # Hamlet mutes Cordelia. + self.login("hamlet") + url = "/api/v1/users/me/muted_users/{}".format(cordelia.id) + result = self.api_post(hamlet, url) + self.assert_json_success(result) + + with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as m: + message_id = self.send_stream_message(cordelia, "general") + m.assert_not_called() + + self.login("cordelia") + with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as m: + result = self.client_patch( + "/json/messages/" + str(message_id), + dict( + message_id=message_id, + content="@**King Hamlet**", + ), + ) + self.assert_json_success(result) + # `maybe_enqueue_notificaions` wasn't called for Hamlet after message edit which mentioned him, + # because the sender (Cordelia) was muted. + m.assert_not_called() diff --git a/zerver/tornado/event_queue.py b/zerver/tornado/event_queue.py index 65fa6fc8e8..2e15c4803e 100644 --- a/zerver/tornado/event_queue.py +++ b/zerver/tornado/event_queue.py @@ -954,6 +954,11 @@ def process_message_event( and "wildcard_mentioned" in flags and "read" not in flags ) + sender_is_muted = user_data.get("sender_is_muted", False) + + if sender_is_muted: + # If the sender is muted, never enqueue notifications. + continue # We first check if a message is potentially mentionable, # since receiver_is_off_zulip is somewhat expensive. @@ -1111,6 +1116,7 @@ def process_message_update_event( stream_push_user_ids = set(event_template.pop("stream_push_user_ids", [])) stream_email_user_ids = set(event_template.pop("stream_email_user_ids", [])) wildcard_mention_user_ids = set(event_template.pop("wildcard_mention_user_ids", [])) + muted_sender_user_ids = set(event_template.pop("muted_sender_user_ids", [])) # TODO/compatibility: Translation code for the rename of # `push_notify_user_ids` to `online_push_user_ids`. Remove this @@ -1146,6 +1152,7 @@ def process_message_update_event( stream_name=stream_name, online_push_enabled=(user_profile_id in online_push_user_ids), presence_idle=(user_profile_id in presence_idle_user_ids), + muted_sender=(user_profile_id in muted_sender_user_ids), prior_mentioned=(user_profile_id in prior_mention_user_ids), ) @@ -1167,8 +1174,13 @@ def maybe_enqueue_notifications_for_message_update( stream_name: Optional[str], online_push_enabled: bool, presence_idle: bool, + muted_sender: bool, prior_mentioned: bool, ) -> None: + if muted_sender: + # Never send notifications if the sender has been muted + return + if private_message: # We don't do offline notifications for PMs, because # we already notified the user of the original message