def _create_dialog_skill_bot_activity(
            self, selected_option: str, turn_context: TurnContext) -> Activity:
        """
        Helper method to create the activity to be sent to the DialogSkillBot using selected type and values.
        """

        selected_option = selected_option.lower()
        # Note: in a real bot, the dialogArgs will be created dynamically based on the conversation
        # and what each action requires; here we hardcode the values to make things simpler.

        # Just forward the message activity to the skill with whatever the user said.
        if selected_option == self._skill_action_message.lower():
            # Note message activities also support input parameters but we are not using them in this example.
            return turn_context.activity

        activity = None

        # Send an event activity to the skill with "BookFlight" in the name.
        if selected_option == self._skill_action_book_flight.lower():
            activity = Activity(type=ActivityTypes.event)
            activity.name = self._skill_action_book_flight

        # Send an event activity to the skill with "BookFlight" in the name and some testing values.
        if (selected_option ==
                self._skill_action_book_flight_with_input_parameters.lower()):
            activity = Activity(type=ActivityTypes.event)
            activity.name = self._skill_action_book_flight
            activity.value = {"origin": "New York", "destination": "Seattle"}

        # Send an event activity to the skill with "GetWeather" in the name and some testing values.
        if selected_option == self._skill_action_get_weather.lower():
            activity = Activity(type=ActivityTypes.event)
            activity.name = self._skill_action_get_weather
            activity.value = {"latitude": 47.614891, "longitude": -122.195801}
            return activity

        if not activity:
            raise Exception(
                f"Unable to create dialogArgs for {selected_option}.")

        # We are manually creating the activity to send to the skill; ensure we add the ChannelData and Properties
        # from the original activity so the skill gets them.
        # Note: this is not necessary if we are just forwarding the current activity from context.
        activity.channel_data = turn_context.activity.channel_data
        activity.additional_properties = turn_context.activity.additional_properties

        return activity
    async def test_should_handle_invoke_activities(self):
        activity_sent = None
        from_bot_id_sent = None
        to_bot_id_sent = None
        to_url_sent = None

        async def capture(
            from_bot_id: str,
            to_bot_id: str,
            to_url: str,
            service_url: str,  # pylint: disable=unused-argument
            conversation_id: str,  # pylint: disable=unused-argument
            activity: Activity,
        ):
            nonlocal from_bot_id_sent, to_bot_id_sent, to_url_sent, activity_sent
            from_bot_id_sent = from_bot_id
            to_bot_id_sent = to_bot_id
            to_url_sent = to_url
            activity_sent = activity

        mock_skill_client = self._create_mock_skill_client(capture)

        conversation_state = ConversationState(MemoryStorage())
        dialog_options = SkillDialogTests.create_skill_dialog_options(
            conversation_state, mock_skill_client)

        sut = SkillDialog(dialog_options, "dialog_id")
        activity_to_send = Activity(
            type=ActivityTypes.invoke,
            name=str(uuid.uuid4()),
        )

        client = DialogTestClient(
            "test",
            sut,
            BeginSkillDialogOptions(activity=activity_to_send),
            conversation_state=conversation_state,
        )

        # Send something to the dialog to start it
        await client.send_activity(MessageFactory.text("irrelevant"))

        # Assert results and data sent to the SkillClient for fist turn
        assert dialog_options.bot_id == from_bot_id_sent
        assert dialog_options.skill.app_id == to_bot_id_sent
        assert dialog_options.skill.skill_endpoint == to_url_sent
        assert activity_to_send.text == activity_sent.text
        assert DialogTurnStatus.Waiting == client.dialog_turn_result.status

        # Send a second message to continue the dialog
        await client.send_activity(MessageFactory.text("Second message"))

        # Assert results for second turn
        assert activity_sent.text == "Second message"
        assert DialogTurnStatus.Waiting == client.dialog_turn_result.status

        # Send EndOfConversation to the dialog
        await client.send_activity(
            Activity(type=ActivityTypes.end_of_conversation))

        # Assert we are done.
        assert DialogTurnStatus.Complete == client.dialog_turn_result.status
from botbuilder.schema import (
    Activity,
    ActivityTypes,
    ChannelAccount,
    ConversationAccount,
    Entity,
    Mention,
    ResourceResponse,
)
from botbuilder.core import BotAdapter, MessageFactory, TurnContext

ACTIVITY = Activity(
    id="1234",
    type="message",
    text="test",
    from_property=ChannelAccount(id="user", name="User Name"),
    recipient=ChannelAccount(id="bot", name="Bot Name"),
    conversation=ConversationAccount(id="convo", name="Convo Name"),
    channel_id="UnitTest",
    service_url="https://example.org",
)


class SimpleAdapter(BotAdapter):
    async def send_activities(self, context, activities) -> List[ResourceResponse]:
        responses = []
        assert context is not None
        assert activities is not None
        assert isinstance(activities, list)
        assert activities
        for (idx, activity) in enumerate(activities):  # pylint: disable=unused-variable
            assert isinstance(activity, Activity)
    ClaimsIdentity,
    AuthenticationConstants,
    AppCredentials,
    CredentialProvider,
)

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)
            await step_context.context.send_activity("step1")
            return Dialog.end_of_turn
        
        async def Waterfall2_Step2(step_context: WaterfallStepContext) -> DialogTurnResult:
            await step_context.context.send_activity("step2")
            return Dialog.end_of_turn
         
        async def Waterfall2_Step3(step_context: WaterfallStepContext) -> DialogTurnResult:
            await step_context.context.send_activity("step3")
            return Dialog.end_of_turn
        
        self.add_step(Waterfall2_Step1)
        self.add_step(Waterfall2_Step2)
        self.add_step(Waterfall2_Step3)

begin_message = Activity()
begin_message.text = 'begin'
begin_message.type = 'message' 

