Ejemplo n.º 1
0
        async def callback(context: TurnContext):
            context.turn_state[
                SkillHandler.SKILL_CONVERSATION_REFERENCE_KEY
            ] = skill_conversation_reference

            TurnContext.apply_conversation_reference(
                activity, skill_conversation_reference.conversation_reference
            )

            context.activity.id = reply_to_activity_id

            app_id = JwtTokenValidation.get_app_id_from_claims(claims_identity.claims)
            context.activity.caller_id = (
                f"{CallerIdConstants.bot_to_bot_prefix}{app_id}"
            )

            if activity.type == ActivityTypes.end_of_conversation:
                await self._conversation_id_factory.delete_conversation_reference(
                    conversation_id
                )
                self._apply_eoc_to_turn_context_activity(context, activity)
                await self._bot.on_turn(context)
            elif activity.type == ActivityTypes.event:
                self._apply_event_to_turn_context_activity(context, activity)
                await self._bot.on_turn(context)
            else:
                await context.send_activity(activity)
Ejemplo n.º 2
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

            # 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)
Ejemplo n.º 3
0
        async def callback(context: TurnContext):
            nonlocal resource_response
            context.turn_state[
                SkillHandler.
                SKILL_CONVERSATION_REFERENCE_KEY] = skill_conversation_reference

            TurnContext.apply_conversation_reference(
                activity, skill_conversation_reference.conversation_reference)

            context.activity.id = reply_to_activity_id

            app_id = JwtTokenValidation.get_app_id_from_claims(
                claims_identity.claims)
            context.activity.caller_id = (
                f"{CallerIdConstants.bot_to_bot_prefix}{app_id}")

            if activity.type == ActivityTypes.end_of_conversation:
                await self._conversation_id_factory.delete_conversation_reference(
                    conversation_id)
                await self._send_to_bot(activity, context)
            elif activity.type == ActivityTypes.event:
                await self._send_to_bot(activity, context)
            elif activity.type in (ActivityTypes.command,
                                   ActivityTypes.command_result):
                if activity.name.startswith("application/"):
                    # Send to channel and capture the resource response for the SendActivityCall so we can return it.
                    resource_response = await context.send_activity(activity)
                else:
                    await self._send_to_bot(activity, context)
            else:
                # Capture the resource response for the SendActivityCall so we can return it.
                resource_response = await context.send_activity(activity)
