8000 Merge branch 'main' into feich/initExpression · mmtrucefacts/botbuilder-python@4391530 · GitHub
[go: up one dir, main page]

Skip to content
8000

Commit 4391530

Browse files
authored
Merge branch 'main' into feich/initExpression
2 parents 8ac80ab + ae2a56e commit 4391530

18 files changed

+620
-75
lines changed

libraries/botbuilder-core/botbuilder/core/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
from .telemetry_logger_constants import TelemetryLoggerConstants
4040
from .telemetry_logger_middleware import TelemetryLoggerMiddleware
4141
from .turn_context import TurnContext
42+
from .transcript_logger import TranscriptLogger, TranscriptLoggerMiddleware
4243
from .user_state import UserState
4344
from .register_class_middleware import RegisterClassMiddleware
4445
from .adapter_extensions import AdapterExtensions
@@ -87,6 +88,8 @@
8788
"TelemetryLoggerConstants",
8889
"TelemetryLoggerMiddleware",
8990
"TopIntent",
91+
"TranscriptLogger",
92+
"TranscriptLoggerMiddleware",
9093
"TurnContext",
9194
"UserState",
9295
"UserTokenProvider",

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ async def create_conversation(
225225
type=ActivityTypes.conversation_update,
226226
members_added=[],
227227
members_removed=[],
228+
channel_id=channel_id,
228229
conversation=ConversationAccount(id=str(uuid.uuid4())),
229230
)
230231
context = self.create_turn_context(update)

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
)
4040
from botbuilder.schema import (
4141
Activity,
42+
ActivityEventNames,
4243
ActivityTypes,
4344
ChannelAccount,
4445
ConversationAccount,
@@ -390,7 +391,7 @@ async def create_conversation(
390391

391392
event_activity = Activity(
392393
type=ActivityTypes.event,
393-
name="CreateConversation",
394+
name=ActivityEventNames.create_conversation,
394395
channel_id=channel_id,
395396
service_url=service_url,
396397
id=resource_response.activity_id

libraries/botbuilder-core/botbuilder/core/conversation_reference_extension.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
# Copyright (c) Microsoft Corporation. All rights reserved.
22
# Licensed under the MIT License.
33
import uuid
4-
from botbuilder.schema import Activity, ActivityTypes, ConversationReference
4+
from botbuilder.schema import (
5+
Activity,
6+
ActivityEventNames,
7+
ActivityTypes,
8+
ConversationReference,
9+
)
510

611

712
def get_continuation_activity(reference: ConversationReference) -> Activity:
813
return Activity(
914
type=ActivityTypes.event,
10-
name="ContinueConversation",
15+
name=ActivityEventNames.continue_conversation,
1116
id=str(uuid.uuid1()),
1217
channel_id=reference.channel_id,
1318
service_url=reference.service_url,

libraries/botbuilder-core/botbuilder/core/memory_transcript_store.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from botbuilder.schema import Activity
77
from .transcript_logger import PagedResult, TranscriptInfo, TranscriptStore
88

9+
910
# pylint: disable=line-too-long
1011
class MemoryTranscriptStore(TranscriptStore):
1112
"""This provider is most useful for simulating production storage when running locally against the
@@ -59,7 +60,9 @@ async def get_transcript_activities(
5960
[
6061
x
6162
for x in sorted(
62-
transcript, key=lambda x: x.timestamp, reverse=False
63+
transcript,
64+
key=lambda x: x.timestamp or str(datetime.datetime.min),
65+
reverse=False,
6366
)
6467
if x.timestamp >= start_date
6568
]
@@ -72,9 +75,11 @@ async def get_transcript_activities(
7275
paged_result.items = [
7376
x
7477
for x in sorted(
75-
transcript, key=lambda x: x.timestamp, reverse=False
78+
transcript,
79+
key=lambda x: x.timestamp or datetime.datetime.min,
80+
reverse=False,
7681
)
77-
if x.timestamp >= start_date
82+
if (x.timestamp or datetime.datetime.min) >= start_date
7883
][:20]
7984
if paged_result.items.count == 20:
8085
paged_result.continuation_token = paged_result.items[-1].id

libraries/botbuilder-core/botbuilder/core/skills/skill_handler.py

Lines changed: 76 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ async def on_send_to_conversation(
6262
This method allows you to send an activity to the end of a conversation.
6363
6464
This is slightly different from ReplyToActivity().
65-
* SendToConversation(conversationId) - will append the activity to the end
65+
* SendToConversation(conversation_id) - will append the activity to the end
6666
of the conversation according to the timestamp or semantics of the channel.
67-
* ReplyToActivity(conversationId,ActivityId) - adds the activity as a reply
67+
* ReplyToActivity(conversation_id,ActivityId) - adds the activity as a reply
6868
to another activity, if the channel supports it. If the channel does not
6969
support nested replies, ReplyToActivity falls back to SendToConversation.
7070
@@ -97,9 +97,9 @@ async def on_reply_to_activity(
9797
This method allows you to reply to an activity.
9898
9999
This is slightly different from SendToConversation().
100-
* SendToConversation(conversationId) - will append the activity to the end
100+
* SendToConversation(conversation_id) - will append the activity to the end
101101
of the conversation according to the timestamp or semantics of the channel.
102-
* ReplyToActivity(conversationId,ActivityId) - adds the activity as a reply
102+
* ReplyToActivity(conversation_id,ActivityId) - adds the activity as a reply
103103
to another activity, if the channel supports it. If the channel does not
104104
support nested replies, ReplyToActivity falls back to SendToConversation.
105105
@@ -111,6 +111,8 @@ async def on_reply_to_activity(
111111
:type claims_identity: :class:`botframework.connector.auth.ClaimsIdentity`
112112
:param conversation_id:The conversation ID.
113113
:type conversation_id: str
114+
:param activity_id: Activity ID to send.
115+
:type activity_id: str
114116
:param activity: Activity to send.
115117
:type activity: Activity
116118
:return:
@@ -119,13 +121,66 @@ async def on_reply_to_activity(
119121
claims_identity, conversation_id, activity_id, activity,
120122
)
121123

122-
async def _process_activity(
124+
async def on_delete_activity(
125+
self, claims_identity: ClaimsIdentity, conversation_id: str, activity_id: str
126+
):
127+
skill_conversation_reference = await self._get_skill_conversation_reference(
128+
conversation_id
129+
)
130+
131+
async def callback(turn_context: TurnContext):
132+
turn_context.turn_state[
133+
self.SKILL_CONVERSATION_REFERENCE_KEY
134+
] = skill_conversation_reference
135+
await turn_context.delete_activity(activity_id)
136+
137+
await self._adapter.continue_conversation(
138+
skill_conversation_reference.conversation_reference,
139+
callback,
140+
claims_identity=claims_identity,
141+
audience=skill_conversation_reference.oauth_scope,
142+
)
143+
144+
async def on_update_activity(
123145
self,
124146
claims_identity: ClaimsIdentity,
125147
conversation_id: str,
126-
reply_to_activity_id: str,
148+
activity_id: str,
127149
activity: Activity,
128150
) -> ResourceResponse:
151+
skill_conversation_reference = await self._get_skill_conversation_reference(
152+
conversation_id
153+
)
154+
155+
resource_response: ResourceResponse = None
156+
157+
async def callback(turn_context: TurnContext):
158+
nonlocal resource_response
159+
turn_context.turn_state[
160+
self.SKILL_CONVERSATION_REFERENCE_KEY
161+
] = skill_conversation_reference
162+
activity.apply_conversation_reference(
163+
skill_conversation_reference.conversation_reference
164+
)
165+
turn_context.activity.id = activity_id
166+
turn_context.activity.caller_id = (
167+
f"{CallerIdConstants.bot_to_bot_prefix}"
168+
f"{JwtTokenValidation.get_app_id_from_claims(claims_identity.claims)}"
169+
)
170+
resource_response = await turn_context.update_activity(activity)
171+
172+
await self._adapter.continue_conversation(
173+
skill_conversation_reference.conversation_reference,
174+
callback,
175+
claims_identity=claims_identity,
176+
audience=skill_conversation_reference.oauth_scope,
177+
)
178+
179+
return resource_response or ResourceResponse(id=str(uuid4()).replace("-", ""))
180+
181+
async def _get_skill_conversation_reference(
182+
self, conversation_id: str
183+
) -> SkillConversationReference:
129184
# Get the SkillsConversationReference
130185
conversation_reference_result = await self._conversation_id_factory.get_conversation_reference(
131186
conversation_id
@@ -135,11 +190,10 @@ async def _process_activity(
135190
# or a ConversationReference (the old way, but still here for compatibility). If a
136191
# ConversationReference is returned, build a new SkillConversationReference to simplify
137192
# the remainder of this method.
138-
skill_conversation_reference: SkillConversationReference = None
139193
if isinstance(conversation_reference_result, SkillConversationReference):
140-
skill_conversation_reference = conversation_reference_result
194+
skill_conversation_reference: SkillConversationReference = conversation_reference_result
141195
else:
142-
skill_conversation_reference = SkillConversationReference(
196+
skill_conversation_reference: SkillConversationReference = SkillConversationReference(
143197
conversation_reference=conversation_reference_result,
144198
oauth_scope=(
145199
GovernmentConstants.TO_CHANNEL_FROM_BOT_OAUTH_SCOPE
@@ -154,6 +208,19 @@ async def _process_activity(
154208
if not skill_conversation_reference.conversation_reference:
155209
raise KeyError("conversationReference not found")
156210

211+
return skill_conversation_reference
212+
213+
async def _process_activity(
214+
self,
215+
claims_identity: ClaimsIdentity,
216+
conversation_id: str,
217+
reply_to_activity_id: str,
218+
activity: Activity,
219+
) -> ResourceResponse:
220+
skill_conversation_reference = await self._get_skill_conversation_reference(
221+
conversation_id
222+
)
223+
157224
# If an activity is sent, return the ResourceResponse
158225
resource_response: ResourceResponse = None
159226

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

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,22 @@
22
# Licensed under the MIT License.
33

44
from botbuilder.schema import Activity
5-
from botbuilder.schema.teams import NotificationInfo, TeamsChannelData, TeamInfo
5+
from botbuilder.schema.teams import (
6+
NotificationInfo,
7+
TeamsChannelData,
8+
TeamInfo,
9+
TeamsMeetingInfo,
10+
)
11+
12+
13+
def teams_get_channel_data(activity: Activity) -> TeamsChannelData:
14+
if not activity:
15+
return None
16+
17+
if activity.channel_data:
18+
return TeamsChannelData().deserialize(activity.channel_data)
19+
20+
return None
621

722

823
def teams_get_channel_id(activity: Activity) -> str:
@@ -41,3 +56,14 @@ def teams_notify_user(
4156
channel_data.notification.alert_in_meeting = alert_in_meeting
4257
channel_data.notification.external_resource_url = external_resource_url
4358
activity.channel_data = channel_data
59+
60+
61+
def teams_get_meeting_info(activity: Activity) -> TeamsMeetingInfo:
62+
if not activity:
63+
return None
64+
65+
if activity.channel_data:
66+
channel_data = TeamsChannelData().deserialize(activity.channel_data)
67+
return channel_data.meeting
68+
69+
return None

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

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,23 @@
22
# Licensed under the MIT License.
33

44
from typing import List, Tuple
5+
6+
from botframework.connector.aio import ConnectorClient
7+
from botframework.connector.teams.teams_connector_client import TeamsConnectorClient
58
from botbuilder.schema import ConversationParameters, ConversationReference
9+
from botbuilder.core.teams.teams_activity_extensions import (
10+
teams_get_meeting_info,
11+
teams_get_channel_data,
12+
)
613
from botbuilder.core.turn_context import Activity, TurnContext
714
from botbuilder.schema.teams import (
815
ChannelInfo,
916
TeamDetails,
1017
TeamsChannelData,
1118
TeamsChannelAccount,
1219
TeamsPagedMembersResult,
20+
TeamsMeetingParticipant,
1321
)
14-
from botframework.connector.aio import ConnectorClient
15-
from botframework.connector.teams.teams_connector_client import TeamsConnectorClient
1622

1723

1824
class TeamsInfo:
@@ -177,6 +183,48 @@ async def get_member(
177183

178184
return await TeamsInfo.get_team_member(turn_context, team_id, member_id)
179185

186+
@staticmethod
187+
async def get_meeting_participant(
188+
turn_context: TurnContext,
189+
meeting_id: str = None,
190+
participant_id: str = None,
191+
tenant_id: str = None,
192+
) -> TeamsMeetingParticipant:
193+
meeting_id = (
194+
meeting_id
195+
if meeting_id
196+
else teams_get_meeting_info(turn_context.activity).id
197+
)
198+
if meeting_id is None:
199+
raise TypeError(
200+
"TeamsInfo._get_meeting_participant: method requires a meeting_id"
201+
)
202+
203+
participant_id = (
204+
participant_id
205+
if participant_id
206+
else turn_context.activity.from_property.aad_object_id
207+
)
208+
if participant_id is None:
209+
raise TypeError(
210+
"TeamsInfo._get_meeting_participant: method requires a participant_id"
211+
)
212+
213+
tenant_id = (
214+
tenant_id
215+
if tenant_id
216+
else teams_get_channel_data(turn_context.activity).tenant.id
217+
)
218+
if tenant_id is None:
219+
raise TypeError(
220+
"TeamsInfo._get_meeting_participant: method requires a tenant_id"
221+
)
222+
223+
connector_client = await TeamsInfo.get_teams_connector_client(turn_context)
224+
return connector_client.teams.fetch_participant(
225+
meeting_id, participant_id, tenant_id
226+
)
227+
180228
@staticmethod
181229
async def get_teams_connector_client(
182230
turn_context: TurnContext,

libraries/botbuilder-core/botbuilder/core/transcript_logger.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@
99
from queue import Queue
1010
from abc import ABC, abstractmethod
1111
from typing import Awaitable, Callable, List
12-
from botbuilder.schema import Activity, ActivityTypes, ConversationReference
12+
from botbuilder.schema import (
13+
Activity,
14+
ActivityEventNames,
15+
ActivityTypes,
16+
ChannelAccount,
17+
ConversationReference,
18+
)
1319
from .middleware_set import Middleware
1420
from .turn_context import TurnContext
1521

@@ -46,9 +52,17 @@ async def on_turn(
4652
activity = context.activity
4753
# Log incoming activity at beginning of turn
4854
if activity:
55+
if not activity.from_property:
56+
activity.from_property = ChannelAccount()
4957
if not activity.from_property.role:
5058
activity.from_property.role = "user"
51-
await self.log_activity(transcript, copy.copy(activity))
59+
60+
# We should not log ContinueConversation events used by skills to initialize the middleware.
61+
if not (
62+
context.activity.type == ActivityTypes.event
63+
and context.activity.name == ActivityEventNames.continue_conversation
64+
):
65+
await self.log_activity(transcript, copy.copy(activity))
5266

5367
# hook up onSend pipeline
5468
# pylint: disable=unused-argument

0 commit comments

Comments
 (0)
0