Пример #1
0
    async def begin_dialog(self, dialog_context: DialogContext, options: object = None):
        """
        Method called when a new dialog has been pushed onto the stack and is being activated.
        :param dialog_context: The dialog context for the current turn of conversation.
        :param options: (Optional) additional argument(s) to pass to the dialog being started.
        """
        dialog_args = self._validate_begin_dialog_args(options)

        await dialog_context.context.send_trace_activity(
            f"{SkillDialog.__name__}.BeginDialogAsync()",
            label=f"Using activity of type: {dialog_args.activity.type}",
        )

        # Create deep clone of the original activity to avoid altering it before forwarding it.
        skill_activity: Activity = deepcopy(dialog_args.activity)

        # Apply conversation reference and common properties from incoming activity before sending.
        TurnContext.apply_conversation_reference(
            skill_activity,
            TurnContext.get_conversation_reference(dialog_context.context.activity),
            is_incoming=True,
        )

        # Store delivery mode in dialog state for later use.
        dialog_context.active_dialog.state[
            self._deliver_mode_state_key
        ] = dialog_args.activity.delivery_mode

        # Create the conversationId and store it in the dialog context state so we can use it later
        skill_conversation_id = await self._create_skill_conversation_id(
            dialog_context.context, dialog_context.context.activity
        )
        dialog_context.active_dialog.state[
            SkillDialog.SKILLCONVERSATIONIDSTATEKEY
        ] = skill_conversation_id

        # Send the activity to the skill.
        eoc_activity = await self._send_to_skill(
            dialog_context.context, skill_activity, skill_conversation_id
        )
        if eoc_activity:
            return await dialog_context.end_dialog(eoc_activity.value)

        return self.end_of_turn
    async def on_turn(self, turn_context: TurnContext,
                      logic: Callable[[TurnContext], Awaitable]):
        """
        Processes an incoming activity.
        :param turn_context:
        :param logic:
        :return:
        """
        translate = await self._should_translate(turn_context)
        if translate and turn_context.activity.type == ActivityTypes.message:
            turn_context.activity.text = await self.translator.translate(
                turn_context.activity.text,
                TranslationSettings.default_language.value)

        async def aux_on_send(context: TurnContext, activities: List[Activity],
                              next_send: Callable):
            user_language = await self.language_preference_accessor.get(
                context, TranslationSettings.default_language.value)
            should_translate = user_language != TranslationSettings.default_language.value

            # Translate messages sent to the user to user language
            if should_translate:
                for activity in activities:
                    await self._translate_message_activity(
                        activity, user_language)

            return await next_send()

        async def aux_on_update(context: TurnContext, activity: Activity,
                                next_update: Callable):
            user_language = await self.language_preference_accessor.get(
                context, TranslationSettings.default_language.value)
            should_translate = user_language != TranslationSettings.default_language.value

            # Translate messages sent to the user to user language
            if should_translate and activity.type == ActivityTypes.message:
                await self._translate_message_activity(activity, user_language)

            return await next_update()

        turn_context.on_send_activities(aux_on_send)
        turn_context.on_update_activity(aux_on_update)

        await logic()
    async def test_update_activity_should_apply_conversation_reference(self):
        activity_id = "activity ID"
        context = TurnContext(SimpleAdapter(), ACTIVITY)
        called = False

        async def update_handler(context, activity, next_handler_coroutine):
            nonlocal called
            called = True
            assert context is not None
            assert activity.id == activity_id
            assert activity.conversation.id == ACTIVITY.conversation.id
            await next_handler_coroutine()

        context.on_update_activity(update_handler)
        new_activity = MessageFactory.text("test text")
        new_activity.id = activity_id
        update_result = await context.update_activity(new_activity)
        assert called is True
        assert update_result.id == activity_id
Пример #4
0
    async def test_send_message_to_teams(self):
        def create_conversation():
            pass

        adapter = SimpleAdapterWithCreateConversation(
            call_create_conversation=create_conversation)

        turn_context = TurnContext(adapter, ACTIVITY)
        handler = TestTeamsActivityHandler()
        await handler.on_turn(turn_context)
 def add_or_update_continuation_parameters(self, context: TurnContext):
     self.continuation_parameters_store[
         context.activity.from_property.id
     ] = ContinuationParameters(
         claims_identity=context.turn_state.get(BotAdapter.BOT_IDENTITY_KEY),
         conversation_reference=TurnContext.get_conversation_reference(
             context.activity
         ),
         oauth_scope=context.turn_state.get(BotAdapter.BOT_OAUTH_SCOPE_KEY),
     )
    def test_get_conversation_reference_should_return_valid_reference(self):
        reference = TurnContext.get_conversation_reference(ACTIVITY)

        assert reference.activity_id == ACTIVITY.id
        assert reference.user == ACTIVITY.from_property
        assert reference.bot == ACTIVITY.recipient
        assert reference.conversation == ACTIVITY.conversation
        assert reference.channel_id == ACTIVITY.channel_id
        assert reference.locale == ACTIVITY.locale
        assert reference.service_url == ACTIVITY.service_url
