From de1aba5c5d07a357e017394fb61e80905a53ad39 Mon Sep 17 00:00:00 2001 From: Axel Suarez Date: Mon, 5 Oct 2020 14:16:13 -0700 Subject: [PATCH] Avoid typing activity if is skill --- .../botbuilder/core/adapters/test_adapter.py | 9 +++-- .../botbuilder/core/show_typing_middleware.py | 18 ++++++++-- .../tests/test_show_typing_middleware.py | 35 +++++++++++++++++-- 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/libraries/botbuilder-core/botbuilder/core/adapters/test_adapter.py b/libraries/botbuilder-core/botbuilder/core/adapters/test_adapter.py index a5637d86c..09ffb3e76 100644 --- a/libraries/botbuilder-core/botbuilder/core/adapters/test_adapter.py +++ b/libraries/botbuilder-core/botbuilder/core/adapters/test_adapter.py @@ -153,7 +153,7 @@ async def process_activity( self._conversation_lock.release() activity.timestamp = activity.timestamp or datetime.utcnow() - await self.run_pipeline(TurnContext(self, activity), logic) + await self.run_pipeline(self.create_turn_context(activity), logic) async def send_activities( self, context, activities: List[Activity] @@ -227,7 +227,7 @@ async def create_conversation( members_removed=[], conversation=ConversationAccount(id=str(uuid.uuid4())), ) - context = TurnContext(self, update) + context = self.create_turn_context(update) return await callback(context) async def receive_activity(self, activity): @@ -252,7 +252,7 @@ async def receive_activity(self, activity): request.id = str(self._next_id) # Create context object and run middleware. - context = TurnContext(self, request) + context = self.create_turn_context(request) return await self.run_pipeline(context, self.logic) def get_next_activity(self) -> Activity: @@ -534,6 +534,9 @@ async def exchange_token_from_credentials( return None + def create_turn_context(self, activity: Activity) -> TurnContext: + return TurnContext(self, activity) + class TestFlow: __test__ = False diff --git a/libraries/botbuilder-core/botbuilder/core/show_typing_middleware.py b/libraries/botbuilder-core/botbuilder/core/show_typing_middleware.py index a659cd8bf..80b353b4f 100644 --- a/libraries/botbuilder-core/botbuilder/core/show_typing_middleware.py +++ b/libraries/botbuilder-core/botbuilder/core/show_typing_middleware.py @@ -4,7 +4,9 @@ from typing import Awaitable, Callable from botbuilder.schema import Activity, ActivityTypes +from botframework.connector.auth import ClaimsIdentity, SkillValidation +from .bot_adapter import BotAdapter from .middleware_set import Middleware from .turn_context import TurnContext @@ -82,9 +84,12 @@ async def aux(): def stop_interval(): timer.set_clear_timer() - # if it's a message, start sending typing activities until the - # bot logic is done. - if context.activity.type == ActivityTypes.message: + # Start a timer to periodically send the typing activity + # (bots running as skills should not send typing activity) + if ( + context.activity.type == ActivityTypes.message + and not ShowTypingMiddleware._is_skill_bot(context) + ): start_interval(context, self._delay, self._period) # call the bot logic @@ -93,3 +98,10 @@ def stop_interval(): stop_interval() return result + + @staticmethod + def _is_skill_bot(context: TurnContext) -> bool: + claims_identity = context.turn_state.get(BotAdapter.BOT_IDENTITY_KEY) + return isinstance( + claims_identity, ClaimsIdentity + ) and SkillValidation.is_skill_claim(claims_identity.claims) diff --git a/libraries/botbuilder-core/tests/test_show_typing_middleware.py b/libraries/botbuilder-core/tests/test_show_typing_middleware.py index b3b10a13d..9d0e0b7ce 100644 --- a/libraries/botbuilder-core/tests/test_show_typing_middleware.py +++ b/libraries/botbuilder-core/tests/test_show_typing_middleware.py @@ -1,11 +1,31 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. import asyncio +from uuid import uuid4 import aiounittest -from botbuilder.core import ShowTypingMiddleware +from botbuilder.core import ShowTypingMiddleware, TurnContext from botbuilder.core.adapters import TestAdapter -from botbuilder.schema import ActivityTypes +from botbuilder.schema import Activity, ActivityTypes +from botframework.connector.auth import AuthenticationConstants, ClaimsIdentity + + +class SkillTestAdapter(TestAdapter): + def create_turn_context(self, activity: Activity) -> TurnContext: + turn_context = super().create_turn_context(activity) + + claims_identity = ClaimsIdentity( + claims={ + AuthenticationConstants.VERSION_CLAIM: "2.0", + AuthenticationConstants.AUDIENCE_CLAIM: str(uuid4()), + AuthenticationConstants.AUTHORIZED_PARTY: str(uuid4()), + }, + is_authenticated=True, + ) + + turn_context.turn_state[self.BOT_IDENTITY_KEY] = claims_identity + + return turn_context class TestShowTypingMiddleware(aiounittest.AsyncTestCase): @@ -65,3 +85,14 @@ def assert_is_message(activity, description): # pylint: disable=unused-argument step1 = await adapter.send("foo") await step1.assert_reply(assert_is_message) + + async def test_not_send_not_send_typing_indicator_when_bot_running_as_skill(self): + async def aux(context): + await asyncio.sleep(1) + await context.send_activity(f"echo:{context.activity.text}") + + skill_adapter = SkillTestAdapter(aux) + skill_adapter.use(ShowTypingMiddleware(0.001, 1)) + + step1 = await skill_adapter.send("foo") + await step1.assert_reply("echo:foo")