class WaterfallTests(aiounittest.AsyncTestCase):
    
    def test_waterfall_none_name(self):
        self.assertRaises(TypeError, (lambda:WaterfallDialog(None)))
    
    def test_watterfall_add_none_step(self):
        waterfall = WaterfallDialog("test")
        self.assertRaises(TypeError, (lambda:waterfall.add_step(None)))
    async def test_waterfall_with_set_instead_of_array(self):
        self.assertRaises(TypeError, lambda:WaterfallDialog('a', { 1, 2 }))

    async def _fill_out_user_profile(self, flow: ConversationFlow,
                                     profile: UserProfile,
                                     turn_context: TurnContext):
        user_input = turn_context.activity.text.strip().lower()

        #begin a conversation and ask for a significant word
        if flow.last_question_asked == Question.NONE:
            await turn_context.send_activity(
                MessageFactory.text(
                    "Let's get started. Please type a significant word !"))
            flow.last_question_asked = Question.WORD

        #validate word and ask for the response
        elif flow.last_question_asked == Question.WORD:
            validate_result = self._validate_word(user_input)
            if not validate_result.is_valid:
                await turn_context.send_activity(
                    MessageFactory.text(validate_result.message))
            else:
                profile.word = validate_result.value
                await turn_context.send_activity(
                    MessageFactory.text(
                        f"You choose: {turn_context.activity.text.strip()} \n\nPlease Wait."
                    ))
                await turn_context.send_activities([
                    Activity(type=ActivityTypes.typing),
                    Activity(type="delay", value=2000)
                ])
                global intrus
                global liste
                #liste=closest_words(profile.word)
                most = model.most_similar(profile.word)
                liste = []
                for i in range(5):
                    liste.append(most[:5][i][0])
                sim = 1
                while sim > 0.5:
                    intrus = vocab[random.randint(1, len(vocab) - 1)]
                    if intrus in liste:
                        continue
                    meaning = dic.meaning(intrus)
                    if not meaning:
                        #wordnik api
                        continue
                    #sim=distance(profile.word,intrus)
                    sim = model.similarity(profile.word, intrus)
                liste.insert(random.randrange(len(liste)), intrus)
                card = HeroCard(
                    text=
                    "Here is the list of the words.\n\nPlease choose the intruder one!",
                    buttons=[
                        CardAction(type=ActionTypes.im_back,
                                   title=liste[0],
                                   value=liste[0]),
                        CardAction(type=ActionTypes.im_back,
                                   title=liste[1],
                                   value=liste[1]),
                        CardAction(type=ActionTypes.im_back,
                                   title=liste[2],
                                   value=liste[2]),
                        CardAction(type=ActionTypes.im_back,
                                   title=liste[3],
                                   value=liste[3]),
                        CardAction(type=ActionTypes.im_back,
                                   title=liste[4],
                                   value=liste[4]),
                        CardAction(type=ActionTypes.im_back,
                                   title=liste[5],
                                   value=liste[5]),
                    ],
                )
                reply = MessageFactory.attachment(CardFactory.hero_card(card))
                await turn_context.send_activity(reply)
                flow.last_question_asked = Question.RESPONSE

        # validate response and wrap it up
        elif flow.last_question_asked == Question.RESPONSE:
            if user_input == "exit":
                await turn_context.send_activity(
                    MessageFactory.text(
                        "Type anything to run the Intruder Bot again."))
                flow.last_question_asked = Question.NONE
            else:
                validate_result = self._validate_result(user_input)
                if not validate_result.is_valid:
                    card = HeroCard(
                        text=validate_result.message,
                        buttons=[
                            CardAction(type=ActionTypes.im_back,
                                       title="exit",
                                       value="exit"),
                        ],
                    )
                    reply = MessageFactory.attachment(
                        CardFactory.hero_card(card))
                    await turn_context.send_activity(reply)
                    profile.score -= 1
                    await turn_context.send_activity(
                        MessageFactory.text(f"Your Score is: {profile.score}"))
                    suggested = MessageFactory.text("Chase the Intruder!")
                    suggested.suggested_actions = SuggestedActions(actions=[
                        CardAction(title=liste[0],
                                   type=ActionTypes.im_back,
                                   value=liste[0]),
                        CardAction(title=liste[1],
                                   type=ActionTypes.im_back,
                                   value=liste[1]),
                        CardAction(title=liste[2],
                                   type=ActionTypes.im_back,
                                   value=liste[2]),
                        CardAction(title=liste[3],
                                   type=ActionTypes.im_back,
                                   value=liste[3]),
                        CardAction(title=liste[4],
                                   type=ActionTypes.im_back,
                                   value=liste[4]),
                        CardAction(title=liste[5],
                                   type=ActionTypes.im_back,
                                   value=liste[5]),
                    ])
                    await turn_context.send_activity(suggested)
                else:
                    profile.response = validate_result.value
                    profile.score += 1
                    await turn_context.send_activity(
                        MessageFactory.text(
                            f"You have responded correctly.\n\nYour score is: {profile.score}\n\nThanks for completing the test."
                        ))
                    await turn_context.send_activity(
                        MessageFactory.text(
                            "Type anything to run the Intruder Bot again."))
                    flow.last_question_asked = Question.NONE
    def append_choices(
        self,
        prompt: Activity,
        channel_id: str,
        choices: List[Choice],
        style: ListStyle,
        options: ChoiceFactoryOptions = None,
    ) -> Activity:
        """
        Composes an output activity containing a set of choices.

        :param prompt: The prompt to append the user's choice to
        :type prompt:
        :param channel_id: Id of the channel the prompt is being sent to
        :type channel_id: str
        :param: choices: List of choices to append
        :type choices:  :class:`List`
        :param: style: Configured style for the list of choices
        :type style:  :class:`ListStyle`
        :param: options: Optional formatting options to use when presenting the choices
        :type style: :class:`ChoiceFactoryOptions`
        :return: A :class:Task representing the asynchronous operation
        :rtype: :class:Task

        .. remarks::
            If the task is successful, the result contains the updated activity.
            When overridden in a derived class, appends choices to the activity when the user
            is prompted for input. This is an helper function to compose an output activity
            containing a set of choices.
        """
        # Get base prompt text (if any)
        text = prompt.text if prompt is not None and prompt.text else ""

        # Create temporary msg
        # TODO: fix once ChoiceFactory complete
        def inline() -> Activity:
            return ChoiceFactory.inline(choices, text, None, options)

        def list_style() -> Activity:
            return ChoiceFactory.list_style(choices, text, None, options)

        def suggested_action() -> Activity:
            return ChoiceFactory.suggested_action(choices, text)

        def hero_card() -> Activity:
            return ChoiceFactory.hero_card(choices, text)

        def list_style_none() -> Activity:
            activity = Activity(type=ActivityTypes.message)
            activity.text = text
            return activity

        def default() -> Activity:
            return ChoiceFactory.for_channel(channel_id, choices, text, None,
                                             options)

        # Maps to values in ListStyle Enum
        switcher = {
            0: list_style_none,
            1: default,
            2: inline,
            3: list_style,
            4: suggested_action,
            5: hero_card,
        }

        msg = switcher.get(int(style.value), default)()

        # Update prompt with text, actions and attachments
        if prompt:
            # clone the prompt the set in the options (note ActivityEx has Properties so this is the safest mechanism)
            prompt = copy.copy(prompt)

            prompt.text = msg.text

            if (msg.suggested_actions is not None
                    and msg.suggested_actions.actions is not None
                    and msg.suggested_actions.actions):
                prompt.suggested_actions = msg.suggested_actions

            if msg.attachments:
                if prompt.attachments:
                    prompt.attachments.extend(msg.attachments)
                else:
                    prompt.attachments = msg.attachments

            return prompt

        # TODO: Update to InputHints.ExpectingInput;
        msg.input_hint = None
        return msg
from botbuilder.dialogs.choices import Choice, ChoiceFactoryOptions, ListStyle
from botbuilder.dialogs.prompts import (
    ChoicePrompt,
    PromptCultureModel,
    PromptOptions,
    PromptValidatorContext,
)
from botbuilder.schema import Activity, ActivityTypes

_color_choices: List[Choice] = [
    Choice(value="red"),
    Choice(value="green"),
    Choice(value="blue"),
]

_answer_message: Activity = Activity(text="red", type=ActivityTypes.message)
_invalid_message: Activity = Activity(text="purple",
                                      type=ActivityTypes.message)