Ejemplo n.º 4
0
    async def test_on_reply_to_activity(self):
        self._conversation_id = await self._test_id_factory.create_skill_conversation_id(
            self._conversation_reference)

        mock_adapter = Mock()
        mock_adapter.continue_conversation = MagicMock(return_value=Future())
        mock_adapter.continue_conversation.return_value.set_result(Mock())
        mock_adapter.send_activities = MagicMock(return_value=Future())
        mock_adapter.send_activities.return_value.set_result([])

        sut = self.create_skill_handler_for_testing(mock_adapter)

        activity = Activity(type=ActivityTypes.message,
                            attachments=[],
                            entities=[])
        activity_id = str(uuid4())
        TurnContext.apply_conversation_reference(activity,
                                                 self._conversation_reference)

        await sut.test_on_reply_to_activity(self._claims_identity,
                                            self._conversation_id, activity_id,
                                            activity)

        args, kwargs = mock_adapter.continue_conversation.call_args_list[0]

        assert isinstance(args[0], ConversationReference)
        assert callable(args[1])
        assert isinstance(kwargs["claims_identity"], ClaimsIdentity)

        await args[1](TurnContext(
            mock_adapter,
            conversation_reference_extension.get_continuation_activity(
                self._conversation_reference),
        ))
        assert activity.caller_id is None
    async def test_forwarding_on_send_to_conversation(self):
        self._conversation_id = await self._test_id_factory.create_skill_conversation_id(
            self._conversation_reference)

        resource_response_id = "rId"

        async def side_effect(*arg_list, **args_dict):  # pylint: disable=unused-argument
            fake_context = Mock()
            fake_context.turn_state = {}
            fake_context.send_activity = MagicMock(return_value=Future())
            fake_context.send_activity.return_value.set_result(
                ResourceResponse(id=resource_response_id))
            await arg_list[1](fake_context)

        mock_adapter = Mock()
        mock_adapter.continue_conversation = side_effect
        mock_adapter.send_activities = MagicMock(return_value=Future())
        mock_adapter.send_activities.return_value.set_result([])

        sut = self.create_skill_handler_for_testing(mock_adapter)

        activity = Activity(type=ActivityTypes.message,
                            attachments=[],
                            entities=[])
        TurnContext.apply_conversation_reference(activity,
                                                 self._conversation_reference)

        assert not activity.caller_id

        response = await sut.test_on_send_to_conversation(
            self._claims_identity, self._conversation_id, activity)

        assert response.id is resource_response_id
    async def __activity_callback_test(self, activity: Activity):
        self._conversation_id = await self._test_id_factory.create_skill_conversation_id(
            self._conversation_reference)

        mock_adapter = Mock()
        mock_adapter.continue_conversation = MagicMock(return_value=Future())
        mock_adapter.continue_conversation.return_value.set_result(Mock())
        mock_adapter.send_activities = MagicMock(return_value=Future())
        mock_adapter.send_activities.return_value.set_result([])

        sut = self.create_skill_handler_for_testing(mock_adapter)

        activity_id = str(uuid4())
        TurnContext.apply_conversation_reference(activity,
                                                 self._conversation_reference)

        await sut.test_on_reply_to_activity(self._claims_identity,
                                            self._conversation_id, activity_id,
                                            activity)

        args, kwargs = mock_adapter.continue_conversation.call_args_list[0]

        assert isinstance(args[0], ConversationReference)
        assert callable(args[1])
        assert isinstance(kwargs["claims_identity"], ClaimsIdentity)

        await args[1](TurnContext(mock_adapter, activity))
    async def send(self, activity: Activity) -> Any:
        TurnContext.apply_conversation_reference(activity, self._conversation_reference)

        try:
            await self._connector_client.conversations.send_to_conversation(
                activity.conversation.id, activity
            )
        except Exception:
            return False

        return True
Ejemplo n.º 8
0
    async def reprompt_dialog(  # pylint: disable=unused-argument
            self, context: TurnContext, instance: DialogInstance):
        # Create and send an event to the skill so it can resume the dialog.
        reprompt_event = Activity(type=ActivityTypes.event,
                                  name=DialogEvents.reprompt_dialog)

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

        await self._send_to_skill(context, reprompt_event)
    async def _send_proactive_threaded_message(self,
                                               turn_context: TurnContext):
        activity = MessageFactory.text(
            "I will send two messages to this thread")
        result = await turn_context.send_activity(activity)

        team_id = TeamsInfo.get_team_id(turn_context)
        for i in range(2):
            proactive_activity = MessageFactory.text(
                f"This is message {i+1}/2 that will be sent.")
            TurnContext.apply_conversation_reference(
                proactive_activity,
                TurnContext.get_conversation_reference(activity))
            await turn_context.send_activity(proactive_activity)
