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 packages/api/src/microsoft_teams/api/clients/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"""

from . import bot, conversation, meeting, reaction, team, user
from ._auth_provider_interceptor import AuthProvider
from .api_client import ApiClient
from .api_client_settings import ApiClientSettings, merge_api_client_settings
from .bot import * # noqa: F403
Expand All @@ -17,6 +18,7 @@
__all__: list[str] = [
"ApiClient",
"ApiClientSettings",
"AuthProvider",
"merge_api_client_settings",
]
__all__.extend(bot.__all__)
Expand Down
11 changes: 9 additions & 2 deletions packages/api/src/microsoft_teams/api/clients/base_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Licensed under the MIT License.
"""

from typing import Optional, Union
from typing import Optional, Union, cast

from microsoft_teams.common import Client, ClientOptions

Expand All @@ -21,7 +21,7 @@ def __init__(
"""Initialize the BaseClient.

Args:
options: Optional Client or ClientOptions instance. If not provided, a default Client will be created.
options: Optional Client or ClientOptions instance. If not provided, a default Client is created.
api_client_settings: Optional API client settings.
"""
if options is None:
Expand All @@ -42,3 +42,10 @@ def http(self) -> Client:
def http(self, value: Client) -> None:
"""Set the HTTP client instance."""
self._http = value

def _get_service_url(self, service_url: str | None = None) -> str:
current_service_url = cast(str | None, getattr(self, "service_url", None))
resolved_service_url = service_url or current_service_url
if resolved_service_url is None:
raise ValueError("service_url is required")
return resolved_service_url.rstrip("/")
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
from microsoft_teams.common.http import Client

from ...activities import ActivityParams, SentActivity
from ...models import TeamsChannelAccount
from ...models import AgenticIdentity, TeamsChannelAccount
from .._auth_provider_interceptor import AGENTIC_IDENTITY_EXTENSION
from ..api_client_settings import ApiClientSettings
from ..base_client import BaseClient

Expand Down Expand Up @@ -38,7 +39,14 @@ def __init__(
super().__init__(http_client, api_client_settings)
self.service_url = service_url.rstrip("/")

async def create(self, conversation_id: str, activity: ActivityParams) -> SentActivity:
async def create(
self,
conversation_id: str,
activity: ActivityParams,
*,
service_url: str | None = None,
agentic_identity: AgenticIdentity | None = None,
) -> SentActivity:
"""
Create a new activity in a conversation.

Expand All @@ -51,8 +59,9 @@ async def create(self, conversation_id: str, activity: ActivityParams) -> SentAc
"""

response = await self.http.post(
f"{self.service_url}/v3/conversations/{conversation_id}/activities",
f"{self._get_service_url(service_url)}/v3/conversations/{conversation_id}/activities",
json=activity.model_dump(by_alias=True, exclude_none=True),
extensions={AGENTIC_IDENTITY_EXTENSION: agentic_identity},
)

# Note: Typing activities (non-streaming) always produce empty responses.
Expand All @@ -61,7 +70,15 @@ async def create(self, conversation_id: str, activity: ActivityParams) -> SentAc
id = response.json().get("id", _PLACEHOLDER_ACTIVITY_ID)
return SentActivity(id=id, activity_params=activity)

async def update(self, conversation_id: str, activity_id: str, activity: ActivityParams) -> SentActivity:
async def update(
self,
conversation_id: str,
activity_id: str,
activity: ActivityParams,
*,
service_url: str | None = None,
agentic_identity: AgenticIdentity | None = None,
) -> SentActivity:
"""
Update an existing activity in a conversation.

Expand All @@ -74,13 +91,22 @@ async def update(self, conversation_id: str, activity_id: str, activity: Activit
The updated activity
"""
response = await self.http.put(
f"{self.service_url}/v3/conversations/{conversation_id}/activities/{activity_id}",
f"{self._get_service_url(service_url)}/v3/conversations/{conversation_id}/activities/{activity_id}",
json=activity.model_dump(by_alias=True, exclude_none=True),
extensions={AGENTIC_IDENTITY_EXTENSION: agentic_identity},
)
id = response.json()["id"]
return SentActivity(id=id, activity_params=activity)

async def reply(self, conversation_id: str, activity_id: str, activity: ActivityParams) -> SentActivity:
async def reply(
self,
conversation_id: str,
activity_id: str,
activity: ActivityParams,
*,
service_url: str | None = None,
agentic_identity: AgenticIdentity | None = None,
) -> SentActivity:
"""
Reply to an activity in a conversation.

Expand All @@ -95,23 +121,41 @@ async def reply(self, conversation_id: str, activity_id: str, activity: Activity
activity_json = activity.model_dump(by_alias=True, exclude_none=True)
activity_json["replyToId"] = activity_id
response = await self.http.post(
f"{self.service_url}/v3/conversations/{conversation_id}/activities/{activity_id}",
f"{self._get_service_url(service_url)}/v3/conversations/{conversation_id}/activities/{activity_id}",
json=activity_json,
extensions={AGENTIC_IDENTITY_EXTENSION: agentic_identity},
)
id = response.json()["id"]
return SentActivity(id=id, activity_params=activity)

async def delete(self, conversation_id: str, activity_id: str) -> None:
async def delete(
self,
conversation_id: str,
activity_id: str,
*,
service_url: str | None = None,
agentic_identity: AgenticIdentity | None = None,
) -> 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}")
await self.http.delete(
f"{self._get_service_url(service_url)}/v3/conversations/{conversation_id}/activities/{activity_id}",
extensions={AGENTIC_IDENTITY_EXTENSION: agentic_identity},
)

async def get_members(self, conversation_id: str, activity_id: str) -> List[TeamsChannelAccount]:
async def get_members(
self,
conversation_id: str,
activity_id: str,
*,
service_url: str | None = None,
agentic_identity: AgenticIdentity | None = None,
) -> List[TeamsChannelAccount]:
"""
Get the members associated with an activity.

Expand All @@ -123,12 +167,19 @@ async def get_members(self, conversation_id: str, activity_id: str) -> List[Team
List of TeamsChannelAccount objects representing the activity members
"""
response = await self.http.get(
f"{self.service_url}/v3/conversations/{conversation_id}/activities/{activity_id}/members"
f"{self._get_service_url(service_url)}/v3/conversations/{conversation_id}/activities/{activity_id}/members",
extensions={AGENTIC_IDENTITY_EXTENSION: agentic_identity},
)
return [TeamsChannelAccount.model_validate(member) for member in response.json()]

@experimental("ExperimentalTeamsTargeted")
async def create_targeted(self, conversation_id: str, activity: ActivityParams) -> SentActivity:
async def create_targeted(
self,
conversation_id: str,
activity: ActivityParams,
*,
service_url: str | None = None,
) -> SentActivity:
"""
Create a new targeted activity in a conversation.

Expand All @@ -146,14 +197,21 @@ async def create_targeted(self, conversation_id: str, activity: ActivityParams)
The created activity
"""
response = await self.http.post(
f"{self.service_url}/v3/conversations/{conversation_id}/activities?isTargetedActivity=true",
f"{self._get_service_url(service_url)}/v3/conversations/{conversation_id}/activities?isTargetedActivity=true",
json=activity.model_dump(by_alias=True, exclude_none=True),
)
Comment thread
heyitsaamir marked this conversation as resolved.
id = response.json().get("id", _PLACEHOLDER_ACTIVITY_ID)
return SentActivity(id=id, activity_params=activity)

@experimental("ExperimentalTeamsTargeted")
async def update_targeted(self, conversation_id: str, activity_id: str, activity: ActivityParams) -> SentActivity:
async def update_targeted(
self,
conversation_id: str,
activity_id: str,
activity: ActivityParams,
*,
service_url: str | None = None,
) -> SentActivity:
"""
Update an existing targeted activity in a conversation.

Expand All @@ -170,14 +228,20 @@ async def update_targeted(self, conversation_id: str, activity_id: str, activity
The updated activity
"""
response = await self.http.put(
f"{self.service_url}/v3/conversations/{conversation_id}/activities/{activity_id}?isTargetedActivity=true",
f"{self._get_service_url(service_url)}/v3/conversations/{conversation_id}/activities/{activity_id}?isTargetedActivity=true",
json=activity.model_dump(by_alias=True, exclude_none=True),
)
id = response.json()["id"]
return SentActivity(id=id, activity_params=activity)

@experimental("ExperimentalTeamsTargeted")
async def delete_targeted(self, conversation_id: str, activity_id: str) -> None:
async def delete_targeted(
self,
conversation_id: str,
activity_id: str,
*,
service_url: str | None = None,
) -> None:
"""
Delete a targeted activity from a conversation.

Expand All @@ -190,5 +254,5 @@ async def delete_targeted(self, conversation_id: str, activity_id: str) -> None:
activity_id: The ID of the activity to delete
"""
await self.http.delete(
f"{self.service_url}/v3/conversations/{conversation_id}/activities/{activity_id}?isTargetedActivity=true"
f"{self._get_service_url(service_url)}/v3/conversations/{conversation_id}/activities/{activity_id}?isTargetedActivity=true"
)
Loading
Loading