class ChoicePromptTest(aiounittest.AsyncTestCase):
    def test_choice_prompt_with_empty_id_should_fail(self):
        empty_id = ""

        with self.assertRaises(TypeError):
            ChoicePrompt(empty_id)

    def test_choice_prompt_with_none_id_should_fail(self):
        none_id = None

        with self.assertRaises(TypeError):
        async def exec_test_for_locale(valid_locale: str,
                                       locale_variations: List):
            # Hold the correct answer from when a valid locale is used
            expected_answer = None

            def inspector(activity: Activity, description: str):
                nonlocal expected_answer

                assert not description

                if valid_locale == test_locale:
                    expected_answer = activity.text
                else:
                    # Ensure we're actually testing a variation.
                    assert activity.locale != valid_locale

                assert activity.text == expected_answer
                return True

            async def exec_test(turn_context: TurnContext):
                dialog_context = await dialogs.create_context(turn_context)

                results: DialogTurnResult = await dialog_context.continue_dialog(
                )

                if results.status == DialogTurnStatus.Empty:
                    options = PromptOptions(
                        prompt=Activity(type=ActivityTypes.message,
                                        text="Please choose a color."),
                        choices=_color_choices,
                    )
                    await dialog_context.prompt("prompt", options)
                elif results.status == DialogTurnStatus.Complete:
                    selected_choice = results.result
                    await turn_context.send_activity(selected_choice.value)

                await convo_state.save_changes(turn_context)

            async def validator(prompt: PromptValidatorContext) -> bool:
                assert prompt

                if not prompt.recognized.succeeded:
                    await prompt.context.send_activity("Bad input.")

                return prompt.recognized.succeeded

            test_locale = None
            for test_locale in locale_variations:
                adapter = TestAdapter(exec_test)

                convo_state = ConversationState(MemoryStorage())
                dialog_state = convo_state.create_property("dialogState")
                dialogs = DialogSet(dialog_state)

                choice_prompt = ChoicePrompt("prompt", validator)
                dialogs.add(choice_prompt)

                step1 = await adapter.send(
                    Activity(type=ActivityTypes.message,
                             text="Hello",
                             locale=test_locale))
                await step1.assert_reply(inspector)
    async def run_dialog(dialog: Dialog, turn_context: TurnContext,
                         accessor: StatePropertyAccessor):
        """
        Creates a dialog stack and starts a dialog, pushing it onto the stack.
        """

        dialog_set = DialogSet(accessor)
        dialog_set.add(dialog)

        dialog_context = await dialog_set.create_context(turn_context)

        # Handle EoC and Reprompt event from a parent bot (can be root bot to skill or skill to skill)
        if DialogExtensions.__is_from_parent_to_skill(turn_context):
            # Handle remote cancellation request from parent.
            if turn_context.activity.type == ActivityTypes.end_of_conversation:
                if not dialog_context.stack:
                    # No dialogs to cancel, just return.
                    return

                remote_cancel_text = "Skill was canceled through an EndOfConversation activity from the parent."
                await turn_context.send_trace_activity(
                    f"Extension {Dialog.__name__}.run_dialog",
                    label=remote_cancel_text,
                )

                # Send cancellation message to the dialog to ensure all the parents are canceled
                # in the right order.
                await dialog_context.cancel_all_dialogs()
                return

            # Handle a reprompt event sent from the parent.
            if (turn_context.activity.type == ActivityTypes.event
                    and turn_context.activity.name
                    == DialogEvents.reprompt_dialog):
                if not dialog_context.stack:
                    # No dialogs to reprompt, just return.
                    return

                await dialog_context.reprompt_dialog()
                return

        # Continue or start the dialog.
        result = await dialog_context.continue_dialog()
        if result.status == DialogTurnStatus.Empty:
            result = await dialog_context.begin_dialog(dialog.id)

        # Skills should send EoC when the dialog completes.
        if (result.status == DialogTurnStatus.Complete
                or result.status == DialogTurnStatus.Cancelled):
            if DialogExtensions.__send_eoc_to_parent(turn_context):
                end_message_text = (
                    f"Dialog {dialog.id} has **completed**. Sending EndOfConversation."
                )
                await turn_context.send_trace_activity(
                    f"Extension {Dialog.__name__}.run_dialog",
                    label=end_message_text,
                    value=result.result,
                )

                activity = Activity(
                    type=ActivityTypes.end_of_conversation,
                    value=result.result,
                    locale=turn_context.activity.locale,
                )
                await turn_context.send_activity(activity)
Exemple #11
0
    async def _fill_out_user_profile(self, flow: ConversationFlow,
                                     profile: UserProfile,
                                     turn_context: TurnContext):
        # Begins flow process
        user_input = turn_context.activity.text.strip()

        # Ask for date; starts conversation flow
        if flow.last_question_asked == Question.NONE:
            await turn_context.send_activity(
                MessageFactory.text(
                    "Let's get started. What date and time would you like to book your movie for?"
                ))
            flow.last_question_asked = Question.DATE

        # Validate date; ask for movie selection
        elif flow.last_question_asked == Question.DATE:
            # This is where date must be bound to profile.date
            validate_result = self._validate_date(user_input)
            if not validate_result.is_valid:
                await turn_context.send_activity(
                    MessageFactory.text(validate_result.message))
            else:
                profile.date = validate_result.value
                await turn_context.send_activity(
                    MessageFactory.text(
                        f"Great! We have a good selection of movies showing at {profile.date}"
                    ))

                await turn_context.send_activity(
                    MessageFactory.text(
                        "Which movie would you like to watch? Please type the name of the movie as your reply."
                    ))
                # movies attachment
                message = Activity(type=ActivityTypes.message)
                message.text = "Moana, Once Upon a Time in Hollywood, Ready Player One, Sicario, The Girl With the Dragon Tattoo."
                message.attachments = [self._get_inline_attachment()]

                await turn_context.send_activity(message)
                flow.last_question_asked = Question.MOVIE

        # Validate movie; ask for seat reservations
        elif flow.last_question_asked == Question.MOVIE:
            ## This is where movie must be bound to profile.movie
            validate_result = self._validate_movie(user_input)
            if not validate_result.is_valid:
                await turn_context.send_activity(
                    MessageFactory.text(validate_result.message))
            else:
                profile.movie = validate_result.value
                await turn_context.send_activity(
                    MessageFactory.text(
                        f"Sounds good! {profile.movie} is a great pick!"))
                await turn_context.send_activity(
                    MessageFactory.text("How many seats are you reserving?"))
                flow.last_question_asked = Question.SEATS

        # Validate seats; ask about row preferences

        elif flow.last_question_asked == Question.SEATS:
            validate_result = self._validate_seats(user_input)
            if not validate_result.is_valid:
                await turn_context.send_activity(
                    MessageFactory.text(validate_result.message))
            else:
                profile.seats = validate_result.value
                await turn_context.send_activity(
                    MessageFactory.text(
                        f"You are booking {profile.seats} seats."))
                await turn_context.send_activity(
                    MessageFactory.text(
                        "What is your row preference? There are 50 rows in our theater."
                    ))
                flow.last_question_asked = Question.PREFERENCE

        # Validate preferences; ask about email

        elif flow.last_question_asked == Question.PREFERENCE:
            validate_result = self._validate_preference(user_input)
            if not validate_result.is_valid:
                await turn_context.send_activity(
                    MessageFactory.text(validate_result.message))
            else:
                profile.preference = validate_result.value
                await turn_context.send_activity(
                    MessageFactory.text(
                        f"Your row preference has been set as {profile.preference}"
                    ))
                await turn_context.send_activity(
                    MessageFactory.text(
                        "Please enter your email for booking confirmation."))
                flow.last_question_asked = Question.EMAIL

        # Validate email, confirm by displaying all info, wrap up w/ NONE
        elif flow.last_question_asked == Question.EMAIL:
            validate_result = self._validate_email(user_input)
            if not validate_result.is_valid:
                await turn_context.send_activity(
                    MessageFactory.text(validate_result.message))
            else:
                profile.email = validate_result.value
                await turn_context.send_activity(
                    MessageFactory.text(
                        f"You have now completed the booking process."))
                await turn_context.send_activity(
                    MessageFactory.text(
                        f"Booking for {profile.email}: {profile.movie} on {profile.date}"
                    ))
                await turn_context.send_activity(
                    MessageFactory.text(
                        f"You are reserving {profile.seats} seats in row {profile.preference}"
                    ))
                await turn_context.send_activity(
                    MessageFactory.text("Type anything to run the bot again."))
                flow.last_question_asked = Question.NONE  #End of flow, able to restart again
