From 9cee2f32fccf0a48af1ae0d429125a15c9ca9443 Mon Sep 17 00:00:00 2001 From: Axel Suarez Date: Wed, 28 Oct 2020 10:47:39 -0700 Subject: [PATCH] Invoke with expected replies --- .../botbuilder/core/turn_context.py | 9 +++++++++ .../botbuilder/dialogs/skills/skill_dialog.py | 14 +++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/libraries/botbuilder-core/botbuilder/core/turn_context.py b/libraries/botbuilder-core/botbuilder/core/turn_context.py index 9f719363e..b8799a02b 100644 --- a/libraries/botbuilder-core/botbuilder/core/turn_context.py +++ b/libraries/botbuilder-core/botbuilder/core/turn_context.py @@ -18,6 +18,10 @@ class TurnContext: + + # Same constant as in the BF Adapter, duplicating here to avoid circular dependency + _INVOKE_RESPONSE_KEY = "BotFrameworkAdapter.InvokeResponse" + def __init__(self, adapter_or_context, request: Activity = None): """ Creates a new TurnContext instance. @@ -202,6 +206,11 @@ async def logic(): responses = [] for activity in output: self.buffered_reply_activities.append(activity) + # Ensure the TurnState has the InvokeResponseKey, since this activity + # is not being sent through the adapter, where it would be added to TurnState. + if activity.type == ActivityTypes.invoke_response: + self.turn_state[TurnContext._INVOKE_RESPONSE_KEY] = activity + responses.append(ResourceResponse()) if sent_non_trace_activity: diff --git a/libraries/botbuilder-dialogs/botbuilder/dialogs/skills/skill_dialog.py b/libraries/botbuilder-dialogs/botbuilder/dialogs/skills/skill_dialog.py index 62fee1ace..119d1d62a 100644 --- a/libraries/botbuilder-dialogs/botbuilder/dialogs/skills/skill_dialog.py +++ b/libraries/botbuilder-dialogs/botbuilder/dialogs/skills/skill_dialog.py @@ -244,6 +244,8 @@ async def _send_to_skill( # Process replies in the response.Body. response.body: List[Activity] response.body = ExpectedReplies().deserialize(response.body).activities + # Track sent invoke responses, so more than one is not sent. + sent_invoke_response = False for from_skill_activity in response.body: if from_skill_activity.type == ActivityTypes.end_of_conversation: @@ -254,12 +256,18 @@ async def _send_to_skill( await self.dialog_options.conversation_id_factory.delete_conversation_reference( skill_conversation_id ) - elif await self._intercept_oauth_cards( + elif not sent_invoke_response and await self._intercept_oauth_cards( context, from_skill_activity, self.dialog_options.connection_name ): - # do nothing. Token exchange succeeded, so no oauthcard needs to be shown to the user - pass + # Token exchange succeeded, so no oauthcard needs to be shown to the user + sent_invoke_response = True else: + # If an invoke response has already been sent we should ignore future invoke responses as this + # represents a bug in the skill. + if from_skill_activity.type == ActivityTypes.invoke_response: + if sent_invoke_response: + continue + sent_invoke_response = True # Send the response back to the channel. await context.send_activity(from_skill_activity)