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
1 change: 1 addition & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
python_version = 3.12
strict = true
exclude = tests/
no_namespace_packages = true

# from https://blog.wolt.com/engineering/2021/09/30/professional-grade-mypy-configuration/
disallow_untyped_defs = true
Expand Down
8 changes: 7 additions & 1 deletion packages/api/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ authors = [
{ name = "Microsoft", email = "[email protected]" }
]
requires-python = ">=3.12"
dependencies = []
dependencies = [
"pydantic>=2.0.0",
"microsoft-teams-common",
]

[tool.uv.sources]
"microsoft-teams-common" = { workspace = true }

[project.urls]
Homepage = "https://github.com/microsoft/teams.py/tree/main/packages/api/src/microsoft/teams/api"
Expand Down
11 changes: 9 additions & 2 deletions packages/api/src/microsoft/teams/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
Licensed under the MIT License.
"""

from .clients import * # noqa: F403
from .clients import __all__ as clients_all
from .models import * # noqa: F403
from .models import __all__ as models_all

def hello() -> str:
return "Hello from api!"
# Combine all exports from submodules
__all__ = [
*clients_all,
*models_all,
]
11 changes: 11 additions & 0 deletions packages/api/src/microsoft/teams/api/clients/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License.
"""

from .conversation import * # noqa: F403
from .conversation import __all__ as conversation_all

__all__ = [
*conversation_all,
]
30 changes: 30 additions & 0 deletions packages/api/src/microsoft/teams/api/clients/base_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License.
"""

from typing import Optional, Union

from microsoft.teams.common.http import Client, ClientOptions


class BaseClient:
"""Base client"""

def __init__(self, options: Optional[Union[Client, ClientOptions]] = None) -> None:
"""Initialize the BaseClient.

Args:
options: Optional Client or ClientOptions instance. If not provided, a default Client will be created.
"""
self._http = Client(options or ClientOptions())

@property
def http(self) -> Client:
"""Get the HTTP client instance."""
return self._http

@http.setter
def http(self, value: Client) -> None:
"""Set the HTTP client instance."""
self._http = value
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License.
"""

from .activity import ConversationActivityClient
from .client import ConversationClient
from .member import ConversationMemberClient
from .params import CreateConversationParams, GetConversationsParams, GetConversationsResponse

__all__ = [
"ConversationActivityClient",
"ConversationClient",
"ConversationMemberClient",
"CreateConversationParams",
"GetConversationsParams",
"GetConversationsResponse",
]
108 changes: 108 additions & 0 deletions packages/api/src/microsoft/teams/api/clients/conversation/activity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
"""
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License.
"""

from typing import List, Optional

from microsoft.teams.common.http import Client

from ...models import Account, Activity
from ..base_client import BaseClient


class ConversationActivityClient(BaseClient):
"""
Client for managing activities in a Teams conversation.
"""

def __init__(self, service_url: str, http_client: Optional[Client] = None):
"""
Initialize the conversation activity client.

Args:
service_url: The base URL for the Teams service
http_client: Optional HTTP client to use. If not provided, a new one will be created.
"""
super().__init__(http_client)
self.service_url = service_url

async def create(self, conversation_id: str, activity: Activity) -> Activity:
"""
Create a new activity in a conversation.

Args:
conversation_id: The ID of the conversation
activity: The activity to create

Returns:
The created activity
"""
response = await self.http.post(
f"{self.service_url}/v3/conversations/{conversation_id}/activities",
json=activity.model_dump(by_alias=True),
)
return Activity.model_validate(response.json())

async def update(self, conversation_id: str, activity_id: str, activity: Activity) -> Activity:
"""
Update an existing activity in a conversation.

Args:
conversation_id: The ID of the conversation
activity_id: The ID of the activity to update
activity: The updated activity data

Returns:
The updated activity
"""
response = await self.http.put(
f"{self.service_url}/v3/conversations/{conversation_id}/activities/{activity_id}",
json=activity.model_dump(by_alias=True),
)
return Activity.model_validate(response.json())

async def reply(self, conversation_id: str, activity_id: str, activity: Activity) -> Activity:
"""
Reply to an activity in a conversation.

Args:
conversation_id: The ID of the conversation
activity_id: The ID of the activity to reply to
activity: The reply activity

Returns:
The created reply activity
"""
activity.reply_to_id = activity_id
response = await self.http.post(
f"{self.service_url}/v3/conversations/{conversation_id}/activities/{activity_id}",
json=activity.model_dump(by_alias=True),
)
return Activity.model_validate(response.json())