Exemple #12
0
    async def handle_skill_on_turn(
        self, dialog_context: DialogContext
    ) -> DialogTurnResult:
        # the bot is running as a skill.
        turn_context = dialog_context.context

        # Process remote cancellation
        if (
            turn_context.activity.type == ActivityTypes.end_of_conversation
            and dialog_context.active_dialog is not None
            and self.is_from_parent_to_skill(turn_context)
        ):
            # Handle remote cancellation request from parent.
            active_dialog_context = self.get_active_dialog_context(dialog_context)

            remote_cancel_text = "Skill was canceled through an EndOfConversation activity from the parent."
            await turn_context.send_trace_activity(
                f"{self.__class__.__name__}.on_turn_async()",
                label=f"{remote_cancel_text}",
            )

            # Send cancellation message to the top dialog in the stack to ensure all the parents are canceled in the
            # right order.
            return await active_dialog_context.cancel_all_dialogs(True)

        # Handle reprompt
        # Process a reprompt event sent from the parent.
        if (
            turn_context.activity.type == ActivityTypes.event
            and turn_context.activity.name == DialogEvents.reprompt_dialog
        ):
            if not dialog_context.active_dialog:
                return DialogTurnResult(DialogTurnStatus.Empty)

            await dialog_context.reprompt_dialog()
            return DialogTurnResult(DialogTurnStatus.Waiting)

        # Continue execution
        # - This will apply any queued up interruptions and execute the current/next step(s).
        turn_result = await dialog_context.continue_dialog()
        if turn_result.status == DialogTurnStatus.Empty:
            # restart root dialog
            start_message_text = f"Starting {self._root_dialog_id}."
            await turn_context.send_trace_activity(
                f"{self.__class__.__name__}.handle_skill_on_turn_async()",
                label=f"{start_message_text}",
            )
            turn_result = await dialog_context.begin_dialog(self._root_dialog_id)

        await DialogManager.send_state_snapshot_trace(dialog_context, "Skill State")

        if self.should_send_end_of_conversation_to_parent(turn_context, turn_result):
            end_message_text = f"Dialog {self._root_dialog_id} has **completed**. Sending EndOfConversation."
            await turn_context.send_trace_activity(
                f"{self.__class__.__name__}.handle_skill_on_turn_async()",
                label=f"{end_message_text}",
                value=turn_result.result,
            )

            # Send End of conversation at the end.
            activity = Activity(
                type=ActivityTypes.end_of_conversation,
                value=turn_result.result,
                locale=turn_context.activity.locale,
                code=EndOfConversationCodes.completed_successfully
                if turn_result.status == DialogTurnStatus.Complete
                else EndOfConversationCodes.user_cancelled,
            )
            await turn_context.send_activity(activity)

        return turn_result
Exemple #13
0
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

import aiounittest

from botbuilder.core import TurnContext
from botbuilder.core.adapters import TestAdapter
from botbuilder.schema import Activity, ConversationReference, ChannelAccount
from botframework.connector.auth import MicrosoftAppCredentials

RECEIVED_MESSAGE = Activity(type="message", text="received")
UPDATED_ACTIVITY = Activity(type="message", text="update")
DELETED_ACTIVITY_REFERENCE = ConversationReference(activity_id="1234")


class TestTestAdapter(aiounittest.AsyncTestCase):
    async def test_should_call_bog_logic_when_receive_activity_is_called(self):
        async def logic(context: TurnContext):
            assert context
            assert context.activity
            assert context.activity.type == "message"
            assert context.activity.text == "test"
            assert context.activity.id
            assert context.activity.from_property
            assert context.activity.recipient
            assert context.activity.conversation
            assert context.activity.channel_id
            assert context.activity.service_url

        adapter = TestAdapter(logic)
        await adapter.receive_activity("test")
Exemple #14
0
    async def create_conversation(
        self,
        reference: ConversationReference,
        logic: Callable[[TurnContext], Awaitable] = None,
        conversation_parameters: ConversationParameters = None,
        channel_id: str = None,
        service_url: str = None,
        credentials: AppCredentials = None,
    ):
        """
        Starts a new conversation with a user. Used to direct message to a member of a group.

        :param reference: The conversation reference that contains the tenant
        :type reference: :class:`botbuilder.schema.ConversationReference`
        :param logic: The logic to use for the creation of the conversation
        :type logic: :class:`typing.Callable`
        :param conversation_parameters: The information to use to create the conversation
        :type conversation_parameters:
        :param channel_id: The ID for the channel.
        :type channel_id: :class:`typing.str`
        :param service_url: The channel's service URL endpoint.
        :type service_url: :class:`typing.str`
        :param credentials: The application credentials for the bot.
        :type credentials: :class:`botframework.connector.auth.AppCredentials`

        :raises: It raises a generic exception error.

        :return: A task representing the work queued to execute.

        .. remarks::
            To start a conversation, your bot must know its account information and the user's
            account information on that channel.
            Most channels only support initiating a direct message (non-group) conversation.
            The adapter attempts to create a new conversation on the channel, and
            then sends a conversation update activity through its middleware pipeline
            to the the callback method.
            If the conversation is established with the specified users, the ID of the activity
            will contain the ID of the new conversation.
        """
        try:
            if not service_url:
                service_url = reference.service_url
                if not service_url:
                    raise TypeError(
                        "BotFrameworkAdapter.create_conversation(): service_url or reference.service_url is required."
                    )

            if not channel_id:
                channel_id = reference.channel_id
                if not channel_id:
                    raise TypeError(
                        "BotFrameworkAdapter.create_conversation(): channel_id or reference.channel_id is required."
                    )

            parameters = (conversation_parameters if conversation_parameters
                          else ConversationParameters(bot=reference.bot,
                                                      members=[reference.user],
                                                      is_group=False))

            # Mix in the tenant ID if specified. This is required for MS Teams.
            if reference.conversation and reference.conversation.tenant_id:
                # Putting tenant_id in channel_data is a temporary while we wait for the Teams API to be updated
                parameters.channel_data = {
                    "tenant": {
                        "id": reference.conversation.tenant_id
                    }
                }

                # Permanent solution is to put tenant_id in parameters.tenant_id
                parameters.tenant_id = reference.conversation.tenant_id

            # This is different from C# where credentials are required in the method call.
            # Doing this for compatibility.
            app_credentials = (credentials if credentials else await
                               self.__get_app_credentials(
                                   self.settings.app_id,
                                   self.__get_botframework_oauth_scope()))

            # Create conversation
            client = self._get_or_create_connector_client(
                service_url, app_credentials)

            resource_response = await client.conversations.create_conversation(
                parameters)

            event_activity = Activity(
                type=ActivityTypes.event,
                name="CreateConversation",
                channel_id=channel_id,
                service_url=service_url,
                id=resource_response.activity_id
                if resource_response.activity_id else str(uuid.uuid4()),
                conversation=ConversationAccount(
                    id=resource_response.id,
                    tenant_id=parameters.tenant_id,
                ),
                channel_data=parameters.channel_data,
                recipient=parameters.bot,
            )

            context = self._create_context(event_activity)
            context.turn_state[BotAdapter.BOT_CONNECTOR_CLIENT_KEY] = client

            claims_identity = ClaimsIdentity(
                claims={
                    AuthenticationConstants.AUDIENCE_CLAIM:
                    app_credentials.microsoft_app_id,
                    AuthenticationConstants.APP_ID_CLAIM:
                    app_credentials.microsoft_app_id,
                    AuthenticationConstants.SERVICE_URL_CLAIM: service_url,
                },
                is_authenticated=True,
            )
            context.turn_state[BotAdapter.BOT_IDENTITY_KEY] = claims_identity

            return await self.run_pipeline(context, logic)

        except Exception as error:
            raise error