Пример #7
0
    def create_empty_context():
        b = TestAdapter()
        a = Activity(
            type = ActivityTypes.message,
            channel_id = "EmptyContext",
            conversation = ConversationAccount(id= 'test' ),
            from_property = ChannelAccount(id= '*****@*****.**')
        )
        bc = TurnContext(b, a)

        return bc
Пример #8
0
    async def process(self, req: Request, logic: Callable) -> Response:
        """
        Accept an incoming webhook request and convert it into a TurnContext which can be processed by the bot's logic.

        :param req: The aiohttp Request object.
        :type req: :class:`aiohttp.web_request.Request`
        :param logic: The method to call for the resulting bot turn.
        :type logic: :class:`tying.Callable`
        :return: The aiohttp Response.
        :rtype: :class:`aiohttp.web_response.Response`
        """

        if not req:
            raise Exception("Request is required")

        if not self.slack_logged_in:
            await self.slack_client.login_with_slack()
            self.slack_logged_in = True

        body = await req.text()

        if (self.options.verify_incoming_requests
                and not self.slack_client.verify_signature(req, body)):
            return SlackHelper.response(
                req, 401, "Rejected due to mismatched header signature")

        slack_body = SlackHelper.deserialize_body(req.content_type, body)

        if slack_body.type == "url_verification":
            return SlackHelper.response(req, 200, slack_body.challenge)

        if (not self.slack_client.options.slack_verification_token
                and slack_body.token !=
                self.slack_client.options.slack_verification_token):
            text = f"Rejected due to mismatched verificationToken:{body}"
            return SlackHelper.response(req, 403, text)

        if slack_body.payload:
            # handle interactive_message callbacks and block_actions
            activity = SlackHelper.payload_to_activity(slack_body.payload)
        elif slack_body.type == "event_callback":
            activity = await SlackHelper.event_to_activity(
                slack_body.event, self.slack_client)
        elif slack_body.command:
            activity = await SlackHelper.command_to_activity(
                slack_body, self.slack_client)
        else:
            return SlackHelper.response(
                req, 200, f"Unknown Slack event type {slack_body.type}")

        context = TurnContext(self, activity)
        await self.run_pipeline(context, logic)

        return SlackHelper.response(req, 200)
Пример #9
0
 def _add_conversation_reference(self, activity: Activity):
     """
     This populates the shared Dictionary that holds conversation references. In this sample,
     this dictionary is used to send a message to members when /api/notify is hit.
     :param activity:
     :return:
     """
     conversation_reference = TurnContext.get_conversation_reference(
         activity)
     self.conversation_references[
         conversation_reference.user.id] = conversation_reference
Пример #10
0
    def on_post(self, req, resp):

        try:
            auth_header = req.headers['AUTHORIZATION']
        except KeyError:
            resp.status = falcon.HTTP_403
            return

        if req.content_length:
            activity = Activity().deserialize(json.load(req.stream))

        sync_run(self.adapter.authenticate_request(activity, auth_header))
        commandline = activity.text
        conversation = activity.conversation
        context = TurnContext(self.adapter, activity)

        commandline = commandline.replace("<at>{}</at>".format(self.name), "")

        sync_run(
            context.send_activity(self.bot.command_dispatcher(commandline)))
    async def test_typing_activity(self):
        activity = Activity(type=ActivityTypes.typing)

        turn_context = TurnContext(SimpleAdapter(), activity)

        # Act
        bot = TestingTeamsActivityHandler()
        await bot.on_turn(turn_context)

        assert len(bot.record) == 1
        assert bot.record[0] == "on_typing_activity"
Пример #12
0
    def create_empty_context():
        adapter = TestAdapter()
        activity = Activity(
            type=ActivityTypes.message,
            channel_id="EmptyContext",
            conversation=ConversationAccount(id="test"),
            from_property=ChannelAccount(id="*****@*****.**"),
        )
        context = TurnContext(adapter, activity)

        return context
