8000 updated teams info for cloud adapter (#1776) · azclouddev001/botbuilder-python@8e71426 · GitHub
[go: up one dir, main page]

Skip to content

Commit 8e71426

Browse files
axelsrztracyboehrerTracy Boehrer
authored
updated teams info for cloud adapter (microsoft#1776)
* updated teams info for cloud adapter * black corrections --------- Co-authored-by: tracyboehrer <tracyboehrer@users.noreply.github.com> Co-authored-by: Tracy Boehrer <trboehre@microsoft.com>
1 parent ab92acc commit 8e71426

File tree

7 files changed

+202
-11
lines changed

7 files changed

+202
-11
lines changed

libraries/botbuilder-core/botbuilder/core/adapters/test_adapter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ async def continue_conversation(
217217
reference, callback, bot_id, claims_identity, audience
218218
)
219219

220-
async def create_conversation(
220+
async def create_conversation( # pylint: disable=arguments-differ
221221
self, channel_id: str, callback: Callable # pylint: disable=unused-argument
222222
):
223223
self.activity_buffer.clear()

libraries/botbuilder-core/botbuilder/core/bot_adapter.py

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@
33

44
from abc import ABC, abstractmethod
55
from typing import List, Callable, Awaitable
6-
from botbuilder.schema import Activity, ConversationReference, ResourceResponse
7-
from botframework.connector.auth import ClaimsIdentity
6+
from botbuilder.schema import (
7+
Activity,
8+
ConversationReference,
9+
ConversationParameters,
10+
ResourceResponse,
11+
)
12+
from botframework.connector.auth import AppCredentials, ClaimsIdentity
813

914
from . import conversation_reference_extension
1015
from .bot_assert import BotAssert
@@ -108,6 +113,47 @@ async def continue_conversation(
108113
)
109114
return await self.run_pipeline(context, callback)
110115

116+
async def create_conversation(
117+
self,
118+
reference: ConversationReference,
119+
logic: Callable[[TurnContext], Awaitable] = None,
120+
conversation_parameters: ConversationParameters = None,
121+
channel_id: str = None,
122+
service_url: str = None,
123+
credentials: AppCredentials = None,
124+
):
125+
"""
126+
Starts a new conversation with a user. Used to direct message to a member of a group.
127+
128+
:param reference: The conversation reference that contains the tenant
129+
:type reference: :class:`botbuilder.schema.ConversationReference`
130+
:param logic: The logic to use for the creation of the conversation
131+
:type logic: :class:`typing.Callable`
132+
:param conversation_parameters: The information to use to create the conversation
133+
:type conversation_parameters:
134+
:param channel_id: The ID for the channel.
135+
:type channel_id: :class:`typing.str`
136+
:param service_url: The channel's service URL endpoint.
137+
:type service_url: :class:`typing.str`
138+
:param credentials: The application credentials for the bot.
139+
:type credentials: :class:`botframework.connector.auth.AppCredentials`
140+
141+
:raises: It raises a generic exception error.
142+
143+
:return: A task representing the work queued to execute.
144+
145+
.. remarks::
146+
To start a conversation, your bot must know its account information and the user's
147+
account information on that channel.
148+
Most channels only support initiating a direct message (non-group) conversation.
149+
The adapter attempts to create a new conversation on the channel, and
150+
then sends a conversation update activity through its middleware pipeline
151+
to the the callback method.
152+
If the conversation is established with the specified users, the ID of the activity
153+
will contain the ID of the new conversation.
154+
"""
155+
raise Exception("Not Implemented")
156+
111157
async def run_pipeline(
112158
self, context: TurnContext, callback: Callable[[TurnContext], Awaitable] = None
113159
):

libraries/botbuilder-core/botbuilder/core/bot_framework_adapter.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,11 @@ async def create_conversation(
363363
)
364364

365365
# Mix in the tenant ID if specified. This is required for MS Teams.
366-
if reference.conversation and reference.conversation.tenant_id:
366+
if (
367+
reference
368+
and reference.conversation
369+
and reference.conversation.tenant_id
370+
):
367371
# Putting tenant_id in channel_data is a temporary while we wait for the Teams API to be updated
368372
if parameters.channel_data is None:
369373
parameters.channel_data = {}

libraries/botbuilder-core/botbuilder/core/cloud_adapter_base.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,18 @@
66
from copy import Error
77
from http import HTTPStatus
88
from typing import Awaitable, Callable, List, Union
9+
from uuid import uuid4
910

1011
from botbuilder.core.invoke_response import InvokeResponse
1112

1213
from botbuilder.schema import (
1314
Activity,
15+
ActivityEventNames,
1416
ActivityTypes,
17+
ConversationAccount,
1518
ConversationReference,
19+
ConversationResourceResponse,
20+
ConversationParameters,
1621
DeliveryModes,
1722
ExpectedReplies,
1823
ResourceResponse,
@@ -175,6 +180,71 @@ async def continue_conversation_with_claims(
175180
claims_identity, get_continuation_activity(reference), audience, logic
176181
)
177182

183+
async def create_conversation( # pylint: disable=arguments-differ
184+
self,
185+
bot_app_id: ConversationReference,
186+
callback: Callable[[TurnContext], Awaitable] = None,
187+
conversation_parameters: ConversationParameters = None,
188+
channel_id: str = None,
189+
service_url: str = None,
190+
audience: str = None,
191+
):
192+
if not service_url:
193+
raise TypeError(
194+
"CloudAdapter.create_conversation(): service_url is required."
195+
)
196+
if not conversation_parameters:
197+
raise TypeError(
198+
"CloudAdapter.create_conversation(): conversation_parameters is required."
199+
)
200+
if not callback:
201+
raise TypeError("CloudAdapter.create_conversation(): callback is required.")
202+
203+
# Create a ClaimsIdentity, to create the connector and for adding to the turn context.
204+
claims_identity = self.create_claims_identity(bot_app_id)
205+
claims_identity.claims[AuthenticationConstants.SERVICE_URL_CLAIM] = service_url
206+
207+
# create the connectror factory
208+
connector_factory = self.bot_framework_authentication.create_connector_factory(
209+
claims_identity
210+
)
211+
212+
# Create the connector client to use for outbound requests.
213+
connector_client = await connector_factory.create(service_url, audience)
214+
215+
# Make the actual create conversation call using the connector.
216+
create_conversation_result = (
217+
await connector_client.conversations.create_conversation(
218+
conversation_parameters
219+
)
220+
)
221+
222+
# Create the create activity to communicate the results to the application.
223+
create_activity = self._create_create_activity(
224+
create_conversation_result, channel_id, service_url, conversation_parameters
225+
)
226+
227+
# Create a UserTokenClient instance for the application to use. (For example, in the OAuthPrompt.)
228+
user_token_client = (
229+
await self.bot_framework_authentication.create_user_token_client(
230+
claims_identity
231+
)
232+
)
233+
234+
# Create a turn context and run the pipeline.
235+
context = self._create_turn_context(
236+
create_activity,
237+
claims_identity,
238+
None,
239+
connector_client,
240+
user_token_client,
241+
callback,
242+
connector_factory,
243+
)
244+
245+
# Run the pipeline
246+
await self.run_pipeline(context, callback)
247+
178248
async def process_proactive(
179249
self,
180250
claims_identity: ClaimsIdentity,
@@ -301,6 +371,28 @@ def create_claims_identity(self, bot_app_id: str = "") -> ClaimsIdentity:
301371
True,
302372
)
303373

374+
def _create_create_activity(
375+
self,
376+
create_conversation_result: ConversationResourceResponse,
377+
channel_id: str,
378+
service_url: str,
379+
conversation_parameters: ConversationParameters,
380+
) -> Activity:
381+
# Create a conversation update activity to represent the result.
382+
activity = Activity.create_event_activity()
383+
activity.name = ActivityEventNames.create_conversation
384+
activity.channel_id = channel_id
385+
activity.service_url = service_url
386+
activity.id = create_conversation_result.activity_id or str(uuid4())
387+
activity.conversation = ConversationAccount(
388+
id=create_conversation_result.id,
389+
tenant_id=conversation_parameters.tenant_id,
390+
)
391+
activity.channel_data = conversation_parameters.channel_data
392+
activity.recipient = conversation_parameters.bot
393+
394+
return activity
395+
304396
def _create_turn_context(
305397
self,
306398
activity: Activity,

libraries/botbuilder-core/botbuilder/core/teams/teams_info.py

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33

44
from typing import List, Tuple
55

6+
from botframework.connector import Channels
67
from botframework.connector.aio import ConnectorClient
7-
from botframework.connector.teams.teams_connector_client import TeamsConnectorClient
8-
from botbuilder.schema import ConversationParameters, ConversationReference
8+
from botframework.connector.teams import TeamsConnectorClient
99
from botbuilder.core.teams.teams_activity_extensions import (
1010
teams_get_meeting_info,
1111
teams_get_channel_data,
1212
)
13-
from botbuilder.core.turn_context import Activity, TurnContext
13+
from botbuilder.core import CloudAdapterBase, BotFrameworkAdapter, TurnContext
14+
from botbuilder.schema import Activity, ConversationParameters, ConversationReference
1415
from botbuilder.schema.teams import (
1516
ChannelInfo,
1617
MeetingInfo,
@@ -25,23 +26,71 @@
2526
class TeamsInfo:
2627
@staticmethod
2728
async def send_message_to_teams_channel(
28-
turn_context: TurnContext, activity: Activity, teams_channel_id: str
29+
turn_context: TurnContext,
30+
activity: Activity,
31+
teams_channel_id: str,
32+
*,
33+
bot_app_id: str = None,
2934
) -> Tuple[ConversationReference, str]:
3035
if not turn_context:
3136
raise ValueError("The turn_context cannot be None")
37+
if not turn_context.activity:
38+
raise ValueError("The activity inside turn context cannot be None")
3239
if not activity:
3340
raise ValueError("The activity cannot be None")
3441
if not teams_channel_id:
3542
raise ValueError("The teams_channel_id cannot be None or empty")
3643

44+
if not bot_app_id:
45+
return await TeamsInfo._legacy_send_message_to_teams_channel(
46+
turn_context, activity, teams_channel_id
47+
)
48+
49+
conversation_reference: ConversationReference = None
50+
new_activity_id = ""
51+
service_url = turn_context.activity.service_url
52+
conversation_parameters = ConversationParameters(
53+
is_group=True,
54+
channel_data=TeamsChannelData(channel=ChannelInfo(id=teams_channel_id)),
55+
activity=activity,
56+
)
57+
58+
async def aux_callback(
59+
new_turn_context,
60+
):
61+
nonlocal new_activity_id
62+
nonlocal conversation_reference
63+
new_activity_id = new_turn_context.activity.id
64+
conversation_reference = TurnContext.get_conversation_reference(
65+
new_turn_context.activity
66+
)
67+
68+
adapter: CloudAdapterBase = turn_context.adapter
69+
await adapter.create_conversation(
70+
bot_app_id,
71+
aux_callback,
72+
conversation_parameters,
73+
Channels.ms_teams,
74+
service_url,
75+
None,
76+
)
77+
78+
return (conversation_reference, new_activity_id)
79+
80+
@staticmethod
81+
async def _legacy_send_message_to_teams_channel(
82+
turn_context: TurnContext, activity: Activity, teams_channel_id: str
83+
) -> Tuple[ConversationReference, str]:
3784
old_ref = TurnContext.get_conversation_reference(turn_context.activity)
3885
conversation_parameters = ConversationParameters(
3986
is_group=True,
4087
channel_data={"channel": {"id": teams_channel_id}},
4188
activity=activity,
4289
)
4390

44-
result = await turn_context.adapter.create_conversation(
91+
# if this version of the method is called the adapter probably wont be CloudAdapter
92+
adapter: BotFrameworkAdapter = turn_context.adapter
93+
result = await adapter.create_conversation(
4594
old_ref, TeamsInfo._create_conversation_callback, conversation_parameters
4695
)
4796
return (result[0], result[1])

libraries/botbuilder-core/tests/simple_adapter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ async def send_activities(
5959

6060
return responses
6161

62-
async def create_conversation(
62+
async def create_conversation( # pylint: disable=arguments-differ
6363
self,
6464
reference: ConversationReference,
6565
logic: Callable[[TurnContext], Awaitable] = None,

libraries/botbuilder-core/tests/teams/simple_adapter_with_create_conversation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ async def send_activities(
5858

5959
return responses
6060

61-
async def create_conversation(
61+
async def create_conversation( # pylint: disable=arguments-differ
6262
self,
6363
reference: ConversationReference,
6464
logic: Callable[[TurnContext], Awaitable] = None,

0 commit comments

Comments
 (0)
0