Ejemplo n.º 10
0
async def on_error(context: TurnContext, error: Exception):
    # This check writes out errors to console log .vs. app insights.
    # NOTE: In production environment, you should consider logging this to Azure
    #       application insights.
    print(f"\n [on_turn_error] unhandled error: {error}", file=sys.stderr)
    traceback.print_exc()

    # Send a message to the user
    await context.send_activity("The bot encountered an error or bug.")
    await context.send_activity(
        "To continue to run this bot, please fix the bot source code.")

    # Send a trace activity if we're talking to the Bot Framework Emulator
    if context.activity.channel_id == "emulator":
        # Create a trace activity that contains the error object
        trace_activity = Activity(
            label="TurnError",
            name="on_turn_error Trace",
            timestamp=datetime.utcnow(),
            type=ActivityTypes.trace,
            value=f"{error}",
            value_type="https://www.botframework.com/schemas/error",
        )
        await context.send_activity(trace_activity)

    # Inform the active skill that the conversation is ended so that it has
    # a chance to clean up.
    # Note: ActiveSkillPropertyName is set by the RooBot while messages are being
    # forwarded to a Skill.
    active_skill_id = await CONVERSATION_STATE.create_property(
        "activeSkillProperty").get(context)
    if active_skill_id:
        end_of_conversation = Activity(type=ActivityTypes.end_of_conversation,
                                       code="RootSkillError")
        TurnContext.apply_conversation_reference(
            end_of_conversation,
            TurnContext.get_conversation_reference(context.activity),
            is_incoming=True)

        await CONVERSATION_STATE.save_changes(context, True)
        await CLIENT.post_activity(
            CONFIG.APP_ID,
            SKILL_CONFIG.SKILLS[active_skill_id],
            SKILL_CONFIG.SKILL_HOST_ENDPOINT,
            end_of_conversation,
        )

    # Clear out state
    await CONVERSATION_STATE.delete(context)
