Repository: zulip/zulip

Issue:
Stop threading PM threads with multiple groups of users in a single email
Occasionally, a missed_message email will have emails from multiple different PM senders with you as the recipient mixed together in a single email.  This is a confusing user experience that might make users worry about security issues.  

I think the bug is that `handle_missedmessage_emails` groups messages by `(recipient_id, topic)` pairs, but PMs directly to you actually all have your user ID as the `recipient_id`.  So I think we need to just make that grouping use e.g. the sender message ID as the second part of the tuple for PMs.

It'd also be great to add a test for this sort of situation if we can do so easily.


Discussion:
@zulipbot claim


Resolving patch:
diff --git a/zerver/lib/notifications.py b/zerver/lib/notifications.py
--- a/zerver/lib/notifications.py
+++ b/zerver/lib/notifications.py
@@ -367,7 +367,11 @@ def handle_missedmessage_emails(user_profile_id, missed_email_events):
 
     messages_by_recipient_subject = defaultdict(list)  # type: Dict[Tuple[int, Text], List[Message]]
     for msg in messages:
-        messages_by_recipient_subject[(msg.recipient_id, msg.topic_name())].append(msg)
+        if msg.recipient.type == Recipient.PERSONAL:
+            # For PM's group using (recipient, sender).
+            messages_by_recipient_subject[(msg.recipient_id, msg.sender_id)].append(msg)
+        else:
+            messages_by_recipient_subject[(msg.recipient_id, msg.topic_name())].append(msg)
 
     message_count_by_recipient_subject = {
         recipient_subject: len(msgs)


Test patch:
diff --git a/zerver/tests/test_notifications.py b/zerver/tests/test_notifications.py
--- a/zerver/tests/test_notifications.py
+++ b/zerver/tests/test_notifications.py
@@ -352,3 +352,29 @@ def test_stream_link_in_missed_message(self, mock_random_token):
         body = '<a class="stream" data-stream-id="5" href="http://testserver/#narrow/stream/Verona">#Verona</a'
         subject = 'Othello, the Moor of Venice sent you a message'
         self._test_cases(tokens, msg_id, body, subject, send_as_user=False, verify_html_body=True)
+
+    @patch('zerver.lib.email_mirror.generate_random_token')
+    def test_multiple_missed_personal_messages(self, mock_random_token):
+        # type: (MagicMock) -> None
+        tokens = self._get_tokens()
+        mock_random_token.side_effect = tokens
+
+        hamlet = self.example_user('hamlet')
+        msg_id_1 = self.send_message(self.example_email('othello'),
+                                     hamlet.email,
+                                     Recipient.PERSONAL,
+                                     'Personal Message 1')
+        msg_id_2 = self.send_message(self.example_email('iago'),
+                                     hamlet.email,
+                                     Recipient.PERSONAL,
+                                     'Personal Message 2')
+
+        handle_missedmessage_emails(hamlet.id, [
+            {'message_id': msg_id_1},
+            {'message_id': msg_id_2},
+        ])
+        self.assertEqual(len(mail.outbox), 2)
+        subject = 'Iago sent you a message'
+        self.assertEqual(mail.outbox[0].subject, subject)
+        subject = 'Othello, the Moor of Venice sent you a message'
+        self.assertEqual(mail.outbox[1].subject, subject)