Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions sinch/domains/conversation/api/v1/internal/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
from sinch.domains.conversation.api.v1.internal.messages_endpoints import (
DeleteMessageEndpoint,
GetMessageEndpoint,
ListMessagesEndpoint,
UpdateMessageMetadataEndpoint,
SendMessageEndpoint,
)

__all__ = [
"DeleteMessageEndpoint",
"GetMessageEndpoint",
"ListMessagesEndpoint",
"UpdateMessageMetadataEndpoint",
"SendMessageEndpoint",
]
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
from sinch.core.enums import HTTPAuthentication, HTTPMethods
from sinch.core.models.http_response import HTTPResponse
from sinch.domains.conversation.models.v1.messages.internal.request import (
ListMessagesRequest,
MessageIdRequest,
UpdateMessageMetadataRequest,
SendMessageRequest,
)
from sinch.domains.conversation.models.v1.messages.internal import (
ListMessagesResponse,
)
from sinch.domains.conversation.models.v1.messages.response.types import (
ConversationMessageResponse,
SendMessageResponse,
Expand Down Expand Up @@ -36,6 +40,44 @@ def build_query_params(self) -> dict:
return query_params


class ListMessagesEndpoint(MessageEndpoint):
ENDPOINT_URL = "{origin}/v1/projects/{project_id}/messages"
HTTP_METHOD = HTTPMethods.GET.value
HTTP_AUTHENTICATION = HTTPAuthentication.OAUTH.value

QUERY_PARAM_FIELDS = {
"app_id",
"channel",
"channel_identity",
"contact_id",
"conversation_id",
"direction",
"end_time",
"messages_source",
"only_recipient_originated",
"page_size",
"page_token",
"start_time",
"view",
}

def __init__(self, project_id: str, request_data: ListMessagesRequest):
super(ListMessagesEndpoint, self).__init__(project_id, request_data)
self.project_id = project_id
self.request_data = request_data

def handle_response(self, response: HTTPResponse) -> ListMessagesResponse:
try:
super(ListMessagesEndpoint, self).handle_response(response)
except ConversationException as e:
raise ConversationException(
message=e.args[0],
response=e.http_response,
is_from_server=e.is_from_server,
)
return self.process_response_model(response.body, ListMessagesResponse)


class DeleteMessageEndpoint(MessageEndpoint):
ENDPOINT_URL = "{origin}/v1/projects/{project_id}/messages/{message_id}"
HTTP_METHOD = HTTPMethods.DELETE.value
Expand Down
93 changes: 88 additions & 5 deletions sinch/domains/conversation/api/v1/messages_apis.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from datetime import datetime
from typing import Any, Dict, List, Optional, Union

from sinch.core.pagination import Paginator, TokenBasedPaginator
from sinch.domains.conversation.models.v1.messages.internal.request import (
ListMessagesRequest,
MessageIdRequest,
UpdateMessageMetadataRequest,
SendMessageRequest,
Expand All @@ -11,12 +13,14 @@
SendMessageResponse,
)
from sinch.domains.conversation.models.v1.messages.types import (
MessagesSourceType,
ConversationChannelType,
ProcessingStrategyType,
MetadataUpdateStrategyType,
MessageQueueType,
ConversationDirectionType,
ConversationMessagesViewType,
MessageContentType,
MessageQueueType,
MessagesSourceType,
MetadataUpdateStrategyType,
ProcessingStrategyType,
CardMessageDict,
CarouselMessageDict,
ChoiceMessageDict,
Expand Down Expand Up @@ -58,6 +62,7 @@
from sinch.domains.conversation.api.v1.internal import (
DeleteMessageEndpoint,
GetMessageEndpoint,
ListMessagesEndpoint,
UpdateMessageMetadataEndpoint,
SendMessageEndpoint,
)
Expand Down Expand Up @@ -131,6 +136,84 @@ def get(
)
return self._request(GetMessageEndpoint, request_data)

def list(
self,
page_size: Optional[int] = None,
page_token: Optional[str] = None,
conversation_id: Optional[str] = None,
contact_id: Optional[str] = None,
app_id: Optional[str] = None,
channel_identity: Optional[str] = None,
start_time: Optional[datetime] = None,
end_time: Optional[datetime] = None,
view: Optional[ConversationMessagesViewType] = None,
messages_source: Optional[MessagesSourceType] = None,
only_recipient_originated: Optional[bool] = None,
channel: Optional[ConversationChannelType] = None,
direction: Optional[ConversationDirectionType] = None,
**kwargs,
) -> Paginator[ConversationMessageResponse]:
"""
List messages sent or received via particular Processing Modes.
The messages are ordered by their accept_time property in descending order.

:param page_size: Maximum number of messages to fetch. Defaults to 10, maximum is 1000.
:type page_size: Optional[int]
:param page_token: Next page token previously returned if any.
:type page_token: Optional[str]
:param conversation_id: Filter messages by conversation ID.
:type conversation_id: Optional[str]
:param contact_id: Filter messages by contact ID.
:type contact_id: Optional[str]
:param app_id: Filter messages by app ID.
:type app_id: Optional[str]
:param channel_identity: Channel identity of the contact.
:type channel_identity: Optional[str]
:param start_time: Filter messages with accept_time after this timestamp.
:type start_time: Optional[datetime]
:param end_time: Filter messages with accept_time before this timestamp.
:type end_time: Optional[datetime]
:param view: Messages view type. WITH_METADATA or WITHOUT_METADATA.
:type view: Optional[ConversationMessagesViewType]
:param messages_source: Specifies the message source for the request.
:type messages_source: Optional[MessagesSourceType]
:param only_recipient_originated: Only fetch recipient-originated messages.
:type only_recipient_originated: Optional[bool]
:param channel: Only fetch messages from the specified channel.
:type channel: Optional[ConversationChannelType]
:param direction: Only fetch messages with the specified direction. TO_APP or TO_CONTACT.
:type direction: Optional[ConversationDirectionType]
:param **kwargs: Additional parameters for the request.
:type **kwargs: dict

:returns: TokenBasedPaginator with ConversationMessageResponse items
:rtype: Paginator[ConversationMessageResponse]

For detailed documentation, visit https://developers.sinch.com/docs/conversation/.
"""
return TokenBasedPaginator(
sinch=self._sinch,
endpoint=ListMessagesEndpoint(
project_id=self._sinch.configuration.project_id,
request_data=ListMessagesRequest(
page_size=page_size,
page_token=page_token,
conversation_id=conversation_id,
contact_id=contact_id,
app_id=app_id,
channel_identity=channel_identity,
start_time=start_time,
end_time=end_time,
view=view,
messages_source=messages_source,
only_recipient_originated=only_recipient_originated,
channel=channel,
direction=direction,
**kwargs,
),
),
)

def update(
self,
message_id: str,
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
__all__ = []
from sinch.domains.conversation.models.v1.messages.internal.list_messages_response import (
ListMessagesResponse,
)

__all__ = [
"ListMessagesResponse",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from typing import List, Optional
from pydantic import Field, StrictStr
from sinch.domains.conversation.models.v1.messages.internal.base import (
BaseModelConfiguration,
)
from sinch.domains.conversation.models.v1.messages.response.types import (
ConversationMessageResponse,
)


class ListMessagesResponse(BaseModelConfiguration):
messages: Optional[List[ConversationMessageResponse]] = Field(
default=None,
description="List of messages associated to the referenced conversation.",
)
next_page_token: Optional[StrictStr] = Field(
default=None,
description="Token that should be included in the next request to fetch the next page.",
)

@property
def content(self):
"""Returns the messages as part of the response object for pagination compatibility."""
return self.messages or []
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from sinch.domains.conversation.models.v1.messages.internal.request.list_messages_request import (
ListMessagesRequest,
)
from sinch.domains.conversation.models.v1.messages.internal.request.message_id_request import (
MessageIdRequest,
)
Expand All @@ -17,6 +20,7 @@
)

__all__ = [
"ListMessagesRequest",
"MessageIdRequest",
"UpdateMessageMetadataRequest",
"Recipient",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from datetime import datetime
from typing import Optional
from pydantic import Field, StrictInt, StrictStr
from sinch.domains.conversation.models.v1.messages.internal.base import (
BaseModelConfiguration,
)
from sinch.domains.conversation.models.v1.messages.types import (
ConversationChannelType,
ConversationDirectionType,
ConversationMessagesViewType,
MessagesSourceType,
)


class ListMessagesRequest(BaseModelConfiguration):
"""Request model for listing messages."""

conversation_id: Optional[StrictStr] = Field(
default=None,
description="Filter messages by conversation ID.",
)
contact_id: Optional[StrictStr] = Field(
default=None,
description="Filter messages by contact ID.",
)
app_id: Optional[StrictStr] = Field(
default=None,
description="Filter messages by app ID.",
)
channel_identity: Optional[StrictStr] = Field(
default=None,
description="Channel identity of the contact.",
)
start_time: Optional[datetime] = Field(
default=None,
description="Filter messages with accept_time after this timestamp.",
)
end_time: Optional[datetime] = Field(
default=None,
description="Filter messages with accept_time before this timestamp.",
)
page_size: Optional[StrictInt] = Field(
default=None,
description="Maximum number of messages to fetch. Defaults to 10, maximum is 1000.",
)
page_token: Optional[StrictStr] = Field(
default=None,
description="Next page token previously returned if any.",
)
view: Optional[ConversationMessagesViewType] = Field(
default=None,
description="Messages view type. WITH_METADATA or WITHOUT_METADATA.",
)
messages_source: Optional[MessagesSourceType] = Field(
default=None,
description="Specifies the message source for the request.",
)
only_recipient_originated: Optional[bool] = Field(
default=None,
description="Only fetch recipient-originated messages.",
)
channel: Optional[ConversationChannelType] = Field(
default=None,
description="Only fetch messages from the specified channel.",
)
direction: Optional[ConversationDirectionType] = Field(
default=None,
description="Only fetch messages with the specified direction. TO_APP or TO_CONTACT.",
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
from sinch.domains.conversation.models.v1.messages.types.conversation_channel_type import (
ConversationChannelType,
)
from sinch.domains.conversation.models.v1.messages.types.conversation_messages_view_type import (
ConversationMessagesViewType,
)
from sinch.domains.conversation.models.v1.messages.types.conversation_direction_type import (
ConversationDirectionType,
)
Expand Down Expand Up @@ -87,6 +90,7 @@
__all__ = [
"AgentType",
"ConversationChannelType",
"ConversationMessagesViewType",
"ConversationDirectionType",
"ProcessingModeType",
"CardHeightType",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from typing import Literal, Union
from pydantic import StrictStr


ConversationMessagesViewType = Union[
Literal["WITH_METADATA", "WITHOUT_METADATA"],
StrictStr,
]
Loading