mirror of https://github.com/zulip/zulip.git
settings: Add notification settings checkboxes for wildcard mentions.
This change makes it possible for users to control the notification settings for wildcard mentions as a separate control from PMs and direct @-mentions.
This commit is contained in:
parent
67efed0b64
commit
1fe4f795af
|
@ -1096,6 +1096,7 @@ run_test('settings_tab', () => {
|
||||||
realm_digest_emails_enabled: true,
|
realm_digest_emails_enabled: true,
|
||||||
realm_name_in_notifications: true,
|
realm_name_in_notifications: true,
|
||||||
realm_push_notifications_enabled: true,
|
realm_push_notifications_enabled: true,
|
||||||
|
wildcard_mentions_notify: true,
|
||||||
};
|
};
|
||||||
const page_params = $.extend(page_param_checkbox_options, {
|
const page_params = $.extend(page_param_checkbox_options, {
|
||||||
full_name: "Alyssa P. Hacker", password_auth_enabled: true,
|
full_name: "Alyssa P. Hacker", password_auth_enabled: true,
|
||||||
|
@ -1112,6 +1113,7 @@ run_test('settings_tab', () => {
|
||||||
"enable_online_push_notifications",
|
"enable_online_push_notifications",
|
||||||
"enable_digest_emails",
|
"enable_digest_emails",
|
||||||
"realm_name_in_notifications",
|
"realm_name_in_notifications",
|
||||||
|
"wildcard_mentions_notify",
|
||||||
];
|
];
|
||||||
|
|
||||||
// Render with all booleans set to true.
|
// Render with all booleans set to true.
|
||||||
|
|
|
@ -67,6 +67,7 @@ function setup_settings_label() {
|
||||||
enable_stream_audible_notifications: i18n.t("Audible desktop notifications"),
|
enable_stream_audible_notifications: i18n.t("Audible desktop notifications"),
|
||||||
enable_stream_push_notifications: i18n.t("Mobile notifications"),
|
enable_stream_push_notifications: i18n.t("Mobile notifications"),
|
||||||
enable_stream_email_notifications: i18n.t("Email notifications"),
|
enable_stream_email_notifications: i18n.t("Email notifications"),
|
||||||
|
wildcard_mentions_notify: i18n.t("Notifications for @all/@everyone mentions"),
|
||||||
|
|
||||||
// pm_mention_notification_settings
|
// pm_mention_notification_settings
|
||||||
enable_desktop_notifications: i18n.t("Visual desktop notifications"),
|
enable_desktop_notifications: i18n.t("Visual desktop notifications"),
|
||||||
|
|
|
@ -3,6 +3,7 @@ const stream_notification_settings = [
|
||||||
"enable_stream_audible_notifications",
|
"enable_stream_audible_notifications",
|
||||||
"enable_stream_push_notifications",
|
"enable_stream_push_notifications",
|
||||||
"enable_stream_email_notifications",
|
"enable_stream_email_notifications",
|
||||||
|
"wildcard_mentions_notify"
|
||||||
];
|
];
|
||||||
|
|
||||||
const pm_mention_notification_settings = [
|
const pm_mention_notification_settings = [
|
||||||
|
|
|
@ -49,4 +49,6 @@ notification. Silent mentions start with `@_` instead of `@`.
|
||||||
## Mention everyone on a stream
|
## Mention everyone on a stream
|
||||||
|
|
||||||
You can mention everyone on a stream with the `@**all**` mention. Use
|
You can mention everyone on a stream with the `@**all**` mention. Use
|
||||||
sparingly! Note that this will not notify anyone who has muted the stream.
|
sparingly! Note that this will not notify anyone who has muted the
|
||||||
|
stream, and users can disable receiving email/push notifications for
|
||||||
|
these wildcard mentions.
|
||||||
|
|
|
@ -12,15 +12,28 @@ You can configure desktop, mobile, and email notifications for
|
||||||
|
|
||||||
{end_tabs}
|
{end_tabs}
|
||||||
|
|
||||||
These settings will affect notifications for private messages, group private
|
These settings will affect notifications for private messages, group
|
||||||
messages, mentions, and alert words. The one exception is that we never
|
private messages, mentions, and alert words.
|
||||||
send email notifications for `@all` or `@everyone` mentions.
|
|
||||||
|
|
||||||
You can also hide the content of private messages (and group private
|
You can also hide the content of private messages (and group private
|
||||||
messages) from desktop notifications.
|
messages) from desktop notifications.
|
||||||
Under **Other notification settings**, uncheck
|
Under **Other notification settings**, uncheck
|
||||||
**Include content of private messages in desktop notifications**.
|
**Include content of private messages in desktop notifications**.
|
||||||
|
|
||||||
|
## Wildcard mentions
|
||||||
|
|
||||||
|
By default, wildcard mentions (`@**all**`, `@**everyone**`) trigger
|
||||||
|
email/push notifications as though they were personal @-mentions. You
|
||||||
|
can toggle whether you receive notifications for wildcard mentions:
|
||||||
|
|
||||||
|
{start_tabs}
|
||||||
|
|
||||||
|
{settings_tab|notifications}
|
||||||
|
|
||||||
|
1. Under **Stream messages**, toggle **Notifications for @all/@everyone mentions**.
|
||||||
|
|
||||||
|
{end_tabs}
|
||||||
|
|
||||||
## Related articles
|
## Related articles
|
||||||
|
|
||||||
* [Add an alert word](/help/add-an-alert-word)
|
* [Add an alert word](/help/add-an-alert-word)
|
||||||
|
|
|
@ -1002,8 +1002,9 @@ def get_typing_user_profiles(recipient: Recipient, sender_id: int) -> List[UserP
|
||||||
RecipientInfoResult = TypedDict('RecipientInfoResult', {
|
RecipientInfoResult = TypedDict('RecipientInfoResult', {
|
||||||
'active_user_ids': Set[int],
|
'active_user_ids': Set[int],
|
||||||
'push_notify_user_ids': Set[int],
|
'push_notify_user_ids': Set[int],
|
||||||
'stream_push_user_ids': Set[int],
|
|
||||||
'stream_email_user_ids': Set[int],
|
'stream_email_user_ids': Set[int],
|
||||||
|
'stream_push_user_ids': Set[int],
|
||||||
|
'wildcard_mention_user_ids': Set[int],
|
||||||
'um_eligible_user_ids': Set[int],
|
'um_eligible_user_ids': Set[int],
|
||||||
'long_term_idle_user_ids': Set[int],
|
'long_term_idle_user_ids': Set[int],
|
||||||
'default_bot_user_ids': Set[int],
|
'default_bot_user_ids': Set[int],
|
||||||
|
@ -1013,9 +1014,11 @@ RecipientInfoResult = TypedDict('RecipientInfoResult', {
|
||||||
def get_recipient_info(recipient: Recipient,
|
def get_recipient_info(recipient: Recipient,
|
||||||
sender_id: int,
|
sender_id: int,
|
||||||
stream_topic: Optional[StreamTopicTarget],
|
stream_topic: Optional[StreamTopicTarget],
|
||||||
possibly_mentioned_user_ids: Optional[Set[int]]=None) -> RecipientInfoResult:
|
possibly_mentioned_user_ids: Optional[Set[int]]=None,
|
||||||
|
possible_wildcard_mention: bool=True) -> RecipientInfoResult:
|
||||||
stream_push_user_ids = set() # type: Set[int]
|
stream_push_user_ids = set() # type: Set[int]
|
||||||
stream_email_user_ids = set() # type: Set[int]
|
stream_email_user_ids = set() # type: Set[int]
|
||||||
|
wildcard_mention_user_ids = set() # type: Set[int]
|
||||||
|
|
||||||
if recipient.type == Recipient.PERSONAL:
|
if recipient.type == Recipient.PERSONAL:
|
||||||
# The sender and recipient may be the same id, so
|
# The sender and recipient may be the same id, so
|
||||||
|
@ -1033,12 +1036,16 @@ def get_recipient_info(recipient: Recipient,
|
||||||
subscription_rows = stream_topic.get_active_subscriptions().annotate(
|
subscription_rows = stream_topic.get_active_subscriptions().annotate(
|
||||||
user_profile_email_notifications=F('user_profile__enable_stream_email_notifications'),
|
user_profile_email_notifications=F('user_profile__enable_stream_email_notifications'),
|
||||||
user_profile_push_notifications=F('user_profile__enable_stream_push_notifications'),
|
user_profile_push_notifications=F('user_profile__enable_stream_push_notifications'),
|
||||||
|
user_profile_wildcard_mentions_notify=F(
|
||||||
|
'user_profile__wildcard_mentions_notify'),
|
||||||
).values(
|
).values(
|
||||||
'user_profile_id',
|
'user_profile_id',
|
||||||
'push_notifications',
|
'push_notifications',
|
||||||
'email_notifications',
|
'email_notifications',
|
||||||
|
'wildcard_mentions_notify',
|
||||||
'user_profile_email_notifications',
|
'user_profile_email_notifications',
|
||||||
'user_profile_push_notifications',
|
'user_profile_push_notifications',
|
||||||
|
'user_profile_wildcard_mentions_notify',
|
||||||
'is_muted',
|
'is_muted',
|
||||||
).order_by('user_profile_id')
|
).order_by('user_profile_id')
|
||||||
|
|
||||||
|
@ -1073,6 +1080,23 @@ def get_recipient_info(recipient: Recipient,
|
||||||
if should_send('email_notifications', row)
|
if should_send('email_notifications', row)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if possible_wildcard_mention:
|
||||||
|
# If there's a possible wildcard mention, we need to
|
||||||
|
# determine which users would receive a wildcard mention
|
||||||
|
# notification for this message should the message indeed
|
||||||
|
# contain a wildcard mention.
|
||||||
|
#
|
||||||
|
# We don't have separate values for push/email
|
||||||
|
# notifications here; at this stage, we're just
|
||||||
|
# determining whether this wildcard mention should be
|
||||||
|
# treated as a mention (and follow the user's mention
|
||||||
|
# notification preferences) or a normal message.
|
||||||
|
wildcard_mention_user_ids = {
|
||||||
|
row['user_profile_id']
|
||||||
|
for row in subscription_rows
|
||||||
|
if should_send("wildcard_mentions_notify", row)
|
||||||
|
}
|
||||||
|
|
||||||
elif recipient.type == Recipient.HUDDLE:
|
elif recipient.type == Recipient.HUDDLE:
|
||||||
message_to_user_ids = get_huddle_user_ids(recipient)
|
message_to_user_ids = get_huddle_user_ids(recipient)
|
||||||
|
|
||||||
|
@ -1171,6 +1195,7 @@ def get_recipient_info(recipient: Recipient,
|
||||||
push_notify_user_ids=push_notify_user_ids,
|
push_notify_user_ids=push_notify_user_ids,
|
||||||
stream_push_user_ids=stream_push_user_ids,
|
stream_push_user_ids=stream_push_user_ids,
|
||||||
stream_email_user_ids=stream_email_user_ids,
|
stream_email_user_ids=stream_email_user_ids,
|
||||||
|
wildcard_mention_user_ids=wildcard_mention_user_ids,
|
||||||
um_eligible_user_ids=um_eligible_user_ids,
|
um_eligible_user_ids=um_eligible_user_ids,
|
||||||
long_term_idle_user_ids=long_term_idle_user_ids,
|
long_term_idle_user_ids=long_term_idle_user_ids,
|
||||||
default_bot_user_ids=default_bot_user_ids,
|
default_bot_user_ids=default_bot_user_ids,
|
||||||
|
@ -1313,6 +1338,13 @@ def do_send_messages(messages_maybe_none: Sequence[Optional[MutableMapping[str,
|
||||||
sender_id=message['message'].sender_id,
|
sender_id=message['message'].sender_id,
|
||||||
stream_topic=stream_topic,
|
stream_topic=stream_topic,
|
||||||
possibly_mentioned_user_ids=mention_data.get_user_ids(),
|
possibly_mentioned_user_ids=mention_data.get_user_ids(),
|
||||||
|
# TODO: We should improve the `mention_data` logic to
|
||||||
|
# populate the possible_wildcard_mention field based on
|
||||||
|
# whether wildcard mention syntax actually appears in the
|
||||||
|
# message, to avoid wasting resources computing
|
||||||
|
# wildcard_mention_user_ids for messages that could
|
||||||
|
# not possibly contain a wildcard mention.
|
||||||
|
possible_wildcard_mention=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
message['active_user_ids'] = info['active_user_ids']
|
message['active_user_ids'] = info['active_user_ids']
|
||||||
|
@ -1344,6 +1376,15 @@ def do_send_messages(messages_maybe_none: Sequence[Optional[MutableMapping[str,
|
||||||
members = message['mention_data'].get_group_members(group_id)
|
members = message['mention_data'].get_group_members(group_id)
|
||||||
message['message'].mentions_user_ids.update(members)
|
message['message'].mentions_user_ids.update(members)
|
||||||
|
|
||||||
|
# Only send data to Tornado about wildcard mentions if message
|
||||||
|
# rendering determined the message had an actual wildcard
|
||||||
|
# mention in it (and not e.g. wildcard mention syntax inside a
|
||||||
|
# code block).
|
||||||
|
if message['message'].mentions_wildcard:
|
||||||
|
message['wildcard_mention_user_ids'] = info['wildcard_mention_user_ids']
|
||||||
|
else:
|
||||||
|
message['wildcard_mention_user_ids'] = []
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Once we have the actual list of mentioned ids from message
|
Once we have the actual list of mentioned ids from message
|
||||||
rendering, we can patch in "default bots" (aka normal bots)
|
rendering, we can patch in "default bots" (aka normal bots)
|
||||||
|
@ -1445,6 +1486,7 @@ def do_send_messages(messages_maybe_none: Sequence[Optional[MutableMapping[str,
|
||||||
always_push_notify=(user_id in message['push_notify_user_ids']),
|
always_push_notify=(user_id in message['push_notify_user_ids']),
|
||||||
stream_push_notify=(user_id in message['stream_push_user_ids']),
|
stream_push_notify=(user_id in message['stream_push_user_ids']),
|
||||||
stream_email_notify=(user_id in message['stream_email_user_ids']),
|
stream_email_notify=(user_id in message['stream_email_user_ids']),
|
||||||
|
wildcard_mention_notify=(user_id in message['wildcard_mention_user_ids']),
|
||||||
)
|
)
|
||||||
for user_id in user_ids
|
for user_id in user_ids
|
||||||
]
|
]
|
||||||
|
@ -4473,11 +4515,11 @@ def do_update_message(user_profile: UserProfile, message: Message, topic_name: O
|
||||||
else:
|
else:
|
||||||
stream_topic = None
|
stream_topic = None
|
||||||
|
|
||||||
# TODO: We may want a slightly leaner of this function for updates.
|
|
||||||
info = get_recipient_info(
|
info = get_recipient_info(
|
||||||
recipient=message.recipient,
|
recipient=message.recipient,
|
||||||
sender_id=message.sender_id,
|
sender_id=message.sender_id,
|
||||||
stream_topic=stream_topic,
|
stream_topic=stream_topic,
|
||||||
|
possible_wildcard_mention=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
event['push_notify_user_ids'] = list(info['push_notify_user_ids'])
|
event['push_notify_user_ids'] = list(info['push_notify_user_ids'])
|
||||||
|
@ -4486,6 +4528,10 @@ def do_update_message(user_profile: UserProfile, message: Message, topic_name: O
|
||||||
event['prior_mention_user_ids'] = list(prior_mention_user_ids)
|
event['prior_mention_user_ids'] = list(prior_mention_user_ids)
|
||||||
event['mention_user_ids'] = list(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'])
|
event['presence_idle_user_ids'] = filter_presence_idle_user_ids(info['active_user_ids'])
|
||||||
|
if message.mentions_wildcard:
|
||||||
|
event['wildcard_mention_user_ids'] = list(info['wildcard_mention_user_ids'])
|
||||||
|
else:
|
||||||
|
event['wildcard_mention_user_ids'] = []
|
||||||
|
|
||||||
if topic_name is not None:
|
if topic_name is not None:
|
||||||
orig_topic_name = message.topic_name()
|
orig_topic_name = message.topic_name()
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.25 on 2019-11-06 23:43
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('zerver', '0252_realm_user_group_edit_policy'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='userprofile',
|
||||||
|
name='wildcard_mentions_notify',
|
||||||
|
field=models.BooleanField(default=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='subscription',
|
||||||
|
name='wildcard_mentions_notify',
|
||||||
|
field=models.NullBooleanField(default=None),
|
||||||
|
),
|
||||||
|
]
|
|
@ -860,6 +860,7 @@ class UserProfile(AbstractBaseUser, PermissionsMixin):
|
||||||
enable_stream_push_notifications = models.BooleanField(default=False) # type: bool
|
enable_stream_push_notifications = models.BooleanField(default=False) # type: bool
|
||||||
enable_stream_audible_notifications = models.BooleanField(default=False) # type: bool
|
enable_stream_audible_notifications = models.BooleanField(default=False) # type: bool
|
||||||
notification_sound = models.CharField(max_length=20, default='zulip') # type: str
|
notification_sound = models.CharField(max_length=20, default='zulip') # type: str
|
||||||
|
wildcard_mentions_notify = models.BooleanField(default=True) # type: bool
|
||||||
|
|
||||||
# PM + @-mention notifications.
|
# PM + @-mention notifications.
|
||||||
enable_desktop_notifications = models.BooleanField(default=True) # type: bool
|
enable_desktop_notifications = models.BooleanField(default=True) # type: bool
|
||||||
|
@ -1004,6 +1005,7 @@ class UserProfile(AbstractBaseUser, PermissionsMixin):
|
||||||
enable_stream_email_notifications=bool,
|
enable_stream_email_notifications=bool,
|
||||||
enable_stream_push_notifications=bool,
|
enable_stream_push_notifications=bool,
|
||||||
enable_stream_audible_notifications=bool,
|
enable_stream_audible_notifications=bool,
|
||||||
|
wildcard_mentions_notify=bool,
|
||||||
message_content_in_email_notifications=bool,
|
message_content_in_email_notifications=bool,
|
||||||
notification_sound=str,
|
notification_sound=str,
|
||||||
pm_content_in_desktop_notifications=bool,
|
pm_content_in_desktop_notifications=bool,
|
||||||
|
@ -2079,6 +2081,7 @@ class Subscription(models.Model):
|
||||||
audible_notifications = models.NullBooleanField(default=None) # type: Optional[bool]
|
audible_notifications = models.NullBooleanField(default=None) # type: Optional[bool]
|
||||||
push_notifications = models.NullBooleanField(default=None) # type: Optional[bool]
|
push_notifications = models.NullBooleanField(default=None) # type: Optional[bool]
|
||||||
email_notifications = models.NullBooleanField(default=None) # type: Optional[bool]
|
email_notifications = models.NullBooleanField(default=None) # type: Optional[bool]
|
||||||
|
wildcard_mentions_notify = models.NullBooleanField(default=None) # type: Optional[bool]
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ("user_profile", "recipient")
|
unique_together = ("user_profile", "recipient")
|
||||||
|
|
|
@ -2355,6 +2355,12 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
type: boolean
|
type: boolean
|
||||||
example: true
|
example: true
|
||||||
|
- name: wildcard_mentions_notify
|
||||||
|
in: query
|
||||||
|
description: Whether wildcard mentions (E.g. @**all**) should send notifications like a personal mention.
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
|
example: true
|
||||||
- name: desktop_icon_count_display
|
- name: desktop_icon_count_display
|
||||||
in: query
|
in: query
|
||||||
description: >
|
description: >
|
||||||
|
|
|
@ -47,7 +47,7 @@ class MissedMessageNotificationsTest(ZulipTestCase):
|
||||||
# Boring message doesn't send a notice
|
# Boring message doesn't send a notice
|
||||||
email_notice, mobile_notice = self.check_will_notify(
|
email_notice, mobile_notice = self.check_will_notify(
|
||||||
user_profile.id, message_id, private_message=False,
|
user_profile.id, message_id, private_message=False,
|
||||||
mentioned=False, wildcard_mentioned=False,
|
mentioned=False, wildcard_mention_notify=False,
|
||||||
stream_push_notify=False, stream_email_notify=False,
|
stream_push_notify=False, stream_email_notify=False,
|
||||||
stream_name=None, always_push_notify=False, idle=True, already_notified={})
|
stream_name=None, always_push_notify=False, idle=True, already_notified={})
|
||||||
self.assertTrue(email_notice is None)
|
self.assertTrue(email_notice is None)
|
||||||
|
@ -56,7 +56,7 @@ class MissedMessageNotificationsTest(ZulipTestCase):
|
||||||
# Private message sends a notice
|
# Private message sends a notice
|
||||||
email_notice, mobile_notice = self.check_will_notify(
|
email_notice, mobile_notice = self.check_will_notify(
|
||||||
user_profile.id, message_id, private_message=True,
|
user_profile.id, message_id, private_message=True,
|
||||||
mentioned=False, wildcard_mentioned=False,
|
mentioned=False, wildcard_mention_notify=False,
|
||||||
stream_push_notify=False, stream_email_notify=True,
|
stream_push_notify=False, stream_email_notify=True,
|
||||||
stream_name=None, always_push_notify=False, idle=True, already_notified={})
|
stream_name=None, always_push_notify=False, idle=True, already_notified={})
|
||||||
self.assertTrue(email_notice is not None)
|
self.assertTrue(email_notice is not None)
|
||||||
|
@ -66,7 +66,7 @@ class MissedMessageNotificationsTest(ZulipTestCase):
|
||||||
# already sent notices before.
|
# already sent notices before.
|
||||||
email_notice, mobile_notice = self.check_will_notify(
|
email_notice, mobile_notice = self.check_will_notify(
|
||||||
user_profile.id, message_id, private_message=True,
|
user_profile.id, message_id, private_message=True,
|
||||||
mentioned=False, wildcard_mentioned=False,
|
mentioned=False, wildcard_mention_notify=False,
|
||||||
stream_push_notify=False, stream_email_notify=False,
|
stream_push_notify=False, stream_email_notify=False,
|
||||||
stream_name=None, always_push_notify=False, idle=True, already_notified={
|
stream_name=None, always_push_notify=False, idle=True, already_notified={
|
||||||
'push_notified': True,
|
'push_notified': True,
|
||||||
|
@ -77,7 +77,7 @@ class MissedMessageNotificationsTest(ZulipTestCase):
|
||||||
|
|
||||||
email_notice, mobile_notice = self.check_will_notify(
|
email_notice, mobile_notice = self.check_will_notify(
|
||||||
user_profile.id, message_id, private_message=True,
|
user_profile.id, message_id, private_message=True,
|
||||||
mentioned=False, wildcard_mentioned=False,
|
mentioned=False, wildcard_mention_notify=False,
|
||||||
stream_push_notify=False, stream_email_notify=False,
|
stream_push_notify=False, stream_email_notify=False,
|
||||||
stream_name=None, always_push_notify=False, idle=True, already_notified={
|
stream_name=None, always_push_notify=False, idle=True, already_notified={
|
||||||
'push_notified': False,
|
'push_notified': False,
|
||||||
|
@ -89,16 +89,18 @@ class MissedMessageNotificationsTest(ZulipTestCase):
|
||||||
# Mention sends a notice
|
# Mention sends a notice
|
||||||
email_notice, mobile_notice = self.check_will_notify(
|
email_notice, mobile_notice = self.check_will_notify(
|
||||||
user_profile.id, message_id, private_message=False,
|
user_profile.id, message_id, private_message=False,
|
||||||
mentioned=True, wildcard_mentioned=False,
|
mentioned=True, wildcard_mention_notify=False,
|
||||||
stream_push_notify=False, stream_email_notify=False,
|
stream_push_notify=False, stream_email_notify=False,
|
||||||
stream_name=None, always_push_notify=False, idle=True, already_notified={})
|
stream_name=None, always_push_notify=False, idle=True, already_notified={})
|
||||||
self.assertTrue(email_notice is not None)
|
self.assertTrue(email_notice is not None)
|
||||||
self.assertTrue(mobile_notice is not None)
|
self.assertTrue(mobile_notice is not None)
|
||||||
|
|
||||||
# Wildcard mention sends a notice
|
# Wildcard mention triggers both email and push notices (Like a
|
||||||
|
# direct mention, whether the notice is actually delivered is
|
||||||
|
# determined later, in the email/push notification code)
|
||||||
email_notice, mobile_notice = self.check_will_notify(
|
email_notice, mobile_notice = self.check_will_notify(
|
||||||
user_profile.id, message_id, private_message=False,
|
user_profile.id, message_id, private_message=False,
|
||||||
mentioned=False, wildcard_mentioned=True,
|
mentioned=False, wildcard_mention_notify=True,
|
||||||
stream_push_notify=False, stream_email_notify=False,
|
stream_push_notify=False, stream_email_notify=False,
|
||||||
stream_name=None, always_push_notify=False, idle=True, already_notified={})
|
stream_name=None, always_push_notify=False, idle=True, already_notified={})
|
||||||
self.assertTrue(email_notice is not None)
|
self.assertTrue(email_notice is not None)
|
||||||
|
@ -107,7 +109,7 @@ class MissedMessageNotificationsTest(ZulipTestCase):
|
||||||
# stream_push_notify pushes but doesn't email
|
# stream_push_notify pushes but doesn't email
|
||||||
email_notice, mobile_notice = self.check_will_notify(
|
email_notice, mobile_notice = self.check_will_notify(
|
||||||
user_profile.id, message_id, private_message=False,
|
user_profile.id, message_id, private_message=False,
|
||||||
mentioned=False, wildcard_mentioned=False,
|
mentioned=False, wildcard_mention_notify=False,
|
||||||
stream_push_notify=True, stream_email_notify=False,
|
stream_push_notify=True, stream_email_notify=False,
|
||||||
stream_name="Denmark", always_push_notify=False, idle=True, already_notified={})
|
stream_name="Denmark", always_push_notify=False, idle=True, already_notified={})
|
||||||
self.assertTrue(email_notice is None)
|
self.assertTrue(email_notice is None)
|
||||||
|
@ -116,7 +118,7 @@ class MissedMessageNotificationsTest(ZulipTestCase):
|
||||||
# stream_email_notify emails but doesn't push
|
# stream_email_notify emails but doesn't push
|
||||||
email_notice, mobile_notice = self.check_will_notify(
|
email_notice, mobile_notice = self.check_will_notify(
|
||||||
user_profile.id, message_id, private_message=False,
|
user_profile.id, message_id, private_message=False,
|
||||||
mentioned=False, wildcard_mentioned=False,
|
mentioned=False, wildcard_mention_notify=False,
|
||||||
stream_push_notify=False, stream_email_notify=True,
|
stream_push_notify=False, stream_email_notify=True,
|
||||||
stream_name="Denmark", always_push_notify=False, idle=True, already_notified={})
|
stream_name="Denmark", always_push_notify=False, idle=True, already_notified={})
|
||||||
self.assertTrue(email_notice is not None)
|
self.assertTrue(email_notice is not None)
|
||||||
|
@ -125,7 +127,7 @@ class MissedMessageNotificationsTest(ZulipTestCase):
|
||||||
# Private message doesn't send a notice if not idle
|
# Private message doesn't send a notice if not idle
|
||||||
email_notice, mobile_notice = self.check_will_notify(
|
email_notice, mobile_notice = self.check_will_notify(
|
||||||
user_profile.id, message_id, private_message=True,
|
user_profile.id, message_id, private_message=True,
|
||||||
mentioned=False, wildcard_mentioned=False,
|
mentioned=False, wildcard_mention_notify=False,
|
||||||
stream_push_notify=False, stream_email_notify=True,
|
stream_push_notify=False, stream_email_notify=True,
|
||||||
stream_name=None, always_push_notify=False, idle=False, already_notified={})
|
stream_name=None, always_push_notify=False, idle=False, already_notified={})
|
||||||
self.assertTrue(email_notice is None)
|
self.assertTrue(email_notice is None)
|
||||||
|
@ -134,7 +136,7 @@ class MissedMessageNotificationsTest(ZulipTestCase):
|
||||||
# Mention doesn't send a notice if not idle
|
# Mention doesn't send a notice if not idle
|
||||||
email_notice, mobile_notice = self.check_will_notify(
|
email_notice, mobile_notice = self.check_will_notify(
|
||||||
user_profile.id, message_id, private_message=False,
|
user_profile.id, message_id, private_message=False,
|
||||||
mentioned=True, wildcard_mentioned=False,
|
mentioned=True, wildcard_mention_notify=False,
|
||||||
stream_push_notify=False, stream_email_notify=False,
|
stream_push_notify=False, stream_email_notify=False,
|
||||||
stream_name=None, always_push_notify=False, idle=False, already_notified={})
|
stream_name=None, always_push_notify=False, idle=False, already_notified={})
|
||||||
self.assertTrue(email_notice is None)
|
self.assertTrue(email_notice is None)
|
||||||
|
@ -143,7 +145,7 @@ class MissedMessageNotificationsTest(ZulipTestCase):
|
||||||
# Wildcard mention doesn't send a notice if not idle
|
# Wildcard mention doesn't send a notice if not idle
|
||||||
email_notice, mobile_notice = self.check_will_notify(
|
email_notice, mobile_notice = self.check_will_notify(
|
||||||
user_profile.id, message_id, private_message=False,
|
user_profile.id, message_id, private_message=False,
|
||||||
mentioned=False, wildcard_mentioned=True,
|
mentioned=False, wildcard_mention_notify=True,
|
||||||
stream_push_notify=False, stream_email_notify=False,
|
stream_push_notify=False, stream_email_notify=False,
|
||||||
stream_name=None, always_push_notify=False, idle=False, already_notified={})
|
stream_name=None, always_push_notify=False, idle=False, already_notified={})
|
||||||
self.assertTrue(email_notice is None)
|
self.assertTrue(email_notice is None)
|
||||||
|
@ -152,7 +154,7 @@ class MissedMessageNotificationsTest(ZulipTestCase):
|
||||||
# Private message sends push but not email if not idle but always_push_notify
|
# Private message sends push but not email if not idle but always_push_notify
|
||||||
email_notice, mobile_notice = self.check_will_notify(
|
email_notice, mobile_notice = self.check_will_notify(
|
||||||
user_profile.id, message_id, private_message=True,
|
user_profile.id, message_id, private_message=True,
|
||||||
mentioned=False, wildcard_mentioned=False,
|
mentioned=False, wildcard_mention_notify=False,
|
||||||
stream_push_notify=False, stream_email_notify=True,
|
stream_push_notify=False, stream_email_notify=True,
|
||||||
stream_name=None, always_push_notify=True, idle=False, already_notified={})
|
stream_name=None, always_push_notify=True, idle=False, already_notified={})
|
||||||
self.assertTrue(email_notice is None)
|
self.assertTrue(email_notice is None)
|
||||||
|
@ -161,7 +163,7 @@ class MissedMessageNotificationsTest(ZulipTestCase):
|
||||||
# Stream message sends push but not email if not idle but always_push_notify
|
# Stream message sends push but not email if not idle but always_push_notify
|
||||||
email_notice, mobile_notice = self.check_will_notify(
|
email_notice, mobile_notice = self.check_will_notify(
|
||||||
user_profile.id, message_id, private_message=False,
|
user_profile.id, message_id, private_message=False,
|
||||||
mentioned=False, wildcard_mentioned=False,
|
mentioned=False, wildcard_mention_notify=False,
|
||||||
stream_push_notify=True, stream_email_notify=True,
|
stream_push_notify=True, stream_email_notify=True,
|
||||||
stream_name="Denmark", always_push_notify=True, idle=False, already_notified={})
|
stream_name="Denmark", always_push_notify=True, idle=False, already_notified={})
|
||||||
self.assertTrue(email_notice is None)
|
self.assertTrue(email_notice is None)
|
||||||
|
@ -216,6 +218,11 @@ class MissedMessageNotificationsTest(ZulipTestCase):
|
||||||
Combined with the previous test, this ensures that the missedmessage_hook is correct"""
|
Combined with the previous test, this ensures that the missedmessage_hook is correct"""
|
||||||
user_profile = self.example_user('hamlet')
|
user_profile = self.example_user('hamlet')
|
||||||
email = user_profile.email
|
email = user_profile.email
|
||||||
|
# Fetch the Denmark stream for testing
|
||||||
|
stream = get_stream("Denmark", user_profile.realm)
|
||||||
|
sub = Subscription.objects.get(user_profile=user_profile, recipient__type=Recipient.STREAM,
|
||||||
|
recipient__type_id=stream.id)
|
||||||
|
|
||||||
self.login(email)
|
self.login(email)
|
||||||
|
|
||||||
def change_subscription_properties(user_profile: UserProfile, stream: Stream, sub: Subscription,
|
def change_subscription_properties(user_profile: UserProfile, stream: Stream, sub: Subscription,
|
||||||
|
@ -261,7 +268,7 @@ class MissedMessageNotificationsTest(ZulipTestCase):
|
||||||
{'email_notified': False, 'push_notified': False}))
|
{'email_notified': False, 'push_notified': False}))
|
||||||
destroy_event_queue(client_descriptor.event_queue.id)
|
destroy_event_queue(client_descriptor.event_queue.id)
|
||||||
|
|
||||||
# Test the hook with a private message; will have already notified on send
|
# Test the hook with a private message; this should trigger notifications
|
||||||
client_descriptor = allocate_event_queue()
|
client_descriptor = allocate_event_queue()
|
||||||
self.assertTrue(client_descriptor.event_queue.empty())
|
self.assertTrue(client_descriptor.event_queue.empty())
|
||||||
msg_id = self.send_personal_message(self.example_email("iago"), email)
|
msg_id = self.send_personal_message(self.example_email("iago"), email)
|
||||||
|
@ -275,7 +282,7 @@ class MissedMessageNotificationsTest(ZulipTestCase):
|
||||||
{'email_notified': True, 'push_notified': True}))
|
{'email_notified': True, 'push_notified': True}))
|
||||||
destroy_event_queue(client_descriptor.event_queue.id)
|
destroy_event_queue(client_descriptor.event_queue.id)
|
||||||
|
|
||||||
# Test the hook with a mention; will have already notified on send.
|
# Test the hook with a mention; this should trigger notifications
|
||||||
client_descriptor = allocate_event_queue()
|
client_descriptor = allocate_event_queue()
|
||||||
self.assertTrue(client_descriptor.event_queue.empty())
|
self.assertTrue(client_descriptor.event_queue.empty())
|
||||||
msg_id = self.send_stream_message(self.example_email("iago"), "Denmark",
|
msg_id = self.send_stream_message(self.example_email("iago"), "Denmark",
|
||||||
|
@ -288,13 +295,9 @@ class MissedMessageNotificationsTest(ZulipTestCase):
|
||||||
self.assertEqual(args_list, (user_profile.id, msg_id, False, True, False, False,
|
self.assertEqual(args_list, (user_profile.id, msg_id, False, True, False, False,
|
||||||
False, "Denmark", False, True,
|
False, "Denmark", False, True,
|
||||||
{'email_notified': True, 'push_notified': True}))
|
{'email_notified': True, 'push_notified': True}))
|
||||||
|
|
||||||
stream = get_stream("Denmark", user_profile.realm)
|
|
||||||
sub = Subscription.objects.get(user_profile=user_profile, recipient__type=Recipient.STREAM,
|
|
||||||
recipient__type_id=stream.id)
|
|
||||||
destroy_event_queue(client_descriptor.event_queue.id)
|
destroy_event_queue(client_descriptor.event_queue.id)
|
||||||
|
|
||||||
# Test the hook with a wildcard mention; will have already notified on send.
|
# Test the hook with a wildcard mention; this should trigger notifications
|
||||||
client_descriptor = allocate_event_queue()
|
client_descriptor = allocate_event_queue()
|
||||||
self.assertTrue(client_descriptor.event_queue.empty())
|
self.assertTrue(client_descriptor.event_queue.empty())
|
||||||
msg_id = self.send_stream_message(self.example_email("iago"), "Denmark",
|
msg_id = self.send_stream_message(self.example_email("iago"), "Denmark",
|
||||||
|
@ -309,13 +312,61 @@ class MissedMessageNotificationsTest(ZulipTestCase):
|
||||||
{'email_notified': True, 'push_notified': True}))
|
{'email_notified': True, 'push_notified': True}))
|
||||||
destroy_event_queue(client_descriptor.event_queue.id)
|
destroy_event_queue(client_descriptor.event_queue.id)
|
||||||
|
|
||||||
# Fetch the Denmark stream for testing
|
# Test the hook with a wildcard mention sent by the user
|
||||||
stream = get_stream("Denmark", user_profile.realm)
|
# themself using a human client; should not notify.
|
||||||
sub = Subscription.objects.get(user_profile=user_profile, recipient__type=Recipient.STREAM,
|
client_descriptor = allocate_event_queue()
|
||||||
recipient__type_id=stream.id)
|
self.assertTrue(client_descriptor.event_queue.empty())
|
||||||
change_subscription_properties(user_profile, stream, sub, {'push_notifications': True})
|
msg_id = self.send_stream_message(self.example_email("hamlet"), "Denmark",
|
||||||
|
content="@**all** what's up?",
|
||||||
|
sending_client_name="website")
|
||||||
|
with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue:
|
||||||
|
missedmessage_hook(user_profile.id, client_descriptor, True)
|
||||||
|
mock_enqueue.assert_called_once()
|
||||||
|
args_list = mock_enqueue.call_args_list[0][0]
|
||||||
|
|
||||||
|
self.assertEqual(args_list, (user_profile.id, msg_id, False, False, False, False,
|
||||||
|
False, "Denmark", False, True,
|
||||||
|
{'email_notified': False, 'push_notified': False}))
|
||||||
|
destroy_event_queue(client_descriptor.event_queue.id)
|
||||||
|
|
||||||
|
# Wildcard mentions in muted streams don't notify.
|
||||||
|
change_subscription_properties(user_profile, stream, sub, {'is_muted': True})
|
||||||
|
client_descriptor = allocate_event_queue()
|
||||||
|
self.assertTrue(client_descriptor.event_queue.empty())
|
||||||
|
msg_id = self.send_stream_message(self.example_email("iago"), "Denmark",
|
||||||
|
content="@**all** what's up?")
|
||||||
|
with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue:
|
||||||
|
missedmessage_hook(user_profile.id, client_descriptor, True)
|
||||||
|
mock_enqueue.assert_called_once()
|
||||||
|
args_list = mock_enqueue.call_args_list[0][0]
|
||||||
|
|
||||||
|
self.assertEqual(args_list, (user_profile.id, msg_id, False, False, False, False,
|
||||||
|
False, "Denmark", False, True,
|
||||||
|
{'email_notified': False, 'push_notified': False}))
|
||||||
|
destroy_event_queue(client_descriptor.event_queue.id)
|
||||||
|
change_subscription_properties(user_profile, stream, sub, {'is_muted': False})
|
||||||
|
|
||||||
|
# With wildcard_mentions_notify=False, we treat the user as not mentioned.
|
||||||
|
user_profile.wildcard_mentions_notify = False
|
||||||
|
user_profile.save()
|
||||||
|
client_descriptor = allocate_event_queue()
|
||||||
|
self.assertTrue(client_descriptor.event_queue.empty())
|
||||||
|
msg_id = self.send_stream_message(self.example_email("iago"), "Denmark",
|
||||||
|
content="@**all** what's up?")
|
||||||
|
with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue:
|
||||||
|
missedmessage_hook(user_profile.id, client_descriptor, True)
|
||||||
|
mock_enqueue.assert_called_once()
|
||||||
|
args_list = mock_enqueue.call_args_list[0][0]
|
||||||
|
|
||||||
|
self.assertEqual(args_list, (user_profile.id, msg_id, False, False, False, False,
|
||||||
|
False, "Denmark", False, True,
|
||||||
|
{'email_notified': False, 'push_notified': False}))
|
||||||
|
destroy_event_queue(client_descriptor.event_queue.id)
|
||||||
|
user_profile.wildcard_mentions_notify = True
|
||||||
|
user_profile.save()
|
||||||
|
|
||||||
# Test the hook with a stream message with stream_push_notify
|
# Test the hook with a stream message with stream_push_notify
|
||||||
|
change_subscription_properties(user_profile, stream, sub, {'push_notifications': True})
|
||||||
client_descriptor = allocate_event_queue()
|
client_descriptor = allocate_event_queue()
|
||||||
self.assertTrue(client_descriptor.event_queue.empty())
|
self.assertTrue(client_descriptor.event_queue.empty())
|
||||||
msg_id = self.send_stream_message(self.example_email("iago"), "Denmark",
|
msg_id = self.send_stream_message(self.example_email("iago"), "Denmark",
|
||||||
|
|
|
@ -706,6 +706,7 @@ class EventsRegisterTest(ZulipTestCase):
|
||||||
('message_ids', check_list(check_int)),
|
('message_ids', check_list(check_int)),
|
||||||
('prior_mention_user_ids', check_list(check_int)),
|
('prior_mention_user_ids', check_list(check_int)),
|
||||||
('mention_user_ids', check_list(check_int)),
|
('mention_user_ids', check_list(check_int)),
|
||||||
|
('wildcard_mention_user_ids', check_list(check_int)),
|
||||||
('presence_idle_user_ids', check_list(check_int)),
|
('presence_idle_user_ids', check_list(check_int)),
|
||||||
('stream_push_user_ids', check_list(check_int)),
|
('stream_push_user_ids', check_list(check_int)),
|
||||||
('stream_email_user_ids', check_list(check_int)),
|
('stream_email_user_ids', check_list(check_int)),
|
||||||
|
|
|
@ -218,6 +218,7 @@ class HomeTest(ZulipTestCase):
|
||||||
"user_id",
|
"user_id",
|
||||||
"user_status",
|
"user_status",
|
||||||
"warn_no_email",
|
"warn_no_email",
|
||||||
|
"wildcard_mentions_notify",
|
||||||
"zulip_version",
|
"zulip_version",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -161,7 +161,7 @@ class EditMessageSideEffectsTest(ZulipTestCase):
|
||||||
message_id=message_id,
|
message_id=message_id,
|
||||||
private_message=False,
|
private_message=False,
|
||||||
mentioned=True,
|
mentioned=True,
|
||||||
wildcard_mentioned=False,
|
wildcard_mention_notify=False,
|
||||||
stream_push_notify=False,
|
stream_push_notify=False,
|
||||||
stream_email_notify=False,
|
stream_email_notify=False,
|
||||||
stream_name='Scotland',
|
stream_name='Scotland',
|
||||||
|
@ -314,7 +314,7 @@ class EditMessageSideEffectsTest(ZulipTestCase):
|
||||||
message_id=message_id,
|
message_id=message_id,
|
||||||
private_message=False,
|
private_message=False,
|
||||||
mentioned=True,
|
mentioned=True,
|
||||||
wildcard_mentioned=False,
|
wildcard_mention_notify=False,
|
||||||
stream_push_notify=False,
|
stream_push_notify=False,
|
||||||
stream_email_notify=False,
|
stream_email_notify=False,
|
||||||
stream_name='Scotland',
|
stream_name='Scotland',
|
||||||
|
@ -353,7 +353,7 @@ class EditMessageSideEffectsTest(ZulipTestCase):
|
||||||
message_id=message_id,
|
message_id=message_id,
|
||||||
private_message=False,
|
private_message=False,
|
||||||
mentioned=False,
|
mentioned=False,
|
||||||
wildcard_mentioned=False,
|
wildcard_mention_notify=False,
|
||||||
stream_push_notify=False,
|
stream_push_notify=False,
|
||||||
stream_email_notify=False,
|
stream_email_notify=False,
|
||||||
stream_name='Scotland',
|
stream_name='Scotland',
|
||||||
|
@ -392,7 +392,7 @@ class EditMessageSideEffectsTest(ZulipTestCase):
|
||||||
message_id=message_id,
|
message_id=message_id,
|
||||||
private_message=False,
|
private_message=False,
|
||||||
mentioned=True,
|
mentioned=True,
|
||||||
wildcard_mentioned=False,
|
wildcard_mention_notify=False,
|
||||||
stream_push_notify=False,
|
stream_push_notify=False,
|
||||||
stream_email_notify=False,
|
stream_email_notify=False,
|
||||||
stream_name='Scotland',
|
stream_name='Scotland',
|
||||||
|
@ -406,6 +406,76 @@ class EditMessageSideEffectsTest(ZulipTestCase):
|
||||||
# actual content of these messages.)
|
# actual content of these messages.)
|
||||||
self.assertEqual(len(info['queue_messages']), 2)
|
self.assertEqual(len(info['queue_messages']), 2)
|
||||||
|
|
||||||
|
def test_updates_with_wildcard_mention(self) -> None:
|
||||||
|
cordelia = self.example_user('cordelia')
|
||||||
|
|
||||||
|
message_id = self._login_and_send_original_stream_message(
|
||||||
|
content='no mention'
|
||||||
|
)
|
||||||
|
|
||||||
|
# We will simulate that the user still has a an active client,
|
||||||
|
# but they don't have UserPresence rows, so we will still
|
||||||
|
# send offline notifications.
|
||||||
|
with self._cordelia_connected_to_zulip():
|
||||||
|
info = self._get_queued_data_for_message_update(
|
||||||
|
message_id=message_id,
|
||||||
|
content='now we mention @**all**',
|
||||||
|
)
|
||||||
|
|
||||||
|
expected_enqueue_kwargs = dict(
|
||||||
|
user_profile_id=cordelia.id,
|
||||||
|
message_id=message_id,
|
||||||
|
private_message=False,
|
||||||
|
mentioned=False,
|
||||||
|
wildcard_mention_notify=True,
|
||||||
|
stream_push_notify=False,
|
||||||
|
stream_email_notify=False,
|
||||||
|
stream_name='Scotland',
|
||||||
|
always_push_notify=False,
|
||||||
|
idle=True,
|
||||||
|
already_notified={},
|
||||||
|
)
|
||||||
|
self.assertEqual(info['enqueue_kwargs'], expected_enqueue_kwargs)
|
||||||
|
|
||||||
|
# She will get messages enqueued.
|
||||||
|
self.assertEqual(len(info['queue_messages']), 2)
|
||||||
|
|
||||||
|
def test_updates_with_upgrade_wildcard_mention(self) -> None:
|
||||||
|
message_id = self._login_and_send_original_stream_message(
|
||||||
|
content='Mention @**all**'
|
||||||
|
)
|
||||||
|
|
||||||
|
# If there was a previous wildcard mention delivered to the
|
||||||
|
# user (because wildcard_mention_notify=True), we don't notify
|
||||||
|
with self._cordelia_connected_to_zulip():
|
||||||
|
self._get_queued_data_for_message_update(
|
||||||
|
message_id=message_id,
|
||||||
|
content='now we mention @**Cordelia Lear**',
|
||||||
|
expect_short_circuit=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_updates_with_upgrade_wildcard_mention_disabled(self) -> None:
|
||||||
|
# If the user has disabled notifications for wildcard
|
||||||
|
# mentions, they won't have been notified at first, which
|
||||||
|
# means they should be notified when the message is edited to
|
||||||
|
# contain a wildcard mention.
|
||||||
|
#
|
||||||
|
# This is a bug that we're not equipped to fix right now.
|
||||||
|
cordelia = self.example_user('cordelia')
|
||||||
|
cordelia.wildcard_mentions_notify = False
|
||||||
|
cordelia.save()
|
||||||
|
|
||||||
|
message_id = self._login_and_send_original_stream_message(
|
||||||
|
content='Mention @**all**'
|
||||||
|
)
|
||||||
|
|
||||||
|
with self._cordelia_connected_to_zulip():
|
||||||
|
self._get_queued_data_for_message_update(
|
||||||
|
message_id=message_id,
|
||||||
|
content='now we mention @**Cordelia Lear**',
|
||||||
|
expect_short_circuit=True,
|
||||||
|
)
|
||||||
|
|
||||||
def test_updates_with_stream_mention_of_fully_present_user(self) -> None:
|
def test_updates_with_stream_mention_of_fully_present_user(self) -> None:
|
||||||
cordelia = self.example_user('cordelia')
|
cordelia = self.example_user('cordelia')
|
||||||
|
|
||||||
|
@ -428,7 +498,7 @@ class EditMessageSideEffectsTest(ZulipTestCase):
|
||||||
message_id=message_id,
|
message_id=message_id,
|
||||||
private_message=False,
|
private_message=False,
|
||||||
mentioned=True,
|
mentioned=True,
|
||||||
wildcard_mentioned=False,
|
wildcard_mention_notify=False,
|
||||||
stream_push_notify=False,
|
stream_push_notify=False,
|
||||||
stream_email_notify=False,
|
stream_email_notify=False,
|
||||||
stream_name='Scotland',
|
stream_name='Scotland',
|
||||||
|
|
|
@ -1015,6 +1015,7 @@ class RecipientInfoTest(ZulipTestCase):
|
||||||
recipient=recipient,
|
recipient=recipient,
|
||||||
sender_id=hamlet.id,
|
sender_id=hamlet.id,
|
||||||
stream_topic=stream_topic,
|
stream_topic=stream_topic,
|
||||||
|
possible_wildcard_mention=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
all_user_ids = {hamlet.id, cordelia.id, othello.id}
|
all_user_ids = {hamlet.id, cordelia.id, othello.id}
|
||||||
|
@ -1024,6 +1025,7 @@ class RecipientInfoTest(ZulipTestCase):
|
||||||
push_notify_user_ids=set(),
|
push_notify_user_ids=set(),
|
||||||
stream_push_user_ids=set(),
|
stream_push_user_ids=set(),
|
||||||
stream_email_user_ids=set(),
|
stream_email_user_ids=set(),
|
||||||
|
wildcard_mention_user_ids=set(),
|
||||||
um_eligible_user_ids=all_user_ids,
|
um_eligible_user_ids=all_user_ids,
|
||||||
long_term_idle_user_ids=set(),
|
long_term_idle_user_ids=set(),
|
||||||
default_bot_user_ids=set(),
|
default_bot_user_ids=set(),
|
||||||
|
@ -1032,14 +1034,26 @@ class RecipientInfoTest(ZulipTestCase):
|
||||||
|
|
||||||
self.assertEqual(info, expected_info)
|
self.assertEqual(info, expected_info)
|
||||||
|
|
||||||
|
cordelia.wildcard_mentions_notify = False
|
||||||
|
cordelia.save()
|
||||||
hamlet.enable_stream_push_notifications = True
|
hamlet.enable_stream_push_notifications = True
|
||||||
hamlet.save()
|
hamlet.save()
|
||||||
info = get_recipient_info(
|
info = get_recipient_info(
|
||||||
recipient=recipient,
|
recipient=recipient,
|
||||||
sender_id=hamlet.id,
|
sender_id=hamlet.id,
|
||||||
stream_topic=stream_topic,
|
stream_topic=stream_topic,
|
||||||
|
possible_wildcard_mention=False,
|
||||||
)
|
)
|
||||||
self.assertEqual(info['stream_push_user_ids'], {hamlet.id})
|
self.assertEqual(info['stream_push_user_ids'], {hamlet.id})
|
||||||
|
self.assertEqual(info['wildcard_mention_user_ids'], set())
|
||||||
|
|
||||||
|
info = get_recipient_info(
|
||||||
|
recipient=recipient,
|
||||||
|
sender_id=hamlet.id,
|
||||||
|
stream_topic=stream_topic,
|
||||||
|
possible_wildcard_mention=True,
|
||||||
|
)
|
||||||
|
self.assertEqual(info['wildcard_mention_user_ids'], {hamlet.id, othello.id})
|
||||||
|
|
||||||
sub = get_subscription(stream_name, hamlet)
|
sub = get_subscription(stream_name, hamlet)
|
||||||
sub.push_notifications = False
|
sub.push_notifications = False
|
||||||
|
@ -1075,9 +1089,49 @@ class RecipientInfoTest(ZulipTestCase):
|
||||||
recipient=recipient,
|
recipient=recipient,
|
||||||
sender_id=hamlet.id,
|
sender_id=hamlet.id,
|
||||||
stream_topic=stream_topic,
|
stream_topic=stream_topic,
|
||||||
|
possible_wildcard_mention=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(info['stream_push_user_ids'], set())
|
self.assertEqual(info['stream_push_user_ids'], set())
|
||||||
|
self.assertEqual(info['wildcard_mention_user_ids'], set())
|
||||||
|
|
||||||
|
info = get_recipient_info(
|
||||||
|
recipient=recipient,
|
||||||
|
sender_id=hamlet.id,
|
||||||
|
stream_topic=stream_topic,
|
||||||
|
possible_wildcard_mention=True,
|
||||||
|
)
|
||||||
|
self.assertEqual(info['stream_push_user_ids'], set())
|
||||||
|
# Since Hamlet has muted the stream and Cordelia has disabled
|
||||||
|
# wildcard notifications, it should just be Othello here.
|
||||||
|
self.assertEqual(info['wildcard_mention_user_ids'], {othello.id})
|
||||||
|
|
||||||
|
sub = get_subscription(stream_name, othello)
|
||||||
|
sub.wildcard_mentions_notify = False
|
||||||
|
sub.save()
|
||||||
|
|
||||||
|
info = get_recipient_info(
|
||||||
|
recipient=recipient,
|
||||||
|
sender_id=hamlet.id,
|
||||||
|
stream_topic=stream_topic,
|
||||||
|
possible_wildcard_mention=True,
|
||||||
|
)
|
||||||
|
self.assertEqual(info['stream_push_user_ids'], set())
|
||||||
|
# Verify that stream-level wildcard_mentions_notify=False works correctly.
|
||||||
|
self.assertEqual(info['wildcard_mention_user_ids'], set())
|
||||||
|
|
||||||
|
# Verify that True works as expected as well
|
||||||
|
sub = get_subscription(stream_name, othello)
|
||||||
|
sub.wildcard_mentions_notify = True
|
||||||
|
sub.save()
|
||||||
|
|
||||||
|
info = get_recipient_info(
|
||||||
|
recipient=recipient,
|
||||||
|
sender_id=hamlet.id,
|
||||||
|
stream_topic=stream_topic,
|
||||||
|
possible_wildcard_mention=True,
|
||||||
|
)
|
||||||
|
self.assertEqual(info['stream_push_user_ids'], set())
|
||||||
|
self.assertEqual(info['wildcard_mention_user_ids'], {othello.id})
|
||||||
|
|
||||||
# Add a service bot.
|
# Add a service bot.
|
||||||
service_bot = do_create_user(
|
service_bot = do_create_user(
|
||||||
|
|
|
@ -667,11 +667,12 @@ def missedmessage_hook(user_profile_id: int, client: ClientDescriptor, last_for_
|
||||||
flags = event.get('flags')
|
flags = event.get('flags')
|
||||||
|
|
||||||
mentioned = 'mentioned' in flags and 'read' not in flags
|
mentioned = 'mentioned' in flags and 'read' not in flags
|
||||||
wildcard_mentioned = 'wildcard_mentioned' in flags and 'read' not in flags
|
|
||||||
private_message = event['message']['type'] == 'private'
|
private_message = event['message']['type'] == 'private'
|
||||||
# stream_push_notify is set in process_message_event.
|
# stream_push_notify is set in process_message_event.
|
||||||
stream_push_notify = event.get('stream_push_notify', False)
|
stream_push_notify = event.get('stream_push_notify', False)
|
||||||
stream_email_notify = event.get('stream_email_notify', False)
|
stream_email_notify = event.get('stream_email_notify', False)
|
||||||
|
wildcard_mention_notify = (event.get('wildcard_mention_notify', False) and
|
||||||
|
'read' not in flags and 'wildcard_mentioned' in flags)
|
||||||
|
|
||||||
stream_name = None
|
stream_name = None
|
||||||
if not private_message:
|
if not private_message:
|
||||||
|
@ -689,8 +690,8 @@ def missedmessage_hook(user_profile_id: int, client: ClientDescriptor, last_for_
|
||||||
email_notified = event.get("email_notified", False),
|
email_notified = event.get("email_notified", False),
|
||||||
)
|
)
|
||||||
maybe_enqueue_notifications(user_profile_id, message_id, private_message, mentioned,
|
maybe_enqueue_notifications(user_profile_id, message_id, private_message, mentioned,
|
||||||
wildcard_mentioned,
|
wildcard_mention_notify, stream_push_notify,
|
||||||
stream_push_notify, stream_email_notify, stream_name,
|
stream_email_notify, stream_name,
|
||||||
always_push_notify, idle, already_notified)
|
always_push_notify, idle, already_notified)
|
||||||
|
|
||||||
def receiver_is_off_zulip(user_profile_id: int) -> bool:
|
def receiver_is_off_zulip(user_profile_id: int) -> bool:
|
||||||
|
@ -702,7 +703,8 @@ def receiver_is_off_zulip(user_profile_id: int) -> bool:
|
||||||
return off_zulip
|
return off_zulip
|
||||||
|
|
||||||
def maybe_enqueue_notifications(user_profile_id: int, message_id: int, private_message: bool,
|
def maybe_enqueue_notifications(user_profile_id: int, message_id: int, private_message: bool,
|
||||||
mentioned: bool, wildcard_mentioned: bool,
|
mentioned: bool,
|
||||||
|
wildcard_mention_notify: bool,
|
||||||
stream_push_notify: bool,
|
stream_push_notify: bool,
|
||||||
stream_email_notify: bool, stream_name: Optional[str],
|
stream_email_notify: bool, stream_name: Optional[str],
|
||||||
always_push_notify: bool, idle: bool,
|
always_push_notify: bool, idle: bool,
|
||||||
|
@ -713,13 +715,13 @@ def maybe_enqueue_notifications(user_profile_id: int, message_id: int, private_m
|
||||||
notified = dict() # type: Dict[str, bool]
|
notified = dict() # type: Dict[str, bool]
|
||||||
|
|
||||||
if (idle or always_push_notify) and (private_message or mentioned or
|
if (idle or always_push_notify) and (private_message or mentioned or
|
||||||
wildcard_mentioned or stream_push_notify):
|
wildcard_mention_notify or stream_push_notify):
|
||||||
notice = build_offline_notification(user_profile_id, message_id)
|
notice = build_offline_notification(user_profile_id, message_id)
|
||||||
if private_message:
|
if private_message:
|
||||||
notice['trigger'] = 'private_message'
|
notice['trigger'] = 'private_message'
|
||||||
elif mentioned:
|
elif mentioned:
|
||||||
notice['trigger'] = 'mentioned'
|
notice['trigger'] = 'mentioned'
|
||||||
elif wildcard_mentioned:
|
elif wildcard_mention_notify:
|
||||||
notice['trigger'] = 'wildcard_mentioned'
|
notice['trigger'] = 'wildcard_mentioned'
|
||||||
elif stream_push_notify:
|
elif stream_push_notify:
|
||||||
notice['trigger'] = 'stream_push_notify'
|
notice['trigger'] = 'stream_push_notify'
|
||||||
|
@ -734,13 +736,13 @@ def maybe_enqueue_notifications(user_profile_id: int, message_id: int, private_m
|
||||||
# mention. Eventually, we'll add settings to allow email
|
# mention. Eventually, we'll add settings to allow email
|
||||||
# notifications to match the model of push notifications
|
# notifications to match the model of push notifications
|
||||||
# above.
|
# above.
|
||||||
if idle and (private_message or mentioned or wildcard_mentioned or stream_email_notify):
|
if idle and (private_message or mentioned or wildcard_mention_notify or stream_email_notify):
|
||||||
notice = build_offline_notification(user_profile_id, message_id)
|
notice = build_offline_notification(user_profile_id, message_id)
|
||||||
if private_message:
|
if private_message:
|
||||||
notice['trigger'] = 'private_message'
|
notice['trigger'] = 'private_message'
|
||||||
elif mentioned:
|
elif mentioned:
|
||||||
notice['trigger'] = 'mentioned'
|
notice['trigger'] = 'mentioned'
|
||||||
elif wildcard_mentioned:
|
elif wildcard_mention_notify:
|
||||||
notice['trigger'] = 'wildcard_mentioned'
|
notice['trigger'] = 'wildcard_mentioned'
|
||||||
elif stream_email_notify:
|
elif stream_email_notify:
|
||||||
notice['trigger'] = 'stream_email_notify'
|
notice['trigger'] = 'stream_email_notify'
|
||||||
|
@ -833,23 +835,26 @@ def process_message_event(event_template: Mapping[str, Any], users: Iterable[Map
|
||||||
# or they were @-notified potentially notify more immediately
|
# or they were @-notified potentially notify more immediately
|
||||||
private_message = message_type == "private" and user_profile_id != sender_id
|
private_message = message_type == "private" and user_profile_id != sender_id
|
||||||
mentioned = 'mentioned' in flags and 'read' not in flags
|
mentioned = 'mentioned' in flags and 'read' not in flags
|
||||||
wildcard_mentioned = 'wildcard_mentioned' in flags and 'read' not in flags
|
|
||||||
stream_push_notify = user_data.get('stream_push_notify', False)
|
stream_push_notify = user_data.get('stream_push_notify', False)
|
||||||
stream_email_notify = user_data.get('stream_email_notify', False)
|
stream_email_notify = user_data.get('stream_email_notify', False)
|
||||||
|
wildcard_mention_notify = (user_data.get('wildcard_mention_notify', False) and
|
||||||
|
'wildcard_mentioned' in flags and 'read' not in flags)
|
||||||
|
|
||||||
# We first check if a message is potentially mentionable,
|
# We first check if a message is potentially mentionable,
|
||||||
# since receiver_is_off_zulip is somewhat expensive.
|
# since receiver_is_off_zulip is somewhat expensive.
|
||||||
if (private_message or mentioned or wildcard_mentioned
|
if (private_message or mentioned or wildcard_mention_notify
|
||||||
or stream_push_notify or stream_email_notify):
|
or stream_push_notify or stream_email_notify):
|
||||||
idle = receiver_is_off_zulip(user_profile_id) or (user_profile_id in presence_idle_user_ids)
|
idle = receiver_is_off_zulip(user_profile_id) or (user_profile_id in presence_idle_user_ids)
|
||||||
always_push_notify = user_data.get('always_push_notify', False)
|
always_push_notify = user_data.get('always_push_notify', False)
|
||||||
stream_name = event_template.get('stream_name')
|
stream_name = event_template.get('stream_name')
|
||||||
result = maybe_enqueue_notifications(user_profile_id, message_id, private_message,
|
result = maybe_enqueue_notifications(user_profile_id, message_id, private_message,
|
||||||
mentioned, wildcard_mentioned,
|
mentioned,
|
||||||
|
wildcard_mention_notify,
|
||||||
stream_push_notify, stream_email_notify,
|
stream_push_notify, stream_email_notify,
|
||||||
stream_name, always_push_notify, idle, {})
|
stream_name, always_push_notify, idle, {})
|
||||||
result['stream_push_notify'] = stream_push_notify
|
result['stream_push_notify'] = stream_push_notify
|
||||||
result['stream_email_notify'] = stream_email_notify
|
result['stream_email_notify'] = stream_email_notify
|
||||||
|
result['wildcard_mention_notify'] = wildcard_mention_notify
|
||||||
extra_user_data[user_profile_id] = result
|
extra_user_data[user_profile_id] = result
|
||||||
|
|
||||||
for client_data in send_to_clients.values():
|
for client_data in send_to_clients.values():
|
||||||
|
@ -919,6 +924,7 @@ def process_message_update_event(event_template: Mapping[str, Any],
|
||||||
presence_idle_user_ids = set(event_template.get('presence_idle_user_ids', []))
|
presence_idle_user_ids = set(event_template.get('presence_idle_user_ids', []))
|
||||||
stream_push_user_ids = set(event_template.get('stream_push_user_ids', []))
|
stream_push_user_ids = set(event_template.get('stream_push_user_ids', []))
|
||||||
stream_email_user_ids = set(event_template.get('stream_email_user_ids', []))
|
stream_email_user_ids = set(event_template.get('stream_email_user_ids', []))
|
||||||
|
wildcard_mention_user_ids = set(event_template.get('wildcard_mention_user_ids', []))
|
||||||
push_notify_user_ids = set(event_template.get('push_notify_user_ids', []))
|
push_notify_user_ids = set(event_template.get('push_notify_user_ids', []))
|
||||||
|
|
||||||
stream_name = event_template.get('stream_name')
|
stream_name = event_template.get('stream_name')
|
||||||
|
@ -931,6 +937,8 @@ def process_message_update_event(event_template: Mapping[str, Any],
|
||||||
if key != "id":
|
if key != "id":
|
||||||
user_event[key] = user_data[key]
|
user_event[key] = user_data[key]
|
||||||
wildcard_mentioned = 'wildcard_mentioned' in user_event['flags']
|
wildcard_mentioned = 'wildcard_mentioned' in user_event['flags']
|
||||||
|
wildcard_mention_notify = wildcard_mentioned and (
|
||||||
|
user_profile_id in wildcard_mention_user_ids)
|
||||||
|
|
||||||
maybe_enqueue_notifications_for_message_update(
|
maybe_enqueue_notifications_for_message_update(
|
||||||
user_profile_id=user_profile_id,
|
user_profile_id=user_profile_id,
|
||||||
|
@ -938,7 +946,7 @@ def process_message_update_event(event_template: Mapping[str, Any],
|
||||||
stream_name=stream_name,
|
stream_name=stream_name,
|
||||||
prior_mention_user_ids=prior_mention_user_ids,
|
prior_mention_user_ids=prior_mention_user_ids,
|
||||||
mention_user_ids=mention_user_ids,
|
mention_user_ids=mention_user_ids,
|
||||||
wildcard_mentioned = wildcard_mentioned,
|
wildcard_mention_notify = wildcard_mention_notify,
|
||||||
presence_idle_user_ids=presence_idle_user_ids,
|
presence_idle_user_ids=presence_idle_user_ids,
|
||||||
stream_push_user_ids=stream_push_user_ids,
|
stream_push_user_ids=stream_push_user_ids,
|
||||||
stream_email_user_ids=stream_email_user_ids,
|
stream_email_user_ids=stream_email_user_ids,
|
||||||
|
@ -956,7 +964,7 @@ def maybe_enqueue_notifications_for_message_update(user_profile_id: UserProfile,
|
||||||
stream_name: str,
|
stream_name: str,
|
||||||
prior_mention_user_ids: Set[int],
|
prior_mention_user_ids: Set[int],
|
||||||
mention_user_ids: Set[int],
|
mention_user_ids: Set[int],
|
||||||
wildcard_mentioned: bool,
|
wildcard_mention_notify: bool,
|
||||||
presence_idle_user_ids: Set[int],
|
presence_idle_user_ids: Set[int],
|
||||||
stream_push_user_ids: Set[int],
|
stream_push_user_ids: Set[int],
|
||||||
stream_email_user_ids: Set[int],
|
stream_email_user_ids: Set[int],
|
||||||
|
@ -975,6 +983,13 @@ def maybe_enqueue_notifications_for_message_update(user_profile_id: UserProfile,
|
||||||
#
|
#
|
||||||
# Note that prior_mention_user_ids contains users who received
|
# Note that prior_mention_user_ids contains users who received
|
||||||
# a wildcard mention as well as normal mentions.
|
# a wildcard mention as well as normal mentions.
|
||||||
|
#
|
||||||
|
# TODO: Ideally, that would mean that we exclude here cases
|
||||||
|
# where user_profile.wildcard_mentions_notify=False and have
|
||||||
|
# those still send a notification. However, we don't have the
|
||||||
|
# data to determine whether or not that was the case at the
|
||||||
|
# time the original message was sent, so we can't do that
|
||||||
|
# without extending the UserMessage data model.
|
||||||
return
|
return
|
||||||
|
|
||||||
stream_push_notify = (user_profile_id in stream_push_user_ids)
|
stream_push_notify = (user_profile_id in stream_push_user_ids)
|
||||||
|
@ -1001,7 +1016,7 @@ def maybe_enqueue_notifications_for_message_update(user_profile_id: UserProfile,
|
||||||
message_id=message_id,
|
message_id=message_id,
|
||||||
private_message=private_message,
|
private_message=private_message,
|
||||||
mentioned=mentioned,
|
mentioned=mentioned,
|
||||||
wildcard_mentioned=wildcard_mentioned,
|
wildcard_mention_notify=wildcard_mention_notify,
|
||||||
stream_push_notify=stream_push_notify,
|
stream_push_notify=stream_push_notify,
|
||||||
stream_email_notify=stream_email_notify,
|
stream_email_notify=stream_email_notify,
|
||||||
stream_name=stream_name,
|
stream_name=stream_name,
|
||||||
|
|
|
@ -162,6 +162,7 @@ def json_change_notify_settings(
|
||||||
enable_stream_email_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
|
enable_stream_email_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
|
||||||
enable_stream_push_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
|
enable_stream_push_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
|
||||||
enable_stream_audible_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
|
enable_stream_audible_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
|
||||||
|
wildcard_mentions_notify: Optional[bool]=REQ(validator=check_bool, default=None),
|
||||||
notification_sound: Optional[str]=REQ(validator=check_string, default=None),
|
notification_sound: Optional[str]=REQ(validator=check_string, default=None),
|
||||||
enable_desktop_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
|
enable_desktop_notifications: Optional[bool]=REQ(validator=check_bool, default=None),
|
||||||
enable_sounds: Optional[bool]=REQ(validator=check_bool, default=None),
|
enable_sounds: Optional[bool]=REQ(validator=check_bool, default=None),
|
||||||
|
|
Loading…
Reference in New Issue