diff --git a/zerver/actions/typing.py b/zerver/actions/typing.py index adac26f14e..3a25b0c1c1 100644 --- a/zerver/actions/typing.py +++ b/zerver/actions/typing.py @@ -3,7 +3,7 @@ from typing import List from django.utils.translation import gettext as _ from zerver.lib.exceptions import JsonableError -from zerver.lib.stream_subscription import get_user_ids_for_streams +from zerver.lib.stream_subscription import get_active_subscriptions_for_stream_id from zerver.models import Realm, Stream, UserProfile, get_user_by_id_in_realm_including_cross_realm from zerver.tornado.django_api import send_event @@ -77,6 +77,10 @@ def do_send_stream_typing_notification( topic=topic, ) - user_ids_to_notify = get_user_ids_for_streams({stream.id})[stream.id] + # We don't notify long_term_idle subscribers. + subscriptions = get_active_subscriptions_for_stream_id( + stream.id, include_deactivated_users=False + ).exclude(user_profile__long_term_idle=True) + user_ids_to_notify = set(subscriptions.values_list("user_profile_id", flat=True)) send_event(sender.realm, event, user_ids_to_notify) diff --git a/zerver/lib/test_classes.py b/zerver/lib/test_classes.py index 13c800ccf3..4111b59115 100644 --- a/zerver/lib/test_classes.py +++ b/zerver/lib/test_classes.py @@ -1186,6 +1186,17 @@ Output: return [subscription.user_profile for subscription in subscriptions] + def not_long_term_idle_subscriber_ids(self, stream_name: str, realm: Realm) -> Set[int]: + stream = Stream.objects.get(name=stream_name, realm=realm) + recipient = Recipient.objects.get(type_id=stream.id, type=Recipient.STREAM) + + subscriptions = Subscription.objects.filter( + recipient=recipient, active=True, is_user_active=True + ).exclude(user_profile__long_term_idle=True) + user_profile_ids = set(subscriptions.values_list("user_profile_id", flat=True)) + + return user_profile_ids + def assert_json_success( self, result: Union["TestHttpResponse", HttpResponse], diff --git a/zerver/tests/test_typing.py b/zerver/tests/test_typing.py index e59cf81903..828c5443a5 100644 --- a/zerver/tests/test_typing.py +++ b/zerver/tests/test_typing.py @@ -372,10 +372,7 @@ class TypingHappyPathTestStreams(ZulipTestCase): stream_id = self.get_stream_id(stream_name) topic = "Some topic" - expected_user_ids = { - user_profile.id - for user_profile in self.users_subscribed_to_stream(stream_name, sender.realm) - } + expected_user_ids = self.not_long_term_idle_subscriber_ids(stream_name, sender.realm) params = dict( type="stream", @@ -406,10 +403,7 @@ class TypingHappyPathTestStreams(ZulipTestCase): stream_id = self.get_stream_id(stream_name) topic = "Some topic" - expected_user_ids = { - user_profile.id - for user_profile in self.users_subscribed_to_stream(stream_name, sender.realm) - } + expected_user_ids = self.not_long_term_idle_subscriber_ids(stream_name, sender.realm) params = dict( type="stream", @@ -434,6 +428,50 @@ class TypingHappyPathTestStreams(ZulipTestCase): self.assertEqual("typing", event["type"]) self.assertEqual("stop", event["op"]) + def test_notify_not_long_term_idle_subscribers_only(self) -> None: + sender = self.example_user("hamlet") + stream_name = self.get_streams(sender)[0] + stream_id = self.get_stream_id(stream_name) + topic = "Some topic" + + aaron = self.example_user("aaron") + iago = self.example_user("iago") + for user in [aaron, iago]: + self.subscribe(user, stream_name) + self.soft_deactivate_user(user) + + subscriber_ids = { + user_profile.id + for user_profile in self.users_subscribed_to_stream(stream_name, sender.realm) + } + not_long_term_idle_subscriber_ids = subscriber_ids - {aaron.id, iago.id} + + params = dict( + type="stream", + op="start", + stream_id=str(stream_id), + topic=topic, + ) + + with self.assert_database_query_count(5): + with self.capture_send_event_calls(expected_num_events=1) as events: + result = self.api_post(sender, "/api/v1/typing", params) + self.assert_json_success(result) + self.assert_length(events, 1) + + event = events[0]["event"] + event_user_ids = set(events[0]["users"]) + + # Only subscribers who are not long_term_idle are notified for typing notifications. + self.assertNotEqual(subscriber_ids, event_user_ids) + self.assertEqual(not_long_term_idle_subscriber_ids, event_user_ids) + + self.assertEqual(sender.email, event["sender"]["email"]) + self.assertEqual(stream_id, event["stream_id"]) + self.assertEqual(topic, event["topic"]) + self.assertEqual("typing", event["type"]) + self.assertEqual("start", event["op"]) + class TestSendTypingNotificationsSettings(ZulipTestCase): def test_send_private_typing_notifications_setting(self) -> None: @@ -475,10 +513,7 @@ class TestSendTypingNotificationsSettings(ZulipTestCase): stream_id = self.get_stream_id(stream_name) topic = "Some topic" - expected_user_ids = { - user_profile.id - for user_profile in self.users_subscribed_to_stream(stream_name, sender.realm) - } + expected_user_ids = self.not_long_term_idle_subscriber_ids(stream_name, sender.realm) params = dict( type="stream",