Exemple #15
0
from unittest.mock import Mock

from botbuilder.core import BotFrameworkAdapter, BotFrameworkAdapterSettings, TurnContext
from botbuilder.schema import Activity, ActivityTypes, ConversationAccount, ConversationReference, ChannelAccount
from botframework.connector 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
    async def test_should_recognize_and_use_custom_locale_dict(self, ):
        async def exec_test(turn_context: TurnContext):
            dialog_context = await dialogs.create_context(turn_context)

            results: DialogTurnResult = await dialog_context.continue_dialog()

            if results.status == DialogTurnStatus.Empty:
                options = PromptOptions(
                    prompt=Activity(type=ActivityTypes.message,
                                    text="Please choose a color."),
                    choices=_color_choices,
                )
                await dialog_context.prompt("prompt", options)
            elif results.status == DialogTurnStatus.Complete:
                selected_choice = results.result
                await turn_context.send_activity(selected_choice.value)

            await convo_state.save_changes(turn_context)

        async def validator(prompt: PromptValidatorContext) -> bool:
            assert prompt

            if not prompt.recognized.succeeded:
                await prompt.context.send_activity("Bad input.")

            return prompt.recognized.succeeded

        adapter = TestAdapter(exec_test)

        convo_state = ConversationState(MemoryStorage())
        dialog_state = convo_state.create_property("dialogState")
        dialogs = DialogSet(dialog_state)

        culture = PromptCultureModel(
            locale="custom-locale",
            no_in_language="customNo",
            yes_in_language="customYes",
            separator="customSeparator",
            inline_or="customInlineOr",
            inline_or_more="customInlineOrMore",
        )

        custom_dict = {
            culture.locale:
            ChoiceFactoryOptions(
                inline_or=culture.inline_or,
                inline_or_more=culture.inline_or_more,
                inline_separator=culture.separator,
                include_numbers=True,
            )
        }

        choice_prompt = ChoicePrompt("prompt",
                                     validator,
                                     choice_defaults=custom_dict)
        dialogs.add(choice_prompt)

        step1 = await adapter.send(
            Activity(type=ActivityTypes.message,
                     text="Hello",
                     locale=culture.locale))
        await step1.assert_reply(
            "Please choose a color. (1) redcustomSeparator(2) greencustomInlineOrMore(3) blue"
        )
    async def _send_to_skill(
        self, context: TurnContext, activity: Activity, skill_conversation_id: str
    ) -> Activity:
        if activity.type == ActivityTypes.invoke:
            # Force ExpectReplies for invoke activities so we can get the replies right away and send
            # them back to the channel if needed. This makes sure that the dialog will receive the Invoke
            # response from the skill and any other activities sent, including EoC.
            activity.delivery_mode = DeliveryModes.expect_replies

        # Always save state before forwarding
        # (the dialog stack won't get updated with the skillDialog and things won't work if you don't)
        await self.dialog_options.conversation_state.save_changes(context, True)

        skill_info = self.dialog_options.skill
        response = await self.dialog_options.skill_client.post_activity(
            self.dialog_options.bot_id,
            skill_info.app_id,
            skill_info.skill_endpoint,
            self.dialog_options.skill_host_endpoint,
            skill_conversation_id,
            activity,
        )

        # Inspect the skill response status
        if not 200 <= response.status <= 299:
            raise Exception(
                f'Error invoking the skill id: "{skill_info.id}" at "{skill_info.skill_endpoint}"'
                f" (status is {response.status}). \r\n {response.body}"
            )

        eoc_activity: Activity = None
        if activity.delivery_mode == DeliveryModes.expect_replies and response.body:
            # 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:
                    # Capture the EndOfConversation activity if it was sent from skill
                    eoc_activity = from_skill_activity

                    # The conversation has ended, so cleanup the conversation id
                    await self.dialog_options.conversation_id_factory.delete_conversation_reference(
                        skill_conversation_id
                    )
                elif not sent_invoke_response and await self._intercept_oauth_cards(
                    context, from_skill_activity, self.dialog_options.connection_name
                ):
                    # 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)

        return eoc_activity
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

import aiounittest

from botbuilder.core import TurnContext, MemoryStorage, UserState
from botbuilder.core.adapters import TestAdapter
from botbuilder.schema import Activity, ChannelAccount

RECEIVED_MESSAGE = Activity(
    type="message",
    text="received",
    channel_id="test",
    from_property=ChannelAccount(id="user"),
)
MISSING_CHANNEL_ID = Activity(type="message",
                              text="received",
                              from_property=ChannelAccount(id="user"))
MISSING_FROM_PROPERTY = Activity(type="message",
                                 text="received",
                                 channel_id="test")


class TestUserState(aiounittest.AsyncTestCase):
    storage = MemoryStorage()
    adapter = TestAdapter()
    context = TurnContext(adapter, RECEIVED_MESSAGE)
    user_state = UserState(storage)

    async def test_should_load_and_save_state_from_storage(self):
        await self.user_state.load(self.context)
 async def on_message_activity(self, turn_context: TurnContext):
     # Get the state properties from the turn context.
     profile = await self.profile_accessor.get(turn_context, UserProfile)
     flow = await self.flow_accessor.get(turn_context, ConversationFlow)
     text = turn_context.activity.text.lower()
     if text == "get started" or text == "/start":
         return await turn_context.send_activities([
             Activity(type=ActivityTypes.typing),
             Activity(type="delay", value=1000),
             Activity(
                 type=ActivityTypes.message,
                 text=
                 "Welcome ! Thank you for subscribing.\n\nThis bot will present you a list of words similar to the word that you will type, and you should find the intruder one.\n\nYou can use the built-in dictionary at anytime to search the meanings of a word.\n\nIf you want any help just type 'help me' .\n\nIf you ever want to test your lexical skills, just type a significant word and Let's Start !"
             )
         ])
     elif text == "help me" or text == "/help":
         return await turn_context.send_activity(
             MessageFactory.text(
                 "Intruder Bot\n\nUsage: \n\n _Def word_ You can use the built-in dictionary at anytime to search the meanings of a word.\n\nType anything to start the test, then when the conversation starts, you will be asked to type a word, and we will send you 5 most similar words to that word and an intruder one that you have to detect in order to pass the test. \n\n Your score will be incremented on each correct response, and will be decremented on each incorrect response.\n\n Let's Play !"
             ))
     elif "def" == text.split()[0]:
         if len(text.split()) < 2:
             return await turn_context.send_activity(
                 MessageFactory.text("Please type a word to define !"))
         elif len(text.split()) < 3:
             word = text.split()[1]
             meaning = dic.meaning(word)
             if meaning:
                 df = list(meaning.items())
                 for it in df:
                     await turn_context.send_activity(
                         MessageFactory.text(f"**{it[0]}** : \n\n* " +
                                             " \n\n* ".join(it[1])))
             else:
                 #wordnik_api
                 return await turn_context.send_activity(
                     MessageFactory.text("Please type a significant word !")
                 )
         else:
             return await turn_context.send_activity(
                 MessageFactory.text("Please type just one word !"))
     elif text == "word of the day" or text == "/word_day":
         today = datetime.today().strftime('%Y-%m-%d')
         getwordofday = f"curl -X GET --header 'Accept: application/json' 'https://api.wordnik.com/v4/words.json/wordOfTheDay?date={today}&api_key={api_key}'"
         result = os.popen(getwordofday).read()
         res = json.loads(result)
         await turn_context.send_activity(
             MessageFactory.text(
                 f"The word of today is : **{res['word']}**"))
         for it in res['definitions']:
             await turn_context.send_activity(
                 MessageFactory.text(f"**{it['partOfSpeech']}** : \n\n* " +
                                     it['text']))
         examples = [(example['title'], example['text'])
                     for example in res['examples']]
         for it in range(len(examples)):
             await turn_context.send_activity(
                 MessageFactory.text(
                     f"_Example{it+1}_ \n\n**{examples[it][0]}** : \n\n* " +
                     examples[it][1]))
         if res['note']:
             await turn_context.send_activity(
                 MessageFactory.text("**Note**" + " : \n\n* " + res['note'])
             )
     else:
         await self._fill_out_user_profile(flow, profile, turn_context)
         # Save changes to UserState and ConversationState
         await self.conversation_state.save_changes(turn_context)
         await self.user_state.save_changes(turn_context)
    async def run_dialog(
        dialog: Dialog, turn_context: TurnContext, accessor: StatePropertyAccessor
    ):
        dialog_set = DialogSet(accessor)
        dialog_set.add(dialog)

        dialog_context = await dialog_set.create_context(turn_context)

        claims = turn_context.turn_state.get(BotAdapter.BOT_IDENTITY_KEY)
        if isinstance(claims, ClaimsIdentity) and SkillValidation.is_skill_claim(
            claims.claims
        ):
            # The bot is running as a skill.
            if (
                turn_context.activity.type == ActivityTypes.end_of_conversation
                and dialog_context.stack
                and DialogExtensions.__is_eoc_coming_from_parent(turn_context)
            ):
                remote_cancel_text = "Skill was canceled through an EndOfConversation activity from the parent."
                await turn_context.send_trace_activity(
                    f"Extension {Dialog.__name__}.run_dialog", label=remote_cancel_text,
                )

                await dialog_context.cancel_all_dialogs()
            else:
                # Process a reprompt event sent from the parent.
                if (
                    turn_context.activity.type == ActivityTypes.event
                    and turn_context.activity.name == DialogEvents.reprompt_dialog
                    and dialog_context.stack
                ):
                    await dialog_context.reprompt_dialog()
                    return

                # Run the Dialog with the new message Activity and capture the results
                # so we can send end of conversation if needed.
                result = await dialog_context.continue_dialog()
                if result.status == DialogTurnStatus.Empty:
                    start_message_text = f"Starting {dialog.id}"
                    await turn_context.send_trace_activity(
                        f"Extension {Dialog.__name__}.run_dialog",
                        label=start_message_text,
                    )
                    result = await dialog_context.begin_dialog(dialog.id)

                # Send end of conversation if it is completed or cancelled.
                if (
                    result.status == DialogTurnStatus.Complete
                    or result.status == DialogTurnStatus.Cancelled
                ):
                    end_message_text = f"Dialog {dialog.id} has **completed**. Sending EndOfConversation."
                    await turn_context.send_trace_activity(
                        f"Extension {Dialog.__name__}.run_dialog",
                        label=end_message_text,
                        value=result.result,
                    )

                    activity = Activity(
                        type=ActivityTypes.end_of_conversation, value=result.result
                    )
                    await turn_context.send_activity(activity)

        else:
            # The bot is running as a standard bot.
            results = await dialog_context.continue_dialog()
            if results.status == DialogTurnStatus.Empty:
                await dialog_context.begin_dialog(dialog.id)
 def list_style_none() -> Activity:
     activity = Activity(type=ActivityTypes.message)
     activity.text = text
     return activity