async def delete(self, conversation_id: str, activity_id: str) -> None:
"""
Delete an activity from a conversation.

Args:
conversation_id: The ID of the conversation
activity_id: The ID of the activity to delete
"""
await self.http.delete(f"{self.service_url}/v3/conversations/{conversation_id}/activities/{activity_id}")

async def get_members(self, conversation_id: str, activity_id: str) -> List[Account]:
"""
Get the members associated with an activity.

Args:
conversation_id: The ID of the conversation
activity_id: The ID of the activity

Returns:
List of Account objects representing the activity members
"""
response = await self.http.get(
f"{self.service_url}/v3/conversations/{conversation_id}/activities/{activity_id}/members"
)
return [Account.model_validate(member) for member in response.json()]
131 changes: 131 additions & 0 deletions packages/api/src/microsoft/teams/api/clients/conversation/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
"""
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License.
"""

from typing import Any, Optional, Union

from microsoft.teams.common.http import Client, ClientOptions

from ...models import ConversationResource
from ..base_client import BaseClient
from .activity import ConversationActivityClient
from .member import ConversationMemberClient
from .params import (
CreateConversationParams,
GetConversationsParams,
GetConversationsResponse,
)


class ConversationOperations:
"""Base class for conversation operations."""

def __init__(self, client: "ConversationClient", conversation_id: str) -> None:
self._client = client
self._conversation_id = conversation_id


class ActivityOperations(ConversationOperations):
"""Operations for managing activities in a conversation."""

async def create(self, activity: Any) -> Any:
return await self._client._activities.create(self._conversation_id, activity)

async def update(self, activity_id: str, activity: Any) -> Any:
return await self._client._activities.update(self._conversation_id, activity_id, activity)

async def reply(self, activity_id: str, activity: Any) -> Any:
return await self._client._activities.reply(self._conversation_id, activity_id, activity)

async def delete(self, activity_id: str) -> None:
await self._client._activities.delete(self._conversation_id, activity_id)

async def get_members(self, activity_id: str) -> Any:
return await self._client._activities.get_members(self._conversation_id, activity_id)


class MemberOperations(ConversationOperations):
"""Operations for managing members in a conversation."""

async def get_all(self) -> Any:
return await self._client._members.get(self._conversation_id)

async def get(self, member_id: str) -> Any:
return await self._client._members.get_by_id(self._conversation_id, member_id)

async def delete(self, member_id: str) -> None:
await self._client._members.delete(self._conversation_id, member_id)


class ConversationClient(BaseClient):
"""Client for managing Teams conversations."""

def __init__(self, service_url: str, options: Optional[Union[Client, ClientOptions]] = None) -> None:
"""Initialize the client.

Args:
service_url: The Teams service URL.
options: Either an HTTP client instance or client options. If None, a default client is created.
"""
super().__init__(options)
self.service_url = service_url

self._activities = ConversationActivityClient(service_url, self.http)
self._members = ConversationMemberClient(service_url, self.http)

def activities(self, conversation_id: str) -> ActivityOperations:
"""Get activity operations for a conversation.

Args:
conversation_id: The ID of the conversation.

Returns:
An operations object for managing activities in the conversation.
"""
return ActivityOperations(self, conversation_id)

def members(self, conversation_id: str) -> MemberOperations:
"""Get member operations for a conversation.

Args:
conversation_id: The ID of the conversation.

Returns:
An operations object for managing members in the conversation.
"""
return MemberOperations(self, conversation_id)

async def get(self, params: Optional[GetConversationsParams] = None) -> GetConversationsResponse:
"""Get a list of conversations.

Args:
params: Optional parameters for getting conversations.

Returns:
A response containing the list of conversations and a continuation token.
"""
query_params = {}
if params and params.continuation_token:
query_params["continuationToken"] = params.continuation_token

response = await self.http.get(
f"{self.service_url}/v3/conversations",
params=query_params,
)
return GetConversationsResponse.model_validate(response.json())

async def create(self, params: CreateConversationParams) -> ConversationResource:
"""Create a new conversation.

Args:
params: Parameters for creating the conversation.

Returns:
The created conversation resource.
"""
response = await self.http.post(
f"{self.service_url}/v3/conversations",
json=params.model_dump(by_alias=True),
)
return ConversationResource.model_validate(response.json())
Loading