Пример #13
0
    def _get_context(question: str, bot_adapter: BotAdapter) -> TurnContext:
        test_adapter = bot_adapter or TestAdapter()
        activity = Activity(
            type=ActivityTypes.message,
            text=question,
            conversation=ConversationAccount(),
            recipient=ChannelAccount(),
            from_property=ChannelAccount(),
        )

        return TurnContext(test_adapter, activity)
Пример #14
0
        async def logic(context: TurnContext):
            if test_case != FlowTestCase.root_bot_only:
                claims_identity = ClaimsIdentity(
                    {
                        AuthenticationConstants.VERSION_CLAIM:
                        "2.0",
                        AuthenticationConstants.AUDIENCE_CLAIM:
                        self.skill_bot_id,
                        AuthenticationConstants.AUTHORIZED_PARTY:
                        self.parent_bot_id,
                    },
                    True,
                )
                context.turn_state[
                    BotAdapter.BOT_IDENTITY_KEY] = claims_identity

                if test_case == FlowTestCase.root_bot_consuming_skill:
                    context.turn_state[
                        SkillHandler.
                        SKILL_CONVERSATION_REFERENCE_KEY] = SkillConversationReference(
                            None, AuthenticationConstants.
                            TO_CHANNEL_FROM_BOT_OAUTH_SCOPE)

                if test_case == FlowTestCase.middle_skill:
                    context.turn_state[
                        SkillHandler.
                        SKILL_CONVERSATION_REFERENCE_KEY] = SkillConversationReference(
                            None, self.parent_bot_id)

            async def capture_eoc(inner_context: TurnContext,
                                  activities: List[Activity], next):  # pylint: disable=unused-argument
                for activity in activities:
                    if activity.type == ActivityTypes.end_of_conversation:
                        self.eoc_sent = activity
                        break
                return await next()

            context.on_send_activities(capture_eoc)

            await DialogExtensions.run_dialog(
                dialog, context, convo_state.create_property("DialogState"))
 async def test_should_add_and_call_save_all_changes_on_multiple_plugins(
         self):
     adapter = TestAdapter()
     context = TurnContext(adapter, Activity())
     foo_state = BotStateMock({"foo": "bar"})
     bar_state = BotStateMock({"bar": "foo"})
     autosave_middleware = AutoSaveStateMiddleware([foo_state, bar_state])
     await autosave_middleware.bot_state_set.save_all_changes(context)
     assert (foo_state.write_called or
             bar_state.write_called), "write not called for either plugin."
     assert foo_state.write_called, "write not called for 'foo_state' plugin."
     assert bar_state.write_called, "write not called for 'bar_state' plugin."
    async def end_dialog(self, context: TurnContext, instance: DialogInstance,
                         reason: DialogReason):
        # Send of of conversation to the skill if the dialog has been cancelled.
        if reason in (DialogReason.CancelCalled, DialogReason.ReplaceCalled):
            activity = Activity(type=ActivityTypes.end_of_conversation)

            # Apply conversation reference and common properties from incoming activity before sending.
            TurnContext.apply_conversation_reference(
                activity,
                TurnContext.get_conversation_reference(context.activity),
                is_incoming=True,
            )
            activity.channel_data = context.activity.channel_data
            activity.additional_properties = context.activity.additional_properties

            # connection Name is not applicable for an EndDialog, as we don't expect as OAuthCard in response.
            skill_conversation_id = instance.state[
                SkillDialog.SKILLCONVERSATIONIDSTATEKEY]
            await self._send_to_skill(context, activity, skill_conversation_id)

        await super().end_dialog(context, instance, reason)
    async def test_on_installation_update(self):
        activity = Activity(type=ActivityTypes.installation_update)

        adapter = TestInvokeAdapter()
        turn_context = TurnContext(adapter, activity)

        # Act
        bot = TestingActivityHandler()
        await bot.on_turn(turn_context)

        assert len(bot.record) == 1
        assert bot.record[0] == "on_installation_update"
    async def process_command(self, context: TurnContext) -> Any:
        if context.activity.type == ActivityTypes.message and context.activity.text:

            original_text = context.activity.text
            TurnContext.remove_recipient_mention(context.activity)

            command = context.activity.text.strip().split(" ")
            if len(command
                   ) > 1 and command[0] == InspectionMiddleware._COMMAND:

                if len(command) == 2 and command[1] == "open":
                    await self._process_open_command(context)
                    return True

                if len(command) == 3 and command[1] == "attach":
                    await self.process_attach_command(context, command[2])
                    return True

            context.activity.text = original_text

        return False
    async def test_on_end_of_conversation_activity(self):
        activity = Activity(type=ActivityTypes.end_of_conversation)

        adapter = TestInvokeAdapter()
        turn_context = TurnContext(adapter, activity)

        # Act
        bot = TestingActivityHandler()
        await bot.on_turn(turn_context)

        assert len(bot.record) == 1
        assert bot.record[0] == "on_end_of_conversation_activity"
