push_notifications: Skip enqueuing notif for users without push devices.

Previously, we enqueued events to "missedmessage_mobile_notifications"
even for users who had no registered push devices.
'handle_push_notification' later used to perform the check & skip
if there were no registered devices.

This commit avoids putting such events into the
"missedmessage_mobile_notifications" queue at all. By doing so,
we reduce unnecessary churn.
This commit is contained in:
Prakhar Pratyush
2025-09-05 21:27:09 +05:30
committed by Tim Abbott
parent aa317ee844
commit 421637ce31
13 changed files with 236 additions and 36 deletions

View File

@@ -570,6 +570,7 @@ def update_message_content(
event["prior_mention_user_ids"] = list(prior_mention_user_ids)
event["presence_idle_user_ids"] = filter_presence_idle_user_ids(info.active_user_ids)
event["all_bot_user_ids"] = list(info.all_bot_user_ids)
event["push_device_registered_user_ids"] = list(info.push_device_registered_user_ids)
if rendering_result.mentions_stream_wildcard:
event["stream_wildcard_mention_user_ids"] = list(info.stream_wildcard_mention_user_ids)
event["stream_wildcard_mention_in_followed_topic_user_ids"] = list(

View File

@@ -5,17 +5,18 @@ from collections.abc import Set as AbstractSet
from dataclasses import dataclass
from datetime import timedelta
from email.headerregistry import Address
from typing import Any, TypedDict
from typing import Any, TypedDict, cast
import orjson
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import IntegrityError, transaction
from django.db.models import F, Q, QuerySet
from django.db.models import Exists, F, OuterRef, Q, QuerySet
from django.utils.html import escape
from django.utils.timezone import now as timezone_now
from django.utils.translation import gettext as _
from django.utils.translation import override as override_language
from django_stubs_ext import WithAnnotations
from zerver.actions.uploads import do_claim_attachments
from zerver.actions.user_topics import (
@@ -99,6 +100,8 @@ from zerver.lib.widget import do_widget_post_save_actions
from zerver.models import (
Client,
Message,
PushDevice,
PushDeviceToken,
Realm,
Recipient,
Stream,
@@ -208,6 +211,7 @@ class RecipientInfoResult:
all_bot_user_ids: set[int]
topic_participant_user_ids: set[int]
sender_muted_stream: bool | None
push_device_registered_user_ids: set[int]
class ActiveUserDict(TypedDict):
@@ -218,6 +222,11 @@ class ActiveUserDict(TypedDict):
long_term_idle: bool
is_bot: bool
bot_type: int | None
has_push_device_registered: bool
class UserProfileAnnotations(TypedDict):
has_push_device_registered: bool
@dataclass
@@ -400,16 +409,40 @@ def get_recipient_info(
user_ids = message_to_user_id_set | possibly_mentioned_user_ids
if user_ids:
query: QuerySet[UserProfile, ActiveUserDict] = UserProfile.objects.filter(
is_active=True
).values(
"id",
"enable_online_push_notifications",
"enable_offline_email_notifications",
"enable_offline_push_notifications",
"is_bot",
"bot_type",
"long_term_idle",
query: QuerySet[WithAnnotations[UserProfile, UserProfileAnnotations], ActiveUserDict] = (
# We use 'cast' because django-stubs says:
# Currently, the mypy plugin can recognize that specific names were passed to `QuerySet.annotate`
# and include them in the type, but does not record the types of these attributes.
#
# The knowledge of the specific annotated fields is not yet used in creating more specific types
# for `QuerySet`'s `values` method, however knowledge that the model was annotated _is_ used to
# create a broader type result type for `values`, and to allow `filter`ing on any field.
#
# ref: https://github.com/typeddjango/django-stubs/blob/de8e0b4dd6588129f0e02f601a71486f42799533/README.md?plain=1#L263
cast(
"QuerySet[WithAnnotations[UserProfile, UserProfileAnnotations], ActiveUserDict]",
UserProfile.objects.filter(is_active=True)
.annotate(
# Uses index "zerver_pushdevice_user_bouncer_device_id_idx"
# and "zerver_pushdevicetoken_user_id_015e5dc1".
has_push_device_registered=Exists(
PushDevice.objects.filter(
user_id=OuterRef("id"), bouncer_device_id__isnull=False
)
)
| Exists(PushDeviceToken.objects.filter(user_id=OuterRef("id")))
)
.values(
"id",
"enable_online_push_notifications",
"enable_offline_email_notifications",
"enable_offline_push_notifications",
"is_bot",
"bot_type",
"long_term_idle",
"has_push_device_registered",
),
)
)
# query_for_ids is fast highly optimized for large queries, and we
@@ -487,6 +520,9 @@ def get_recipient_info(
# where we determine notifiability of the message for users.
all_bot_user_ids = {row["id"] for row in rows if row["is_bot"]}
# Users who have at least one push device registered to receive push notifications.
push_device_registered_user_ids = get_ids_for(lambda r: r["has_push_device_registered"])
return RecipientInfoResult(
active_user_ids=active_user_ids,
online_push_user_ids=online_push_user_ids,
@@ -508,6 +544,7 @@ def get_recipient_info(
all_bot_user_ids=all_bot_user_ids,
topic_participant_user_ids=topic_participant_user_ids,
sender_muted_stream=sender_muted_stream,
push_device_registered_user_ids=push_device_registered_user_ids,
)
@@ -712,6 +749,7 @@ def build_message_send_dict(
default_bot_user_ids=info.default_bot_user_ids,
service_bot_tuples=info.service_bot_tuples,
all_bot_user_ids=info.all_bot_user_ids,
push_device_registered_user_ids=info.push_device_registered_user_ids,
topic_wildcard_mention_user_ids=topic_wildcard_mention_user_ids,
stream_wildcard_mention_user_ids=stream_wildcard_mention_user_ids,
topic_wildcard_mention_in_followed_topic_user_ids=topic_wildcard_mention_in_followed_topic_user_ids,
@@ -1124,6 +1162,7 @@ def do_send_messages(
stream_wildcard_mention_in_followed_topic_user_ids=send_request.stream_wildcard_mention_in_followed_topic_user_ids,
muted_sender_user_ids=send_request.muted_sender_user_ids,
all_bot_user_ids=send_request.all_bot_user_ids,
push_device_registered_user_ids=send_request.push_device_registered_user_ids,
)
for user_id in send_request.active_user_ids
]
@@ -1167,6 +1206,7 @@ def do_send_messages(
),
muted_sender_user_ids=list(send_request.muted_sender_user_ids),
all_bot_user_ids=list(send_request.all_bot_user_ids),
push_device_registered_user_ids=list(send_request.push_device_registered_user_ids),
disable_external_notifications=send_request.disable_external_notifications,
realm_host=send_request.realm.host,
)

View File

@@ -149,6 +149,7 @@ class SendMessageRequest:
default_bot_user_ids: set[int]
service_bot_tuples: list[tuple[int, int]]
all_bot_user_ids: set[int]
push_device_registered_user_ids: set[int]
# IDs of topic participants who should be notified of topic wildcard mention.
# The 'user_allows_notifications_in_StreamTopic' with 'wildcard_mentions_notify'
# setting ON should return True.

View File

@@ -71,6 +71,7 @@ class UserMessageNotificationsData:
stream_wildcard_mention_in_followed_topic_user_ids: set[int],
muted_sender_user_ids: set[int],
all_bot_user_ids: set[int],
push_device_registered_user_ids: set[int],
) -> "UserMessageNotificationsData":
if user_id in all_bot_user_ids:
# Don't send any notifications to bots
@@ -126,30 +127,46 @@ class UserMessageNotificationsData:
and "stream_wildcard_mentioned" in flags
)
dm_push_notify = user_id not in dm_mention_push_disabled_user_ids and private_message
push_device_registered = user_id in push_device_registered_user_ids
dm_push_notify = (
push_device_registered
and user_id not in dm_mention_push_disabled_user_ids
and private_message
)
mention_push_notify = (
user_id not in dm_mention_push_disabled_user_ids and "mentioned" in flags
push_device_registered
and user_id not in dm_mention_push_disabled_user_ids
and "mentioned" in flags
)
topic_wildcard_mention_push_notify = (
user_id in topic_wildcard_mention_user_ids
push_device_registered
and user_id in topic_wildcard_mention_user_ids
and user_id not in dm_mention_push_disabled_user_ids
and "topic_wildcard_mentioned" in flags
)
stream_wildcard_mention_push_notify = (
user_id in stream_wildcard_mention_user_ids
push_device_registered
and user_id in stream_wildcard_mention_user_ids
and user_id not in dm_mention_push_disabled_user_ids
and "stream_wildcard_mentioned" in flags
)
topic_wildcard_mention_in_followed_topic_push_notify = (
user_id in topic_wildcard_mention_in_followed_topic_user_ids
push_device_registered
and user_id in topic_wildcard_mention_in_followed_topic_user_ids
and user_id not in dm_mention_push_disabled_user_ids
and "topic_wildcard_mentioned" in flags
)
stream_wildcard_mention_in_followed_topic_push_notify = (
user_id in stream_wildcard_mention_in_followed_topic_user_ids
push_device_registered
and user_id in stream_wildcard_mention_in_followed_topic_user_ids
and user_id not in dm_mention_push_disabled_user_ids
and "stream_wildcard_mentioned" in flags
)
online_push_enabled = push_device_registered and user_id in online_push_user_ids
stream_push_notify = push_device_registered and user_id in stream_push_user_ids
followed_topic_push_notify = (
push_device_registered and user_id in followed_topic_push_user_ids
)
return cls(
user_id=user_id,
dm_email_notify=dm_email_notify,
@@ -160,10 +177,10 @@ class UserMessageNotificationsData:
mention_push_notify=mention_push_notify,
topic_wildcard_mention_push_notify=topic_wildcard_mention_push_notify,
stream_wildcard_mention_push_notify=stream_wildcard_mention_push_notify,
online_push_enabled=user_id in online_push_user_ids,
stream_push_notify=user_id in stream_push_user_ids,
online_push_enabled=online_push_enabled,
stream_push_notify=stream_push_notify,
stream_email_notify=user_id in stream_email_user_ids,
followed_topic_push_notify=user_id in followed_topic_push_user_ids,
followed_topic_push_notify=followed_topic_push_notify,
followed_topic_email_notify=user_id in followed_topic_email_user_ids,
topic_wildcard_mention_in_followed_topic_push_notify=topic_wildcard_mention_in_followed_topic_push_notify,
topic_wildcard_mention_in_followed_topic_email_notify=topic_wildcard_mention_in_followed_topic_email_notify,

View File

@@ -1917,6 +1917,23 @@ Output:
realm, licenses, licenses_at_next_renewal, CustomerPlan.BILLING_SCHEDULE_MONTHLY
)
def register_push_device(self, user_profile_id: int) -> None:
PushDevice.objects.create(
user_id=user_profile_id,
push_account_id=10,
bouncer_device_id=1,
token_kind=PushDevice.TokenKind.FCM,
push_public_key="n4WTVqj8KH6u0vScRycR4TqRaHhFeJ0POvMb8LCu8iI=",
)
def register_push_device_token(self, user_profile_id: int) -> None:
PushDeviceToken.objects.create(
user_id=user_profile_id,
kind=PushDeviceToken.APNS,
token="test-token",
ios_app_id="com.zulip.flutter",
)
def create_user_notifications_data_object(
self, *, user_id: int, **kwargs: Any
) -> UserMessageNotificationsData:

View File

@@ -16,7 +16,7 @@ from zerver.actions.user_topics import do_set_user_topic_visibility_policy
from zerver.lib.cache import cache_delete, get_muting_users_cache_key
from zerver.lib.test_classes import ZulipTestCase
from zerver.lib.test_helpers import HostRequestMock, dummy_handler, mock_queue_publish
from zerver.models import Recipient, Subscription, UserProfile, UserTopic
from zerver.models import PushDevice, Recipient, Subscription, UserProfile, UserTopic
from zerver.models.streams import get_stream
from zerver.tornado.event_queue import (
ClientDescriptor,
@@ -194,6 +194,7 @@ class MissedMessageHookTest(ZulipTestCase):
do_change_user_setting(
self.user_profile, "enable_online_push_notifications", False, acting_user=None
)
self.register_push_device(self.user_profile.id)
self.iago = self.example_user("iago")
self.client_descriptor = self.allocate_event_queue(self.user_profile)
self.assertTrue(self.client_descriptor.event_queue.empty())
@@ -306,6 +307,28 @@ class MissedMessageHookTest(ZulipTestCase):
already_notified={"email_notified": True, "push_notified": False},
)
def test_no_push_device_registered(self) -> None:
# When `enable_offline_push_notifications` is `true` but no push device registered,
# push notifications should not be sent.
do_change_user_setting(
self.user_profile, "enable_offline_push_notifications", True, acting_user=None
)
PushDevice.objects.all().delete()
msg_id = self.send_personal_message(self.iago, self.user_profile)
with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue:
missedmessage_hook(self.user_profile.id, self.client_descriptor, True)
mock_enqueue.assert_called_once()
args_dict = mock_enqueue.call_args_list[0][1]
self.assert_maybe_enqueue_notifications_call_args(
args_dict=args_dict,
message_id=msg_id,
user_id=self.user_profile.id,
dm_email_notify=True,
dm_push_notify=False,
already_notified={"email_notified": True, "push_notified": False},
)
def test_topic_wildcard_mention(self) -> None:
# By default, topic wildcard mentions should send notifications, just like regular mentions
self.send_stream_message(self.user_profile, "Denmark")

View File

@@ -875,6 +875,7 @@ class HandlePushNotificationTest(PushNotificationTestCase):
othello = self.example_user("othello")
cordelia = self.example_user("cordelia")
zulip_realm = get_realm("zulip")
self.register_push_device_token(self.user_profile.id)
# user groups having upto 'MAX_GROUP_SIZE_FOR_MENTION_REACTIVATION'
# members are small user groups.

View File

@@ -3,19 +3,26 @@ from typing import Any
from unittest import mock
from django.utils.timezone import now as timezone_now
from typing_extensions import override
from zerver.actions.user_settings import do_change_user_setting
from zerver.actions.user_topics import do_set_user_topic_visibility_policy
from zerver.lib.push_notifications import get_apns_badge_count, get_apns_badge_count_future
from zerver.lib.test_classes import ZulipTestCase
from zerver.lib.test_helpers import mock_queue_publish
from zerver.models import Subscription, UserPresence, UserTopic
from zerver.models import PushDevice, Subscription, UserPresence, UserTopic
from zerver.models.scheduled_jobs import NotificationTriggers
from zerver.models.streams import get_stream
from zerver.tornado.event_queue import maybe_enqueue_notifications
class EditMessageSideEffectsTest(ZulipTestCase):
@override
def setUp(self) -> None:
super().setUp()
cordelia = self.example_user("cordelia")
self.register_push_device(cordelia.id)
def _assert_update_does_not_notify_anybody(self, message_id: int, content: str) -> None:
url = "/json/messages/" + str(message_id)
@@ -223,6 +230,37 @@ class EditMessageSideEffectsTest(ZulipTestCase):
updated_content = "re-mention @**Cordelia, Lear's daughter**"
self._send_and_update_message(original_content, updated_content, expect_short_circuit=True)
def test_no_push_device_registered(self) -> None:
cordelia = self.example_user("cordelia")
hamlet = self.example_user("hamlet")
# We're interested to test the push notification behaviour when
# the user has no push device registered. Disable email notification.
do_change_user_setting(
cordelia, "enable_offline_email_notifications", False, acting_user=None
)
self.assertTrue(cordelia.enable_offline_push_notifications)
PushDevice.objects.all().delete()
original_content = "no mention"
updated_content = "now we mention @**Cordelia, Lear's daughter**"
notification_message_data = self._send_and_update_message(original_content, updated_content)
message_id = notification_message_data["message_id"]
info = notification_message_data["info"]
expected_enqueue_kwargs = self.get_maybe_enqueue_notifications_parameters(
user_id=cordelia.id,
acting_user_id=hamlet.id,
message_id=message_id,
mention_email_notify=False,
mention_push_notify=False,
already_notified={},
)
self.assertEqual(info["enqueue_kwargs"], expected_enqueue_kwargs)
self.assert_length(info["queue_messages"], 0)
def _turn_on_stream_push_for_cordelia(self) -> None:
"""
conventions:
@@ -632,6 +670,7 @@ class EditMessageSideEffectsTest(ZulipTestCase):
self, mock_push_notifications: mock.MagicMock
) -> None:
mentioned_user = self.example_user("iago")
self.register_push_device(mentioned_user.id)
self.assertEqual(get_apns_badge_count(mentioned_user), 0)
self.assertEqual(get_apns_badge_count_future(mentioned_user), 0)
@@ -677,6 +716,7 @@ class EditMessageSideEffectsTest(ZulipTestCase):
mentioned_user = self.example_user("iago")
mentioned_user.enable_stream_push_notifications = True
mentioned_user.save()
self.register_push_device(mentioned_user.id)
self.assertEqual(get_apns_badge_count(mentioned_user), 0)
self.assertEqual(get_apns_badge_count_future(mentioned_user), 0)

View File

@@ -172,18 +172,22 @@ class UnreadCountTests(ZulipTestCase):
@override
def setUp(self) -> None:
super().setUp()
with mock.patch(
"zerver.lib.push_notifications.push_notifications_configured", return_value=True
) as mock_push_notifications_configured:
hamlet = self.example_user("hamlet")
self.register_push_device(hamlet.id)
with (
mock.patch(
"zerver.lib.push_notifications.send_push_notifications"
) as mock_send_push_notifications,
mock.patch(
"zerver.lib.push_notifications.push_notifications_configured", return_value=True
) as mock_push_notifications_configured,
):
self.unread_msg_ids = [
self.send_personal_message(
self.example_user("iago"), self.example_user("hamlet"), "hello"
),
self.send_personal_message(
self.example_user("iago"), self.example_user("hamlet"), "hello2"
),
self.send_personal_message(self.example_user("iago"), hamlet, "hello"),
self.send_personal_message(self.example_user("iago"), hamlet, "hello2"),
]
mock_push_notifications_configured.assert_called()
mock_send_push_notifications.assert_called()
# Sending a new message results in unread UserMessages being created
# for users other than sender.
@@ -858,11 +862,13 @@ class PushNotificationMarkReadFlowsTest(ZulipTestCase):
.values_list("message_id", flat=True)
)
@mock.patch("zerver.lib.push_notifications.send_push_notifications")
@mock.patch("zerver.lib.push_notifications.push_notifications_configured", return_value=True)
def test_track_active_mobile_push_notifications(
self, mock_push_notifications: mock.MagicMock
self,
mock_push_notifications: mock.MagicMock,
mock_send_push_notifications: mock.MagicMock,
) -> None:
mock_push_notifications.return_value = True
self.login("hamlet")
user_profile = self.example_user("hamlet")
cordelia = self.example_user("cordelia")
@@ -870,6 +876,7 @@ class PushNotificationMarkReadFlowsTest(ZulipTestCase):
self.subscribe(cordelia, "test_stream")
second_stream = self.subscribe(user_profile, "second_stream")
self.subscribe(cordelia, "second_stream")
self.register_push_device(user_profile.id)
property_name = "push_notifications"
result = self.api_post(
@@ -942,6 +949,7 @@ class PushNotificationMarkReadFlowsTest(ZulipTestCase):
result = self.client_post("/json/mark_all_as_read", {})
self.assertEqual(self.get_mobile_push_notification_ids(user_profile), [])
mock_push_notifications.assert_called()
mock_send_push_notifications.assert_called()
class MarkAllAsReadEndpointTest(ZulipTestCase):

View File

@@ -366,6 +366,7 @@ class TestNotificationData(ZulipTestCase):
followed_topic_push_user_ids=set(),
topic_wildcard_mention_in_followed_topic_user_ids=set(),
stream_wildcard_mention_in_followed_topic_user_ids=set(),
push_device_registered_user_ids=set(),
)
self.assertEqual(user_data.is_notifiable(acting_user_id=1000, idle=True), notifiable)
@@ -474,3 +475,39 @@ class TestNotificationData(ZulipTestCase):
cordelia.id: hamlet_and_cordelia.id,
},
)
def test_push_notifiability_when_no_push_device_registered(self) -> None:
user_data = UserMessageNotificationsData.from_user_id_sets(
user_id=9,
flags=[
"mentioned",
"topic_wildcard_mentioned",
"stream_wildcard_mentioned",
"topic_wildcard_mentioned",
"stream_wildcard_mentioned",
],
private_message=False,
disable_external_notifications=False,
online_push_user_ids={9},
dm_mention_email_disabled_user_ids=set(),
dm_mention_push_disabled_user_ids=set(),
all_bot_user_ids=set(),
muted_sender_user_ids=set(),
stream_email_user_ids=set(),
stream_push_user_ids={9},
topic_wildcard_mention_user_ids={9},
stream_wildcard_mention_user_ids={9},
followed_topic_email_user_ids=set(),
followed_topic_push_user_ids={9},
topic_wildcard_mention_in_followed_topic_user_ids={9},
stream_wildcard_mention_in_followed_topic_user_ids={9},
push_device_registered_user_ids=set(),
)
self.assertFalse(user_data.online_push_enabled)
self.assertFalse(user_data.stream_push_notify)
self.assertFalse(user_data.topic_wildcard_mention_push_notify)
self.assertFalse(user_data.stream_wildcard_mention_push_notify)
self.assertFalse(user_data.followed_topic_push_notify)
self.assertFalse(user_data.topic_wildcard_mention_in_followed_topic_push_notify)
self.assertFalse(user_data.stream_wildcard_mention_in_followed_topic_push_notify)

View File

@@ -1408,9 +1408,13 @@ class TestAPNs(PushNotificationTestCase):
logger.output,
)
@mock.patch("zerver.lib.push_notifications.send_push_notifications_legacy")
@mock.patch("zerver.lib.push_notifications.push_notifications_configured", return_value=True)
def test_apns_badge_count(self, mock_push_notifications: mock.MagicMock) -> None:
def test_apns_badge_count(
self, mock_push_notifications: mock.MagicMock, mock_send_push_notifications: mock.MagicMock
) -> None:
user_profile = self.example_user("othello")
self.register_push_device_token(user_profile.id)
# Test APNs badge count for personal messages.
message_ids = [
self.send_personal_message(self.sender, user_profile, "Content of message")
@@ -1439,6 +1443,7 @@ class TestAPNs(PushNotificationTestCase):
self.assertEqual(get_apns_badge_count_future(user_profile), num_messages - i - 1)
mock_push_notifications.assert_called()
mock_send_push_notifications.assert_called()
class TestGetAPNsPayload(PushNotificationTestCase):
@@ -1516,11 +1521,15 @@ class TestGetAPNsPayload(PushNotificationTestCase):
}
self.assertDictEqual(payload, expected)
@mock.patch("zerver.lib.push_notifications.send_push_notifications_legacy")
@mock.patch("zerver.lib.push_notifications.push_notifications_configured", return_value=True)
def test_get_message_payload_apns_group_direct_message(
self, mock_push_notifications: mock.MagicMock
self,
mock_push_notifications: mock.MagicMock,
mock_send_push_notifications: mock.MagicMock,
) -> None:
user_profile = self.example_user("othello")
self.register_push_device_token(user_profile.id)
message_id = self.send_group_direct_message(
self.sender, [self.example_user("othello"), self.example_user("cordelia")]
)
@@ -1558,6 +1567,7 @@ class TestGetAPNsPayload(PushNotificationTestCase):
}
self.assertDictEqual(payload, expected)
mock_push_notifications.assert_called()
mock_send_push_notifications.assert_called()
def _test_get_message_payload_apns_stream_message(
self, trigger: str, empty_string_topic: bool = False

View File

@@ -2348,6 +2348,7 @@ class RecipientInfoTest(ZulipTestCase):
all_bot_user_ids=set(),
topic_participant_user_ids=set(),
sender_muted_stream=False,
push_device_registered_user_ids=set(),
)
self.assertEqual(info, expected_info)

View File

@@ -1122,6 +1122,7 @@ def process_message_event(
)
muted_sender_user_ids = set(event_template.get("muted_sender_user_ids", []))
all_bot_user_ids = set(event_template.get("all_bot_user_ids", []))
push_device_registered_user_ids = set(event_template.get("push_device_registered_user_ids", []))
disable_external_notifications = event_template.get("disable_external_notifications", False)
user_ids_without_access_to_sender = set(
event_template.get("user_ids_without_access_to_sender", [])
@@ -1192,6 +1193,7 @@ def process_message_event(
stream_wildcard_mention_in_followed_topic_user_ids=stream_wildcard_mention_in_followed_topic_user_ids,
muted_sender_user_ids=muted_sender_user_ids,
all_bot_user_ids=all_bot_user_ids,
push_device_registered_user_ids=push_device_registered_user_ids,
)
# Calling asdict would be slow, as it does a deep copy; pull
@@ -1401,6 +1403,7 @@ def process_message_update_event(
)
muted_sender_user_ids = set(event_template.pop("muted_sender_user_ids", []))
all_bot_user_ids = set(event_template.pop("all_bot_user_ids", []))
push_device_registered_user_ids = set(event_template.pop("push_device_registered_user_ids", []))
disable_external_notifications = event_template.pop("disable_external_notifications", False)
online_push_user_ids = set(event_template.pop("online_push_user_ids", []))
stream_name = event_template.get("stream_name")
@@ -1445,6 +1448,7 @@ def process_message_update_event(
stream_wildcard_mention_in_followed_topic_user_ids=stream_wildcard_mention_in_followed_topic_user_ids,
muted_sender_user_ids=muted_sender_user_ids,
all_bot_user_ids=all_bot_user_ids,
push_device_registered_user_ids=push_device_registered_user_ids,
)
maybe_enqueue_notifications_for_message_update(