From 9d70ed452af972976add5b9a0637d5ea9c6b0fbd Mon Sep 17 00:00:00 2001 From: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Date: Fri, 3 May 2024 12:22:36 -0400 Subject: [PATCH 1/2] Add clean conversation support (#361) This PR adds support for the /messages/clean endpoint. --- CHANGELOG.md | 4 +++ nylas/models/messages.py | 53 ++++++++++++++++++++++++++++++++ nylas/resources/messages.py | 23 ++++++++++++++ tests/conftest.py | 29 +++++++++++++++++ tests/resources/test_messages.py | 34 ++++++++++++++++++++ 5 files changed, 143 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14b8ae25..30638355 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ nylas-python Changelog ====================== +Unreleased +---------------- +* Add clean messages support + v6.1.1 ---------------- * Improved message sending and draft create/update performance diff --git a/nylas/models/messages.py b/nylas/models/messages.py index 3e1c82a9..58f917ce 100644 --- a/nylas/models/messages.py +++ b/nylas/models/messages.py @@ -198,3 +198,56 @@ class StopScheduledMessageResponse: """ message: str + + +class CleanMessagesRequest(TypedDict): + """ + Request to clean a list of messages. + + Attributes: + message_id: IDs of the email messages to clean. + ignore_links: If true, removes link-related tags () from the email message while keeping the text. + ignore_images: If true, removes images from the email message. + images_as_markdown: If true, converts images in the email message to Markdown. + ignore_tables: If true, removes table-related tags (, ) from the email message while + keeping rows. + remove_conclusion_phrases: If true, removes phrases such as "Best" and "Regards" in the email message signature. + """ + + message_id: List[str] + ignore_links: NotRequired[bool] + ignore_images: NotRequired[bool] + images_as_markdown: NotRequired[bool] + ignore_tables: NotRequired[bool] + remove_conclusion_phrases: NotRequired[bool] + + +@dataclass_json +@dataclass +class CleanMessagesResponse(Message): + """ + Message object with the cleaned HTML message body. + + Attributes: + id (str): Globally unique object identifier. + grant_id (str): The grant that this message belongs to. + from_ (List[EmailName]): The sender of the message. + date (int): The date the message was received. + object: The type of object. + thread_id (Optional[str]): The thread that this message belongs to. + subject (Optional[str]): The subject of the message. + to (Optional[List[EmailName]]): The recipients of the message. + cc (Optional[List[EmailName]]): The CC recipients of the message. + bcc (Optional[List[EmailName]]): The BCC recipients of the message. + reply_to (Optional[List[EmailName]]): The reply-to recipients of the message. + unread (Optional[bool]): Whether the message is unread. + starred (Optional[bool]): Whether the message is starred. + snippet (Optional[str]): A snippet of the message body. + body (Optional[str]): The body of the message. + attachments (Optional[List[Attachment]]): The attachments on the message. + folders (Optional[List[str]]): The folders that the message is in. + created_at (Optional[int]): Unix timestamp of when the message was created. + conversation (str): The cleaned HTML message body. + """ + + conversation: str = "" diff --git a/nylas/resources/messages.py b/nylas/resources/messages.py index b37dabed..ed19c106 100644 --- a/nylas/resources/messages.py +++ b/nylas/resources/messages.py @@ -14,6 +14,8 @@ UpdateMessageRequest, ScheduledMessage, StopScheduledMessageResponse, + CleanMessagesRequest, + CleanMessagesResponse, ) from nylas.models.response import Response, ListResponse, DeleteResponse from nylas.resources.smart_compose import SmartCompose @@ -223,3 +225,24 @@ def stop_scheduled_message( ) return Response.from_dict(json_response, StopScheduledMessageResponse) + + def clean_messages( + self, identifier: str, request_body: CleanMessagesRequest + ) -> ListResponse[CleanMessagesResponse]: + """ + Remove extra information from a list of messages. + + Args: + identifier: The identifier of the grant to clean the message for. + request_body: The values to clean the message with. + + Returns: + The list of cleaned messages. + """ + json_resposne = self._http_client._execute( + method="PUT", + path=f"/v3/grants/{identifier}/messages/clean", + request_body=request_body, + ) + + return ListResponse.from_dict(json_resposne, CleanMessagesResponse) diff --git a/tests/conftest.py b/tests/conftest.py index 41542ea7..6fba23db 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -189,3 +189,32 @@ def http_client_list_scheduled_messages(): ], } return mock_http_client + + +@pytest.fixture +def http_client_clean_messages(): + mock_http_client = Mock() + mock_http_client._execute.return_value = { + "request_id": "dd3ec9a2-8f15-403d-b269-32b1f1beb9f5", + "data": [ + { + "body": "Hello, I just sent a message using Nylas!", + "from": [ + {"name": "Daenerys Targaryen", "email": "daenerys.t@example.com"} + ], + "grant_id": "41009df5-bf11-4c97-aa18-b285b5f2e386", + "id": "message-1", + "object": "message", + "conversation": "cleaned example", + }, + { + "body": "Hello, this is a test message!", + "from": [{"name": "Michael Scott", "email": "m.scott@email.com"}], + "grant_id": "41009df5-bf11-4c97-aa18-b285b5f2e386", + "id": "message-2", + "object": "message", + "conversation": "another example", + }, + ], + } + return mock_http_client diff --git a/tests/resources/test_messages.py b/tests/resources/test_messages.py index d345a444..9a1be5bb 100644 --- a/tests/resources/test_messages.py +++ b/tests/resources/test_messages.py @@ -272,3 +272,37 @@ def test_stop_scheduled_message(self, http_client_response): method="DELETE", path="/v3/grants/abc-123/messages/schedules/schedule-123", ) + + def test_clean_messages(self, http_client_clean_messages): + messages = Messages(http_client_clean_messages) + request_body = { + "message_id": ["message-1", "message-2"], + "ignore_images": True, + "ignore_links": True, + "ignore_tables": True, + "images_as_markdown": True, + "remove_conclusion_phrases": True, + } + + response = messages.clean_messages( + identifier="abc-123", + request_body=request_body, + ) + + http_client_clean_messages._execute.assert_called_once_with( + method="PUT", + path="/v3/grants/abc-123/messages/clean", + request_body=request_body, + ) + + # Assert the conversation field, and the typical message fields serialize properly + assert len(response.data) == 2 + assert response.data[0].body == "Hello, I just sent a message using Nylas!" + assert response.data[0].from_ == [ + {"name": "Daenerys Targaryen", "email": "daenerys.t@example.com"} + ] + assert response.data[0].object == "message" + assert response.data[0].id == "message-1" + assert response.data[0].grant_id == "41009df5-bf11-4c97-aa18-b285b5f2e386" + assert response.data[0].conversation == "cleaned example" + assert response.data[1].conversation == "another example" From 873363fd9c58613ad06f798fcd1b68525708bd29 Mon Sep 17 00:00:00 2001 From: Blag Date: Fri, 3 May 2024 12:26:23 -0400 Subject: [PATCH 2/2] Python SDK - Add missing Webhook triggers (#357) Added message_bouce_detected, message_created, message_updated, message_opened, message_link_clicked, thread.replied --- nylas/models/webhooks.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/nylas/models/webhooks.py b/nylas/models/webhooks.py index dc49a284..c4239f3a 100644 --- a/nylas/models/webhooks.py +++ b/nylas/models/webhooks.py @@ -24,6 +24,12 @@ class WebhookTriggers(str, Enum): GRANT_EXPIRED = "grant.expired" MESSAGE_SEND_SUCCESS = "message.send_success" MESSAGE_SEND_FAILED = "message.send_failed" + MESSAGE_BOUNCE_DETECTED = "message.bounce_detected" + MESSAGE_CREATED = "message.created" + MESSAGE_UPDATED = "message.updated" + MESSAGE_OPENED = "message.opened" + MESSAGE_LINK_CLICKED = "message.link_clicked" + THREAD_REPLIED = "thread.replied" @dataclass_json
, ,