Пример #20
0
    async def test_should_call_multiple_on_delete_activity_handlers_in_order(
            self):
        context = TurnContext(SimpleAdapter(), ACTIVITY)
        called_first = False
        called_second = False

        async def first_delete_handler(context, reference,
                                       next_handler_coroutine):
            nonlocal called_first, called_second
            assert called_first is False, 'called_first should not be True before first_delete_handler is called.'
            called_first = True
            assert called_second is False, 'Second on_delete_activity handler was called before first.'
            assert reference is not None
            assert context is not None
            assert reference.activity_id == '1234'
            await next_handler_coroutine()

        async def second_delete_handler(context, reference,
                                        next_handler_coroutine):
            nonlocal called_first, called_second
            assert called_first
            assert called_second is False, 'called_second was set to True before second handler was called.'
            called_second = True
            assert reference is not None
            assert context is not None
            assert reference.activity_id == '1234'
            await next_handler_coroutine()

        context.on_delete_activity(first_delete_handler)
        context.on_delete_activity(second_delete_handler)
        await context.delete_activity(ACTIVITY.id)
        assert called_first is True
        assert called_second is True
    async def _end_skill_conversation(
            self,
            turn_context: TurnContext,
            error: Exception  # pylint: disable=unused-argument
    ):
        if not self._skill_client or not self._skill_config:
            return

        try:
            # Inform the active skill that the conversation is ended so that it has a chance to clean up.
            # Note: the root bot manages the ActiveSkillPropertyName, which has a value while the root bot
            # has an active conversation with a skill.
            active_skill = await self._conversation_state.create_property(
                MainDialog.ACTIVE_SKILL_PROPERTY_NAME).get(turn_context)

            if active_skill:
                bot_id = self._config.APP_ID
                end_of_conversation = Activity(
                    type=ActivityTypes.end_of_conversation)
                end_of_conversation.code = "RootSkillError"
                TurnContext.apply_conversation_reference(
                    end_of_conversation,
                    TurnContext.get_conversation_reference(
                        turn_context.activity),
                    True,
                )

                await self._conversation_state.save_changes(turn_context, True)
                await self._skill_client.post_activity_to_skill(
                    bot_id,
                    active_skill,
                    self._skill_config.SKILL_HOST_ENDPOINT,
                    end_of_conversation,
                )
        except Exception as exception:
            print(
                f"\n Exception caught on _end_skill_conversation : {exception}",
                file=sys.stderr,
            )
            traceback.print_exc()
    async def on_message_activity(self, turn_context: TurnContext):
        TurnContext.remove_recipient_mention(turn_context.activity)
        turn_context.activity.text = turn_context.activity.text.strip()

        if turn_context.activity.text == "MentionMe":
            await self._mention_activity(turn_context)
            return

        if turn_context.activity.text == "UpdateCardAction":
            await self._update_card_activity(turn_context)
            return

        if turn_context.activity.text == "MessageAllMembers":
            await self._message_all_members(turn_context)
            return

        if turn_context.activity.text == "Delete":
            await self._delete_card_activity(turn_context)
            return

        card = HeroCard(
            title="Welcome Card",
            text="Click the buttons to update this card",
            buttons=[
                CardAction(
                    type=ActionTypes.message_back,
                    title="Update Card",
                    text="UpdateCardAction",
                    value={"count": 0},
                ),
                CardAction(
                    type=ActionTypes.message_back,
                    title="Message all memebers",
                    text="MessageAllMembers",
                ),
            ],
        )
        await turn_context.send_activity(
            MessageFactory.attachment(CardFactory.hero_card(card)))
        return