Exemple #22
0
    async def create_conversation(
        self,
        reference: ConversationReference,
        logic: Callable[[TurnContext], Awaitable] = None,
        conversation_parameters: ConversationParameters = None,
    ):
        """
        Starts a new conversation with a user. Used to direct message to a member of a group.

        :param reference: The conversation reference that contains the tenant
        :type reference: :class:`botbuilder.schema.ConversationReference`
        :param logic: The logic to use for the creation of the conversation
        :type logic: :class:`typing.Callable`
        :param conversation_parameters: The information to use to create the conversation
        :type conversation_parameters:

        :raises: It raises a generic exception error.

        :return: A task representing the work queued to execute.

        .. remarks::
            To start a conversation, your bot must know its account information and the user's
            account information on that channel.
            Most channels only support initiating a direct message (non-group) conversation.
            The adapter attempts to create a new conversation on the channel, and
            then sends a conversation update activity through its middleware pipeline
            to the the callback method.
            If the conversation is established with the specified users, the ID of the activity
            will contain the ID of the new conversation.</para>
        """
        try:
            if reference.service_url is None:
                raise TypeError(
                    "BotFrameworkAdapter.create_conversation(): reference.service_url cannot be None."
                )

            # Create conversation
            parameters = (conversation_parameters if conversation_parameters
                          else ConversationParameters(bot=reference.bot,
                                                      members=[reference.user],
                                                      is_group=False))
            client = await self.create_connector_client(reference.service_url)

            # Mix in the tenant ID if specified. This is required for MS Teams.
            if reference.conversation is not None and reference.conversation.tenant_id:
                # Putting tenant_id in channel_data is a temporary while we wait for the Teams API to be updated
                parameters.channel_data = {
                    "tenant": {
                        "id": reference.conversation.tenant_id
                    }
                }

                # Permanent solution is to put tenant_id in parameters.tenant_id
                parameters.tenant_id = reference.conversation.tenant_id

            resource_response = await client.conversations.create_conversation(
                parameters)
            request = TurnContext.apply_conversation_reference(
                Activity(type=ActivityTypes.event, name="CreateConversation"),
                reference,
                is_incoming=True,
            )
            request.conversation = ConversationAccount(
                id=resource_response.id, tenant_id=parameters.tenant_id)
            request.channel_data = parameters.channel_data
            if resource_response.service_url:
                request.service_url = resource_response.service_url

            context = self.create_context(request)
            return await self.run_pipeline(context, logic)

        except Exception as error:
            raise error
    async def process_request(
            self,
            request: ReceiveRequest,
            logger: Logger,  # pylint: disable=unused-argument
            context: object,  # pylint: disable=unused-argument
    ) -> StreamingResponse:
        # pylint: disable=pointless-string-statement
        response = StreamingResponse()

        # We accept all POSTs regardless of path, but anything else requires special treatment.
        if not request.verb == StreamingRequest.POST:
            return self._handle_custom_paths(request, response)

        # Convert the StreamingRequest into an activity the adapter can understand.
        try:
            body_str = await request.read_body_as_str()
        except Exception as error:
            traceback.print_exc()
            response.status_code = int(HTTPStatus.BAD_REQUEST)
            # TODO: log error

            return response

        try:
            # TODO: validate if should use deserialize or from_dict
            body_dict = loads(body_str)
            activity: Activity = Activity.deserialize(body_dict)

            # All activities received by this StreamingRequestHandler will originate from the same channel, but we won't
            # know what that channel is until we've received the first request.
            if not self.service_url:
                self._service_url = activity.service_url

            # If this is the first time the handler has seen this conversation it needs to be added to the dictionary so
            # the adapter is able to route requests to the correct handler.
            if not self.has_conversation(activity.conversation.id):
                self._conversations[activity.conversation.id] = datetime.now()
            """
            Any content sent as part of a StreamingRequest, including the request body
            and inline attachments, appear as streams added to the same collection. The first
            stream of any request will be the body, which is parsed and passed into this method
            as the first argument, 'body'. Any additional streams are inline attachments that need
            to be iterated over and added to the Activity as attachments to be sent to the Bot.
            """

            if len(request.streams) > 1:
                stream_attachments = [
                    Attachment(content_type=stream.content_type,
                               content=stream.stream)
                    for stream in request.streams
                ]

                if activity.attachments:
                    activity.attachments += stream_attachments
                else:
                    activity.attachments = stream_attachments

            # Now that the request has been converted into an activity we can send it to the adapter.
            adapter_response = await self._activity_processor.process_streaming_activity(
                activity, self._bot.on_turn)

            # Now we convert the invokeResponse returned by the adapter into a StreamingResponse we can send back
            # to the channel.
            if not adapter_response:
                response.status_code = int(HTTPStatus.OK)
            else:
                response.status_code = adapter_response.status
                if adapter_response.body:
                    response.set_body(adapter_response.body)

        except Exception as error:
            traceback.print_exc()
            response.status_code = int(HTTPStatus.INTERNAL_SERVER_ERROR)
            response.set_body(str(error))
            # TODO: log error

        return response
 async def test_eoc_activity(self):
     activity = Activity(type=ActivityTypes.end_of_conversation)
     await self.__activity_callback_test(activity)
     assert (activity.caller_id ==
             f"{CallerIdConstants.bot_to_bot_prefix}{self.skill_id}")
Exemple #25
0
    async def _recognize_token(
            self, dialog_context: DialogContext) -> PromptRecognizerResult:
        context = dialog_context.context
        token = None
        if OAuthPrompt._is_token_response_event(context):
            token = context.activity.value

            # fixup the turnContext's state context if this was received from a skill host caller
            state: CallerInfo = dialog_context.active_dialog.state[
                OAuthPrompt.PERSISTED_CALLER]
            if state:
                # set the ServiceUrl to the skill host's Url
                dialog_context.context.activity.service_url = state.caller_service_url
                claims_identity = context.turn_state.get(
                    BotAdapter.BOT_IDENTITY_KEY)
                connector_client = await _UserTokenAccess.create_connector_client(
                    context,
                    dialog_context.context.activity.service_url,
                    claims_identity,
                    state.scope,
                )

                context.turn_state[
                    BotAdapter.BOT_CONNECTOR_CLIENT_KEY] = connector_client

        elif OAuthPrompt._is_teams_verification_invoke(context):
            code = context.activity.value["state"]
            try:
                token = await _UserTokenAccess.get_user_token(
                    context, self._settings, code)
                if token is not None:
                    await context.send_activity(
                        Activity(
                            type="invokeResponse",
                            value=InvokeResponse(int(HTTPStatus.OK)),
                        ))
                else:
                    await context.send_activity(
                        Activity(
                            type="invokeResponse",
                            value=InvokeResponse(int(HTTPStatus.NOT_FOUND)),
                        ))
            except Exception:
                await context.send_activity(
                    Activity(
                        type="invokeResponse",
                        value=InvokeResponse(
                            int(HTTPStatus.INTERNAL_SERVER_ERROR)),
                    ))
        elif self._is_token_exchange_request_invoke(context):
            if isinstance(context.activity.value, dict):
                context.activity.value = TokenExchangeInvokeRequest(
                ).from_dict(context.activity.value)

            if not (context.activity.value and self._is_token_exchange_request(
                    context.activity.value)):
                # Received activity is not a token exchange request.
                await context.send_activity(
                    self._get_token_exchange_invoke_response(
                        int(HTTPStatus.BAD_REQUEST),
                        "The bot received an InvokeActivity that is missing a TokenExchangeInvokeRequest value."
                        " This is required to be sent with the InvokeActivity.",
                    ))
            elif (context.activity.value.connection_name !=
                  self._settings.connection_name):
                # Connection name on activity does not match that of setting.
                await context.send_activity(
                    self._get_token_exchange_invoke_response(
                        int(HTTPStatus.BAD_REQUEST),
                        "The bot received an InvokeActivity with a TokenExchangeInvokeRequest containing a"
                        " ConnectionName that does not match the ConnectionName expected by the bots active"
                        " OAuthPrompt. Ensure these names match when sending the InvokeActivityInvalid"
                        " ConnectionName in the TokenExchangeInvokeRequest",
                    ))
            elif not getattr(context.adapter, "exchange_token"):
                # Token Exchange not supported in the adapter.
                await context.send_activity(
                    self._get_token_exchange_invoke_response(
                        int(HTTPStatus.BAD_GATEWAY),
                        "The bot's BotAdapter does not support token exchange operations."
                        " Ensure the bot's Adapter supports the ExtendedUserTokenProvider interface.",
                    ))

                raise AttributeError(
                    "OAuthPrompt._recognize_token(): not supported by the current adapter."
                )
            else:
                # No errors. Proceed with token exchange.
                token_exchange_response = None
                try:
                    token_exchange_response = await _UserTokenAccess.exchange_token(
                        context,
                        self._settings,
                        TokenExchangeRequest(
                            token=context.activity.value.token),
                    )
                except:
                    # Ignore Exceptions
                    # If token exchange failed for any reason, tokenExchangeResponse above stays null, and
                    # hence we send back a failure invoke response to the caller.
                    pass

                if not token_exchange_response or not token_exchange_response.token:
                    await context.send_activity(
                        self._get_token_exchange_invoke_response(
                            int(HTTPStatus.PRECONDITION_FAILED),
                            "The bot is unable to exchange token. Proceed with regular login.",
                        ))
                else:
                    await context.send_activity(
                        self._get_token_exchange_invoke_response(
                            int(HTTPStatus.OK), None,
                            context.activity.value.id))
                    token = TokenResponse(
                        channel_id=token_exchange_response.channel_id,
                        connection_name=token_exchange_response.
                        connection_name,
                        token=token_exchange_response.token,
                        expiration=None,
                    )
        elif context.activity.type == ActivityTypes.message and context.activity.text:
            match = re.match(r"(?<!\d)\d{6}(?!\d)", context.activity.text)
            if match:
                token = await _UserTokenAccess.get_user_token(
                    context, self._settings, match[0])

        return (PromptRecognizerResult(True, token)
                if token is not None else PromptRecognizerResult())
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