Ejemplo n.º 11
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: The application ID of the bot. This parameter is ignored in
        single tenant the Adpters (Console, Test, etc) but is critical to the BotFrameworkAdapter
        which is multi-tenant aware. </param>
        :param reference: A reference to the conversation to continue.</param>
        :param callback: The method to call for the resulting bot turn.</param>
        :param claims_identity:
        """

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

        request = TurnContext.apply_conversation_reference(
            conversation_reference_extension.get_continuation_activity(
                reference),
            reference,
        )
        context = TurnContext(self, request)

        return await self.run_pipeline(context, callback)
Ejemplo n.º 12
0
    async def test_should_migrate_tenant_id_for_msteams(self):
        incoming = TurnContext.apply_conversation_reference(
            activity=Activity(
                type=ActivityTypes.message,
                text="foo",
                channel_data={"tenant": {
                    "id": "1234"
                }},
            ),
            reference=REFERENCE,
            is_incoming=True,
        )

        incoming.channel_id = "msteams"
        adapter = AdapterUnderTest()

        async def aux_func_assert_tenant_id_copied(context):
            self.assertEqual(
                context.activity.conversation.tenant_id,
                "1234",
                "should have copied tenant id from "
                "channel_data to conversation address",
            )

        await adapter.process_activity(incoming, "",
                                       aux_func_assert_tenant_id_copied)
Ejemplo n.º 13
0
    async def reprompt_dialog(  # pylint: disable=unused-argument
        self, context: TurnContext, instance: DialogInstance
    ):
        # Create and send an event to the skill so it can resume the dialog.
        reprompt_event = Activity(
            type=ActivityTypes.event, name=DialogEvents.reprompt_dialog
        )

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

        # connection Name is not applicable for a RePrompt, as we don't expect as OAuthCard in response.
        skill_conversation_id = instance.state[SkillDialog.SKILLCONVERSATIONIDSTATEKEY]
        await self._send_to_skill(context, reprompt_event, skill_conversation_id)
Ejemplo n.º 14
0
        async def callback(context: TurnContext):
            context.turn_state[
                SkillHandler.
                SKILL_CONVERSATION_REFERENCE_KEY] = skill_conversation_reference
            TurnContext.apply_conversation_reference(activity,
                                                     conversation_reference)
            context.activity.id = reply_to_activity_id

            if activity.type == ActivityTypes.end_of_conversation:
                await self._conversation_id_factory.delete_conversation_reference(
                    conversation_id)
                self._apply_eoc_to_turn_context_activity(context, activity)
                await self._bot.on_turn(context)
            elif activity.type == ActivityTypes.event:
                self._apply_event_to_turn_context_activity(context, activity)
                await self._bot.on_turn(context)
            else:
                await context.send_activity(activity)
Ejemplo n.º 15
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
Ejemplo n.º 16
0
    def test_apply_conversation_reference_when_is_incoming_is_true_should_not_prepare_a_reply(
            self):
        reference = TurnContext.get_conversation_reference(ACTIVITY)
        reply = TurnContext.apply_conversation_reference(
            Activity(type="message", text="reply"), reference, True)

        assert reply.recipient == ACTIVITY.recipient
        assert reply.from_property == ACTIVITY.from_property
        assert reply.conversation == ACTIVITY.conversation
        assert reply.service_url == ACTIVITY.service_url
        assert reply.channel_id == ACTIVITY.channel_id
Ejemplo n.º 17
0
    def test_apply_conversation_reference_should_return_prepare_reply_when_is_incoming_is_False(
            self):
        reference = TurnContext.get_conversation_reference(ACTIVITY)
        reply = TurnContext.apply_conversation_reference(
            Activity(type='message', text='reply'), reference)

        assert reply.recipient == ACTIVITY.from_property
        assert reply.from_property == ACTIVITY.recipient
        assert reply.conversation == ACTIVITY.conversation
        assert reply.service_url == ACTIVITY.service_url
        assert reply.channel_id == ACTIVITY.channel_id
    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()
Ejemplo n.º 19
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")
Ejemplo n.º 20
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)
Ejemplo n.º 21
0
    async def continue_conversation(
            self,
            reference: ConversationReference,
            callback: Callable,
            bot_id: str = None,  # pylint: disable=unused-argument
            claims_identity: ClaimsIdentity = None,
            audience: str = None,  # pylint: disable=unused-argument
    ):
        """
        Send a proactive message to a conversation.

        .. remarks::

            Most channels require a user to initiate a conversation with a bot before the bot can send activities to the
             user.

        :param reference: A reference to the conversation to continue.
        :type reference: :class:`botbuilder.schema.ConversationReference`
        :param callback: The method to call for the resulting bot turn.
        :type callback: :class:`typing.Callable`
        :param bot_id: Unused for this override.
        :type bot_id: str
        :param claims_identity: A ClaimsIdentity for the conversation.
        :type claims_identity: :class:`botframework.connector.auth.ClaimsIdentity`
        :param audience: Unused for this override.
        :type audience: str
        """

        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)
Ejemplo n.º 22
0
    async def test_should_migrate_tenant_id_for_msteams(self):
        incoming = TurnContext.apply_conversation_reference(
            activity=Activity(type=ActivityTypes.message,
                              text='foo',
                              channel_data={'tenant': {
                                  'id': '1234'
                              }}),
            reference=reference,
            is_incoming=True)

        incoming.channel_id = 'msteams'
        adapter = AdapterUnderTest()

        async def aux_func_assert_tenant_id_copied(context):
            self.assertEquals(
                context.activity.conversation.tenant_id, '1234',
                'should have copied tenant id from '
                'channel_data to conversation address')

        await adapter.process_activity(incoming, '',
                                       aux_func_assert_tenant_id_copied)
Ejemplo n.º 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)
Ejemplo n.º 24
0
)

REFERENCE = ConversationReference(
    activity_id="1234",
    channel_id="test",
    locale="en-uS",  # Intentionally oddly-cased to check that it isn't defaulted somewhere, but tests stay in English
    service_url="https://example.org/channel",
    user=ChannelAccount(id="user", name="User Name"),
    bot=ChannelAccount(id="bot", name="Bot Name"),
    conversation=ConversationAccount(id="convo1"),
)

TEST_ACTIVITY = Activity(text="test", type=ActivityTypes.message)

INCOMING_MESSAGE = TurnContext.apply_conversation_reference(
    copy(TEST_ACTIVITY), REFERENCE, True
)
OUTGOING_MESSAGE = TurnContext.apply_conversation_reference(
    copy(TEST_ACTIVITY), REFERENCE
)
INCOMING_INVOKE = TurnContext.apply_conversation_reference(
    Activity(type=ActivityTypes.invoke), REFERENCE, True
)


class AdapterUnderTest(BotFrameworkAdapter):
    def __init__(self, settings=None):
        super().__init__(settings)
        self.tester = aiounittest.AsyncTestCase()
        self.fail_auth = False
        self.fail_operation = False
    async def test_on_reply_to_activity(self):
        resource_response_id = "resourceId"
        self._conversation_id = await self._test_id_factory.create_skill_conversation_id(
            self._conversation_reference)

        types_to_test = [
            ActivityTypes.end_of_conversation,
            ActivityTypes.event,
            ActivityTypes.message,
        ]

        for activity_type in types_to_test:
            with self.subTest(act_type=activity_type):
                mock_adapter = Mock()
                mock_adapter.continue_conversation = MagicMock(
                    return_value=Future())
                mock_adapter.continue_conversation.return_value.set_result(
                    Mock())
                mock_adapter.send_activities = MagicMock(return_value=Future())
                mock_adapter.send_activities.return_value.set_result(
                    [ResourceResponse(id=resource_response_id)])

                sut = self.create_skill_handler_for_testing(mock_adapter)

                activity = Activity(type=activity_type,
                                    attachments=[],
                                    entities=[])
                activity_id = str(uuid4())
                TurnContext.apply_conversation_reference(
                    activity, self._conversation_reference)

                resource_response = await sut.test_on_reply_to_activity(
                    self._claims_identity, self._conversation_id, activity_id,
                    activity)

                # continue_conversation validation
                (
                    args_continue,
                    kwargs_continue,
                ) = mock_adapter.continue_conversation.call_args_list[0]
                mock_adapter.continue_conversation.assert_called_once()

                assert isinstance(args_continue[0], ConversationReference)
                assert callable(args_continue[1])
                assert isinstance(kwargs_continue["claims_identity"],
                                  ClaimsIdentity)

                turn_context = TurnContext(
                    mock_adapter,
                    conversation_reference_extension.get_continuation_activity(
                        self._conversation_reference),
                )
                await args_continue[1](turn_context)
                # assert the callback set the right properties.
                assert (f"{CallerIdConstants.bot_to_bot_prefix}{self.skill_id}"
                        ), turn_context.activity.caller_id

                if activity_type == ActivityTypes.message:
                    # send_activities validation
                    (
                        args_send,
                        _,
                    ) = mock_adapter.send_activities.call_args_list[0]
                    activity_from_send = args_send[1][0]
                    assert activity_from_send.caller_id is None
                    assert activity_from_send.reply_to_id, activity_id
                    assert resource_response.id, resource_response_id
                else:
                    # Assert mock SendActivitiesAsync wasn't called
                    mock_adapter.send_activities.assert_not_called()
    async def test_on_send_to_conversation(self):
        self._conversation_id = await self._test_id_factory.create_skill_conversation_id(
            self._conversation_reference)
        # python 3.7 doesn't support AsyncMock, change this when min ver is 3.8
        send_activities_called = False

        mock_adapter = Mock()

        async def continue_conversation(
            reference: ConversationReference,
            callback: Callable,
            bot_id: str = None,
            claims_identity: ClaimsIdentity = None,
            audience: str = None,
        ):  # pylint: disable=unused-argument
            # Invoke the callback created by the handler so we can assert the rest of the execution.
            turn_context = TurnContext(
                mock_adapter,
                conversation_reference_extension.get_continuation_activity(
                    self._conversation_reference),
            )
            await callback(turn_context)

            # Assert the callback set the right properties.
            assert (f"{CallerIdConstants.bot_to_bot_prefix}{self.skill_id}"
                    ), turn_context.activity.caller_id

        async def send_activities(context: TurnContext,
                                  activities: List[Activity]):  # pylint: disable=unused-argument
            # Messages should not have a caller id set when sent back to the caller.
            nonlocal send_activities_called
            assert activities[0].caller_id is None
            assert activities[0].reply_to_id is None
            send_activities_called = True
            return [ResourceResponse(id="resourceId")]

        mock_adapter.continue_conversation = continue_conversation
        mock_adapter.send_activities = send_activities

        types_to_test = [
            ActivityTypes.end_of_conversation,
            ActivityTypes.event,
            ActivityTypes.message,
        ]

        for activity_type in types_to_test:
            with self.subTest(act_type=activity_type):
                send_activities_called = False
                activity = Activity(type=activity_type,
                                    attachments=[],
                                    entities=[])
                TurnContext.apply_conversation_reference(
                    activity, self._conversation_reference)
                sut = self.create_skill_handler_for_testing(mock_adapter)

                resource_response = await sut.test_on_send_to_conversation(
                    self._claims_identity, self._conversation_id, activity)

                if activity_type == ActivityTypes.message:
                    assert send_activities_called
                    assert resource_response.id == "resourceId"
Ejemplo n.º 27
0
from botbuilder.core import BotFrameworkAdapter, BotFrameworkAdapterSettings, TurnContext
from botbuilder.schema import Activity, ActivityTypes, ConversationAccount, ConversationReference, ChannelAccount
from botframework.connector.aio import ConnectorClient
from botframework.connector.auth import ClaimsIdentity

reference = ConversationReference(
    activity_id='1234',
    channel_id='test',
    service_url='https://example.org/channel',
    user=ChannelAccount(id='user', name='User Name'),
    bot=ChannelAccount(id='bot', name='Bot Name'),
    conversation=ConversationAccount(id='convo1'))

test_activity = Activity(text='test', type=ActivityTypes.message)

incoming_message = TurnContext.apply_conversation_reference(
    copy(test_activity), reference, True)
outgoing_message = TurnContext.apply_conversation_reference(
    copy(test_activity), reference)
incoming_invoke = TurnContext.apply_conversation_reference(
    Activity(type=ActivityTypes.invoke), reference, True)


class AdapterUnderTest(BotFrameworkAdapter):
    def __init__(self, settings=None):
        super().__init__(settings)
        self.tester = aiounittest.AsyncTestCase()
        self.fail_auth = False
        self.fail_operation = False
        self.expect_auth_header = ''
        self.new_service_url = None
Ejemplo n.º 28
0
    async def on_message_activity(self, turn_context: TurnContext):
        # for signin, just use an oauth prompt to get the exchangeable token
        # also ensure that the channelId is not emulator
        if turn_context.activity.type != "emulator":
            if (turn_context.activity.text == "login"
                    or turn_context.activity.text.isdigit()):
                await self._conversation_state.load(turn_context, True)
                await self._user_state.load(turn_context, True)
                await DialogHelper.run_dialog(
                    self._dialog,
                    turn_context,
                    self._conversation_state.create_property("DialogState"),
                )
            elif turn_context.activity.text == "logout":
                bot_adapter = turn_context.adapter
                await bot_adapter.sign_out_user(turn_context,
                                                self._connection_name)
                await turn_context.send_activity(
                    MessageFactory.text("You have been signed out."))
            elif turn_context.activity.text in ("skill login", "skill logout"):
                # incoming activity needs to be cloned for buffered replies
                clone_activity = MessageFactory.text(
                    turn_context.activity.text)

                TurnContext.apply_conversation_reference(
                    clone_activity,
                    TurnContext.get_conversation_reference(
                        turn_context.activity),
                    True,
                )

                clone_activity.delivery_mode = DeliveryModes.expect_replies

                activities = await self._client.post_buffered_activity(
                    self._from_bot_id,
                    self._to_bot_id,
                    "http://localhost:3979/api/messages",
                    "http://tempuri.org/whatever",
                    turn_context.activity.conversation.id,
                    clone_activity,
                )

                if activities:
                    if not await self._intercept_oauth_cards(
                            activities, turn_context):
                        await turn_context.send_activities(activities)

            return

        await turn_context.send_activity(
            MessageFactory.text("parent: before child"))

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

        activities = await self._client.post_buffered_activity(
            self._from_bot_id,
            self._to_bot_id,
            "http://localhost:3979/api/messages",
            "http://tempuri.org/whatever",
            str(uuid4()),
            activity,
        )

        await turn_context.send_activities(activities)
        await turn_context.send_activity(
            MessageFactory.text("parent: after child"))