Пример #23
0
    async def continue_conversation(
        self,
        reference: ConversationReference,
        callback: Callable,
        bot_id: str = None,
        claims_identity: ClaimsIdentity = None,
        audience: str = None,
    ):
        """
        Sends a proactive message to a conversation. Call this method to proactively send a message to a conversation.
        Most _channels require a user to initiate a conversation with a bot before the bot can send activities
        to the user.

        :param bot_id: Unused for this override.
        :param reference: A reference to the conversation to continue.
        :param callback: The method to call for the resulting bot turn.
        :param claims_identity: A ClaimsIdentity for the conversation.
        :param audience: Unused for this override.
        """

        if not reference:
            raise Exception("ConversationReference is required")
        if not callback:
            raise Exception("callback is required")

        if claims_identity:
            request = conversation_reference_extension.get_continuation_activity(
                reference
            )
            context = TurnContext(self, request)
            context.turn_state[BotAdapter.BOT_IDENTITY_KEY] = claims_identity
            context.turn_state[BotAdapter.BOT_CALLBACK_HANDLER_KEY] = callback
        else:
            request = TurnContext.apply_conversation_reference(
                conversation_reference_extension.get_continuation_activity(reference),
                reference,
            )
            context = TurnContext(self, request)

        return await self.run_pipeline(context, callback)
    async def test_pass_resource_responses_through(self):
        def validate_responses(activities: List[Activity]):
            pass  # no need to do anything.

        adapter = SimpleAdapter(call_on_send=validate_responses)
        context = TurnContext(adapter, Activity())

        activity_id = str(uuid.uuid1())
        activity = TestMessage.message(activity_id)

        resource_response = await context.send_activity(activity)
        self.assertTrue(resource_response.id == activity_id,
                        "Incorrect response Id returned")
Пример #25
0
    async def test_should_reject_with_error_if_channel_id_is_missing(self):
        context = TurnContext(self.adapter, MISSING_CHANNEL_ID)

        async def next_middleware():
            assert False, 'Should not have called next_middleware'

        try:
            await self.user_state.on_process_request(context, next_middleware)
        except AttributeError:
            pass
        else:
            raise AssertionError(
                'Should not have completed and not raised AttributeError.')
Пример #26
0
    async def end_dialog(self, context: TurnContext, instance: DialogInstance,
                         reason: DialogReason):
        # Send of of conversation to the skill if the dialog has been cancelled.
        if reason in (DialogReason.CancelCalled, DialogReason.ReplaceCalled):
            await context.send_trace_activity(
                f"{SkillDialog.__name__}.end_dialog()",
                label=f"ActivityType: {context.activity.type}",
            )
            activity = Activity(type=ActivityTypes.end_of_conversation)

            # Apply conversation reference and common properties from incoming activity before sending.
            TurnContext.apply_conversation_reference(
                activity,
                TurnContext.get_conversation_reference(context.activity),
                is_incoming=True,
            )
            activity.channel_data = context.activity.channel_data
            activity.additional_properties = context.activity.additional_properties

            await self._send_to_skill(context, activity)

        await super().end_dialog(context, instance, reason)
Пример #27
0
    async def on_message_activity(self, turn_context: TurnContext):
        await turn_context.send_activity("parent: before child")

        activity = MessageFactory.text("parent to child")
        TurnContext.apply_conversation_reference(
            activity,
            TurnContext.get_conversation_reference(turn_context.activity))
        activity.delivery_mode = DeliveryModes.expect_replies

        activities = await self.client.post_buffered_activity(
            None,
            "toBotId",
            "http://localhost:3979/api/messages",
            "http://tempuri.org/whatever",
            str(uuid.uuid4()),
            activity,
        )

        if activities:
            await turn_context.send_activities(activities)

        await turn_context.send_activity("parent: after child")
    async def test_invoke(self):
        activity = Activity(type=ActivityTypes.invoke, name="some.random.invoke")

        adapter = TestInvokeAdapter()
        turn_context = TurnContext(adapter, activity)

        # Act
        bot = TestingActivityHandler()
        await bot.on_turn(turn_context)

        assert len(bot.record) == 1
        assert bot.record[0] == "on_invoke_activity"
        assert adapter.activity.value.status == int(HTTPStatus.OK)
    async def test_invoke_should_not_match(self):
        activity = Activity(type=ActivityTypes.invoke, name="should.not.match")

        adapter = TestInvokeAdapter()
        turn_context = TurnContext(adapter, activity)

        # Act
        bot = TestingActivityHandler()
        await bot.on_turn(turn_context)

        assert len(bot.record) == 1
        assert bot.record[0] == "on_invoke_activity"
        assert adapter.activity.value.status == int(HTTPStatus.NOT_IMPLEMENTED)
Пример #30
0
    async def test_should_reject_with_error_if_from_property_is_missing(self):
        context = TurnContext(self.adapter, MISSING_FROM_PROPERTY)

        async def next_middleware():
            assert False, 'Should not have called next_middleware'

        try:
            await self.middleware.on_process_request(context, next_middleware)
        except AttributeError:
            pass
        else:
            raise AssertionError(
                'Should not have completed and not raised AttributeError.')