import aiounittest
from botbuilder.schema import Activity, ConversationReference
from botbuilder.core import TurnContext
from botbuilder.core.adapters import TestAdapter
from datetime import datetime

RECEIVED_MESSAGE = Activity(type='message', text='received')
UPDATED_ACTIVITY = Activity(type='message', text='update')
DELETED_ACTIVITY_REFERENCE = ConversationReference(activity_id='1234')


class TestTestAdapter(aiounittest.AsyncTestCase):
    async def test_should_call_bog_logic_when_receive_activity_is_called(self):
        async def logic(context: TurnContext):
            assert context
            assert context.activity
            assert context.activity.type == 'message'
            assert context.activity.text == 'test'
            assert context.activity.id
            assert context.activity.from_property
            assert context.activity.recipient
            assert context.activity.conversation
            assert context.activity.channel_id
            assert context.activity.service_url

        adapter = TestAdapter(logic)
        await adapter.receive_activity('test')
 def create_send_activity() -> Activity:
     return Activity(
         type=ActivityTypes.message,
         delivery_mode=DeliveryModes.expect_replies,
         text=str(uuid.uuid4()),
     )
 async def logic(context: TurnContext):
     nonlocal counter
     counter += 1
     await context.send_activity(
         Activity(type='message', text=str(counter)))
    async def _recognize_token(self,
                               context: TurnContext) -> PromptRecognizerResult:
        token = None
        if OAuthPrompt._is_token_response_event(context):
            token = context.activity.value
        elif OAuthPrompt._is_teams_verification_invoke(context):
            code = context.activity.value["state"]
            try:
                token = await self.get_user_token(context, code)
                if token is not None:
                    await context.send_activity(
                        Activity(
                            type="invokeResponse",
                            value=InvokeResponse(int(HTTPStatus.OK)),
                        ))
                else:
                    await context.send_activity(
                        Activity(
                            type="invokeResponse",
                            value=InvokeResponse(int(HTTPStatus.NOT_FOUND)),
                        ))
            except Exception:
                await context.send_activity(
                    Activity(
                        type="invokeResponse",
                        value=InvokeResponse(
                            int(HTTPStatus.INTERNAL_SERVER_ERROR)),
                    ))
        elif self._is_token_exchange_request_invoke(context):
            if isinstance(context.activity.value, dict):
                context.activity.value = TokenExchangeInvokeRequest(
                ).from_dict(context.activity.value)

            if not (context.activity.value and self._is_token_exchange_request(
                    context.activity.value)):
                # Received activity is not a token exchange request.
                await context.send_activity(
                    self._get_token_exchange_invoke_response(
                        int(HTTPStatus.BAD_REQUEST),
                        "The bot received an InvokeActivity that is missing a TokenExchangeInvokeRequest value."
                        " This is required to be sent with the InvokeActivity.",
                    ))
            elif (context.activity.value.connection_name !=
                  self._settings.connection_name):
                # Connection name on activity does not match that of setting.
                await context.send_activity(
                    self._get_token_exchange_invoke_response(
                        int(HTTPStatus.BAD_REQUEST),
                        "The bot received an InvokeActivity with a TokenExchangeInvokeRequest containing a"
                        " ConnectionName that does not match the ConnectionName expected by the bots active"
                        " OAuthPrompt. Ensure these names match when sending the InvokeActivityInvalid"
                        " ConnectionName in the TokenExchangeInvokeRequest",
                    ))
            elif not getattr(context.adapter, "exchange_token"):
                # Token Exchange not supported in the adapter.
                await context.send_activity(
                    self._get_token_exchange_invoke_response(
                        int(HTTPStatus.BAD_GATEWAY),
                        "The bot's BotAdapter does not support token exchange operations."
                        " Ensure the bot's Adapter supports the ITokenExchangeProvider interface.",
                    ))

                raise AttributeError(
                    "OAuthPrompt.recognize(): not supported by the current adapter."
                )
            else:
                # No errors. Proceed with token exchange.
                extended_user_token_provider: ExtendedUserTokenProvider = context.adapter
                token_exchange_response = await extended_user_token_provider.exchange_token_from_credentials(
                    context,
                    self._settings.oath_app_credentials,
                    self._settings.connection_name,
                    context.activity.from_property.id,
                    TokenExchangeRequest(token=context.activity.value.token),
                )

                if not token_exchange_response or not token_exchange_response.token:
                    await context.send_activity(
                        self._get_token_exchange_invoke_response(
                            int(HTTPStatus.CONFLICT),
                            "The bot is unable to exchange token. Proceed with regular login.",
                        ))
                else:
                    await context.send_activity(
                        self._get_token_exchange_invoke_response(
                            int(HTTPStatus.OK), None,
                            context.activity.value.id))
                    token = TokenResponse(
                        channel_id=token_exchange_response.channel_id,
                        connection_name=token_exchange_response.
                        connection_name,
                        token=token_exchange_response.token,
                        expiration=None,
                    )
        elif context.activity.type == ActivityTypes.message and context.activity.text:
            match = re.match(r"(?<!\d)\d{6}(?!\d)", context.activity.text)
            if match:
                token = await self.get_user_token(context, match[0])

        return (PromptRecognizerResult(True, token)
                if token is not None else PromptRecognizerResult())
Exemple #30
0
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

import pytest

from botbuilder.core import BotContext, BotState, MemoryStorage, TestAdapter
from botbuilder.schema import Activity

RECEIVED_MESSAGE = Activity(type='message', text='received')
STORAGE_KEY = 'stateKey'


def cached_state(context, state_key):
    cached = context.services.get(state_key)
    return cached['state'] if cached is not None else None


def key_factory(context):
    assert context is not None
    return STORAGE_KEY


class TestBotState:
    storage = MemoryStorage()
    adapter = TestAdapter()
    context = BotContext(adapter, RECEIVED_MESSAGE)
    middleware = BotState(storage, key_factory)

    @pytest.mark.asyncio
    async def test_should_return_undefined_from_get_if_nothing_cached(self):
        state = await self.middleware.get(self.context)