diff --git a/stream_chat/async_chat/client.py b/stream_chat/async_chat/client.py index 112cb8e..ce07f6d 100644 --- a/stream_chat/async_chat/client.py +++ b/stream_chat/async_chat/client.py @@ -109,7 +109,7 @@ async def _make_request( headers["Authorization"] = self.auth_token headers["stream-auth-type"] = "jwt" - if method.__name__ in ["post", "put", "patch"]: + if method.__name__ in ["post", "put", "patch", "delete"]: serialized = json.dumps(data) async with method( @@ -134,8 +134,10 @@ async def post( async def get(self, relative_url: str, params: Dict = None) -> StreamResponse: return await self._make_request(self.session.get, relative_url, params, None) - async def delete(self, relative_url: str, params: Dict = None) -> StreamResponse: - return await self._make_request(self.session.delete, relative_url, params, None) + async def delete( + self, relative_url: str, params: Dict = None, data: Any = None + ) -> StreamResponse: + return await self._make_request(self.session.delete, relative_url, params, data) async def patch( self, relative_url: str, params: Dict = None, data: Any = None @@ -355,8 +357,23 @@ async def update_message_partial( data.update(options) return await self.put(f"messages/{message_id}", data=data) - async def delete_message(self, message_id: str, **options: Any) -> StreamResponse: - return await self.delete(f"messages/{message_id}", options) + async def delete_message( + self, + message_id: str, + delete_for_me: bool = False, + deleted_by: str = None, + **options: Any, + ) -> StreamResponse: + if delete_for_me and not deleted_by: + raise ValueError("deleted_by is required when delete_for_me is True") + + params = options.copy() + if delete_for_me: + body = {"delete_for_me": True, "user": {"id": deleted_by}} + return await self.delete(f"messages/{message_id}", None, body) + if deleted_by: + params["deleted_by"] = deleted_by + return await self.delete(f"messages/{message_id}", params) async def undelete_message(self, message_id: str, user_id: str) -> StreamResponse: return await self.post( diff --git a/stream_chat/base/client.py b/stream_chat/base/client.py index 77f16a5..c6f2d94 100644 --- a/stream_chat/base/client.py +++ b/stream_chat/base/client.py @@ -560,10 +560,20 @@ def update_message_partial( @abc.abstractmethod def delete_message( - self, message_id: str, **options: Any + self, + message_id: str, + delete_for_me: bool = False, + deleted_by: str = None, + **options: Any, ) -> Union[StreamResponse, Awaitable[StreamResponse]]: """ Deletes a message. + + Args: + message_id: The ID of the message to delete + delete_for_me: If True, deletes the message only for the specified user + deleted_by: The user ID who is deleting the message (required when delete_for_me is True) + **options: Additional options to pass to the delete request """ pass diff --git a/stream_chat/client.py b/stream_chat/client.py index 1093b4e..b9572b1 100644 --- a/stream_chat/client.py +++ b/stream_chat/client.py @@ -84,6 +84,10 @@ def _make_request( data: Any = None, ) -> StreamResponse: params = params or {} + # Convert boolean values to lowercase strings for consistency with async implementation + params = { + k: str(v).lower() if isinstance(v, bool) else v for k, v in params.items() + } data = data or {} serialized = None default_params = self.get_default_params() @@ -94,7 +98,9 @@ def _make_request( url = f"{self.base_url}/{relative_url}" - if method.__name__ in ["post", "put", "patch"]: + if method.__name__ in ["post", "put", "patch"] or ( + method.__name__ == "delete" and data + ): serialized = json.dumps(data) response = method( @@ -119,8 +125,10 @@ def post( def get(self, relative_url: str, params: Dict = None) -> StreamResponse: return self._make_request(self.session.get, relative_url, params, None) - def delete(self, relative_url: str, params: Dict = None) -> StreamResponse: - return self._make_request(self.session.delete, relative_url, params, None) + def delete( + self, relative_url: str, params: Dict = None, data: Any = None + ) -> StreamResponse: + return self._make_request(self.session.delete, relative_url, params, data) def patch( self, relative_url: str, params: Dict = None, data: Any = None @@ -342,8 +350,24 @@ def update_message_partial( data.update(options) return self.put(f"messages/{message_id}", data=data) - def delete_message(self, message_id: str, **options: Any) -> StreamResponse: - return self.delete(f"messages/{message_id}", options) + def delete_message( + self, + message_id: str, + delete_for_me: bool = False, + deleted_by: str = None, + **options: Any, + ) -> StreamResponse: + if delete_for_me and not deleted_by: + raise ValueError("deleted_by is required when delete_for_me is True") + + params = options.copy() + if delete_for_me: + # Send in body with acting user for server-side auth compatibility + body = {"delete_for_me": True, "user": {"id": deleted_by}} + return self.delete(f"messages/{message_id}", None, body) + if deleted_by: + params["deleted_by"] = deleted_by + return self.delete(f"messages/{message_id}", params) def undelete_message(self, message_id: str, user_id: str) -> StreamResponse: return self.post( diff --git a/stream_chat/tests/async_chat/test_client.py b/stream_chat/tests/async_chat/test_client.py index 5bae8b3..4907841 100644 --- a/stream_chat/tests/async_chat/test_client.py +++ b/stream_chat/tests/async_chat/test_client.py @@ -462,6 +462,23 @@ async def test_delete_message( ) await client.delete_message(msg_id, hard=True) + async def test_delete_message_for_me( + self, client: StreamChatAsync, channel: Channel, random_user: Dict + ): + """Test deleting a message for a specific user (delete for me functionality)""" + msg_id = str(uuid.uuid4()) + await channel.send_message( + {"id": msg_id, "text": "helloworld"}, random_user["id"] + ) + + # Delete message for the user + response = await client.delete_message( + msg_id, delete_for_me=True, deleted_by=random_user["id"] + ) + + # Verify the request succeeded + assert response.is_ok() + async def test_flag_message( self, client: StreamChatAsync, channel: Channel, random_user, server_user: Dict ): diff --git a/stream_chat/tests/test_client.py b/stream_chat/tests/test_client.py index 176b69c..4361169 100644 --- a/stream_chat/tests/test_client.py +++ b/stream_chat/tests/test_client.py @@ -509,6 +509,21 @@ def test_delete_message(self, client: StreamChat, channel, random_user: Dict): channel.send_message({"id": msg_id, "text": "helloworld"}, random_user["id"]) client.delete_message(msg_id, hard=True) + def test_delete_message_for_me( + self, client: StreamChat, channel, random_user: Dict + ): + """Test deleting a message for a specific user (delete for me functionality)""" + msg_id = str(uuid.uuid4()) + channel.send_message({"id": msg_id, "text": "helloworld"}, random_user["id"]) + + # Delete message for the user + response = client.delete_message( + msg_id, delete_for_me=True, deleted_by=random_user["id"] + ) + + # Verify the request succeeded + assert response.is_ok() + def test_flag_message( self, client: StreamChat, channel, random_user, server_user: Dict ):