Repository: zulip/zulip

Issue:
Make it possible to send a DM to bots and to yourself when DMs are disabled
<!-- Issue description -->

Even when DMs are disabled, you should still be able to send a DM to bots and to yourself. We broke something about this at some point, though. Currently, with a bot:

- Replying to a message works fine.
- Starting a new conversation looks like it won't work, but you can actually do it if you send with the keyboard (the button is disabled).

The tooltip on the disabled "Send" button also looks broken specifically when you're composing to a bot, probably because that button is actually supposed to be enabled, and we're in some weird intermediate state:

<img width="888" alt="Screenshot-2023-12-18-at-1 35 05AM" src="https://github.com/zulip/zulip/assets/2090066/1469d6fe-9b1f-4c7f-9e72-fe621cf40715">

This bug is mainly because of the placement of ```check_posting_policy_for_compose_box``` method inside ```on_compose_select_recipient_update``` rather than ```update_on_recipient_change```, which is causing it to update the posting policy of compose only when you change the things in recipient dropdown, and not when you update the sender.

<!-- Link to a message in the chat.zulip.org discussion. Message links will still work even if the topic is renamed or resolved. Link back to this issue from the chat.zulip.org thread. -->

[CZO thread](https://chat.zulip.org/#narrow/stream/9-issues/topic/compose.20box.20tooltips.20lag.20.2327158/near/1705825)



Discussion:
@zulipbot claim

Resolving patch:
diff --git a/zerver/actions/message_send.py b/zerver/actions/message_send.py
--- a/zerver/actions/message_send.py
+++ b/zerver/actions/message_send.py
@@ -1531,8 +1531,10 @@ def check_private_message_policy(
     realm: Realm, sender: UserProfile, user_profiles: Sequence[UserProfile]
 ) -> None:
     if realm.private_message_policy == Realm.PRIVATE_MESSAGE_POLICY_DISABLED:
-        if sender.is_bot or (len(user_profiles) == 1 and user_profiles[0].is_bot):
-            # We allow direct messages only between users and bots,
+        if sender.is_bot or (
+            len(user_profiles) == 1 and (user_profiles[0].is_bot or user_profiles[0] == sender)
+        ):
+            # We allow direct messages only between users and bots or to oneself,
             # to avoid breaking the tutorial as well as automated
             # notifications from system bots to users.
             return


Test patch:
diff --git a/zerver/tests/test_message_send.py b/zerver/tests/test_message_send.py
--- a/zerver/tests/test_message_send.py
+++ b/zerver/tests/test_message_send.py
@@ -2609,6 +2609,73 @@ def test_ensure_stream_gets_called(self) -> None:
         # wasn't automatically created.
         Stream.objects.get(name=stream_name, realm_id=realm.id)
 
+    def test_direct_message_to_self_and_bot_in_dm_disabled_org(self) -> None:
+        """
+        Test that a user can send a direct message to themselves and to a bot in a DM disabled organization
+        """
+        sender = self.example_user("hamlet")
+        sender.realm.private_message_policy = Realm.PRIVATE_MESSAGE_POLICY_DISABLED
+        sender.realm.save()
+
+        #  Create a non-bot user
+        recipient_user = self.example_user("othello")
+        recipient_user.realm = sender.realm
+
+        # Create a new bot user
+        bot = do_create_user(
+            email="test-bot@zulip.com",
+            password="",
+            realm=sender.realm,
+            full_name="Test Bot",
+            bot_type=UserProfile.DEFAULT_BOT,
+            bot_owner=sender,
+            acting_user=None,
+        )
+
+        # Test sending a message to self
+        result = self.api_post(
+            sender,
+            "/api/v1/messages",
+            {
+                "type": "private",
+                "to": orjson.dumps([sender.id]).decode(),
+                "content": "Test message to self",
+            },
+        )
+        self.assert_json_success(result)
+
+        msg = self.get_last_message()
+        expected = "Test message to self"
+        self.assertEqual(msg.content, expected)
+
+        # Test sending a message to non-bot user
+        result = self.api_post(
+            sender,
+            "/api/v1/messages",
+            {
+                "type": "private",
+                "to": orjson.dumps([recipient_user.id]).decode(),
+                "content": "Test message",
+            },
+        )
+        self.assert_json_error(result, "Direct messages are disabled in this organization.")
+
+        # Test sending a message to the bot
+        result = self.api_post(
+            sender,
+            "/api/v1/messages",
+            {
+                "type": "private",
+                "to": orjson.dumps([bot.id]).decode(),
+                "content": "Test message to bot",
+            },
+        )
+        self.assert_json_success(result)
+
+        msg = self.get_last_message()
+        expected = "Test message to bot"
+        self.assertEqual(msg.content, expected)
+
 
 class TestCrossRealmPMs(ZulipTestCase):
     def make_realm(self, domain: str) -> Realm: