async def test_basic_attachment_prompt(self): async def exec_test(turn_context: TurnContext): dialog_context = await dialogs.create_context(turn_context) results = await dialog_context.continue_dialog() if results.status == DialogTurnStatus.Empty: options = PromptOptions( prompt=Activity( type=ActivityTypes.message, text="please add an attachment." ) ) await dialog_context.prompt("AttachmentPrompt", options) elif results.status == DialogTurnStatus.Complete: attachment = results.result[0] content = MessageFactory.text(attachment.content) await turn_context.send_activity(content) await convo_state.save_changes(turn_context) # Initialize TestAdapter. adapter = TestAdapter(exec_test) # Create ConversationState with MemoryStorage and register the state as middleware. convo_state = ConversationState(MemoryStorage()) # Create a DialogState property, DialogSet and AttachmentPrompt. dialog_state = convo_state.create_property("dialog_state") dialogs = DialogSet(dialog_state) dialogs.add(AttachmentPrompt("AttachmentPrompt")) # Create incoming activity with attachment. attachment = Attachment(content="some content", content_type="text/plain") attachment_activity = Activity( type=ActivityTypes.message, attachments=[attachment] ) step1 = await adapter.send("hello") step2 = await step1.assert_reply("please add an attachment.") step3 = await step2.send(attachment_activity) await step3.assert_reply("some content")
async def handle_action_step(self, step_context: WaterfallStepContext): action = str(step_context.result.value).lower() if action == "login": return await step_context.begin_dialog(SsoSignInDialog.__name__) if action == "logout": await step_context.context.adapter.sign_out_user( step_context.context, self._connection_name) await step_context.context.send_activity( "You have been signed out.") return await step_context.next(step_context.result) if action == "show token": token = await step_context.context.adapter.get_user_token( step_context.context, self._connection_name) if token is None: await step_context.context.send_activity( "User has no cached token.") else: await step_context.context.send_activity( f"Here is your current SSO token: { token.token }") return await step_context.next(step_context.result) if action in ["call skill (with sso)", "call skill (without sso)"]: begin_skill_activity = Activity(type=ActivityTypes.event, name="Sso") return await step_context.begin_dialog( self.skill_dialog_id, BeginSkillDialogOptions(activity=begin_skill_activity), ) if action == "back": return DialogTurnResult(DialogTurnStatus.Complete) # This should never be hit since the previous prompt validates the choice raise Exception(f"Unrecognized action: {action}")
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)
async def test_do_not_throw_on_null_from(self): telemetry = Mock() my_logger = TelemetryLoggerMiddleware(telemetry, False) adapter = TestAdapter(template_or_conversation=Activity( channel_id="test", recipient=ChannelAccount(id="bot", name="Bot"), conversation=ConversationAccount(id=str(uuid.uuid4())), )) adapter.use(my_logger) async def send_proactive(context: TurnContext): await context.send_activity("proactive") async def logic(context: TurnContext): await adapter.create_conversation( context.activity.channel_id, send_proactive, ) adapter.logic = logic test_flow = TestFlow(None, adapter) await test_flow.send("foo") await test_flow.assert_reply("proactive") telemetry_calls = [ ( TelemetryLoggerConstants.BOT_MSG_RECEIVE_EVENT, { "fromId": None, "conversationName": None, "locale": None, "recipientId": "bot", "recipientName": "Bot", }, ), ] self.assert_telemetry_calls(telemetry, telemetry_calls)
async def test_attachment_prompt_with_validator(self): async def exec_test(turn_context: TurnContext): dc = await dialogs.create_context(turn_context) results = await dc.continue_dialog() if results.status == DialogTurnStatus.Empty: options = PromptOptions(prompt=Activity(type=ActivityTypes.message, text='please add an attachment.')) await dc.prompt('AttachmentPrompt', options) elif results.status == DialogTurnStatus.Complete: attachment = results.result[0] content = MessageFactory.text(attachment.content) await turn_context.send_activity(content) await convo_state.save_changes(turn_context) # Initialize TestAdapter. adapter = TestAdapter(exec_test) # Create ConversationState with MemoryStorage and register the state as middleware. convo_state = ConversationState(MemoryStorage()) # Create a DialogState property, DialogSet and AttachmentPrompt. dialog_state = convo_state.create_property('dialog_state') dialogs = DialogSet(dialog_state) async def aux_validator(prompt_context: PromptValidatorContext): assert prompt_context, 'Validator missing prompt_context' return prompt_context.recognized.succeeded dialogs.add(AttachmentPrompt('AttachmentPrompt', aux_validator)) # Create incoming activity with attachment. attachment = Attachment(content='some content', content_type='text/plain') attachment_activity = Activity(type=ActivityTypes.message, attachments=[attachment]) step1 = await adapter.send('hello') step2 = await step1.assert_reply('please add an attachment.') step3 = await step2.send(attachment_activity) await step3.assert_reply('some content')
async def handle_delete_dialog_step(self, step_context: WaterfallStepContext): channel = step_context.context.activity.channel_id if channel in self._delete_supported: response = await step_context.context.send_activity( MessageFactory.text("I will delete this message in 5 seconds")) time.sleep(5) await step_context.context.delete_activity(response.id) else: await step_context.context.send_activity( MessageFactory.text( f"Delete is not supported in the {channel} channel.")) await step_context.context.send_activity( Activity( type=ActivityTypes.end_of_conversation, code=EndOfConversationCodes.completed_successfully, )) return DialogTurnResult(DialogTurnStatus.Complete)
def messages(): #check the message format (bot supporting file format) #check whether the HTTP type header is json or not if "application/json" in request.headers["content-type"]: jsonmessage = request.json else: #415 is unsupported media type return Response(status=415) #convert json message to activity activity = Activity().deserialize(jsonmessage) #method to send the activity to bot and get the turn_context async def turn_call(turn_context): await ebot.on_turn(turn_context) #Pass this activity to Adapter object #botadapter.process_activity(activity, "",turn_call) task = loop.create_task( botadapter.process_activity(activity, "", turn_call)) loop.run_until_complete(task)
def messages(): """Main bot message handler.""" if request.headers["Content-Type"] == "application/json": body = request.json else: return Response(status=415) activity = Activity().deserialize(body) auth_header = (request.headers["Authorization"] if "Authorization" in request.headers else "") async def aux_func(turn_context): await BOT.on_turn(turn_context) try: task = LOOP.create_task( ADAPTER.process_activity(activity, auth_header, aux_func)) LOOP.run_until_complete(task) return Response(status=201) except Exception as exception: raise exception
def messages(): if request.headers['Content-Type'] == 'application/json': body = request.json else: return Response(status=415) activity = Activity().deserialize(body) auth_header = request.headers[ 'Authorization'] if 'Authorization' in request.headers else '' async def aux_func(turn_context): asyncio.ensure_future(bot.on_turn(turn_context), loop=loop) try: task = asyncio.ensure_future(ADAPTER.process_activity( activity, auth_header, aux_func), loop=loop) loop.run_until_complete(task) return Response(status=201) except Exception as e: raise e
def messages() -> Response: print("HERE") if "application/json" in request.headers["Content-Type"]: body = request.json else: return Response(status=HTTPStatus.UNSUPPORTED_MEDIA_TYPE) print(body) activity = Activity().deserialize(body) print(activity) auth_header = request.headers[ "Authorization"] if "Authorization" in request.headers else "" async def aux_func(turn_context): await bot.on_turn(turn_context) try: task = LOOP.create_task( ADAPTER.process_activity(activity, auth_header, aux_func)) LOOP.run_until_complete(task) return Response(status=HTTPStatus.CREATED) except Exception as exception: raise exception
def test_conversations_create_conversation_with_invalid_bot_id_fails(self): test_object = ChannelAccount(id=RECIPIENT_ID) create_conversation = ConversationParameters( bot=ChannelAccount(id="INVALID"), members=[test_object], activity=Activity( type=ActivityTypes.message, channel_id=CHANNEL_ID, from_property=ChannelAccount(id="INVALID"), recipient=test_object, text="Hi there!", ), ) with pytest.raises(ErrorResponseException) as excinfo: connector = ConnectorClient(self.credentials, base_url=SERVICE_URL) self.loop.run_until_complete( connector.conversations.create_conversation( create_conversation)) assert excinfo.value.error.error.code == "ServiceError" assert "Invalid userId" in str(excinfo.value.error.error.message)
async def interrupt(self, inner_dc: DialogContext) -> DialogTurnResult: LOGGER.debug(msg=f"{CancelAndHelpDialog.__name__}: interrupt") if inner_dc.context.activity.type == ActivityTypes.message: text = inner_dc.context.activity.text.lower() message = Activity( type=ActivityTypes.message, attachments=[CardFactory.adaptive_card(HelpCard)]) if text in (Prompts.HELP.value, Prompts.QUESTION_MARK.value): await inner_dc.context.send_activity(message) return DialogTurnResult(DialogTurnStatus.Waiting) if text in (Prompts.CANCEL.value, Prompts.END.value, Prompts.QUIT.value): cancel_message = MessageFactory.text(messages.CANCELLED, messages.CANCELLED, InputHints.ignoring_input) await inner_dc.context.send_activity(cancel_message) await inner_dc.cancel_all_dialogs() return await inner_dc.replace_dialog(self.initial_dialog_id) return None
async def messages(req: Request) -> Response: # Main bot message handler. if "application/json" in req.headers["Content-Type"]: body = await req.json() else: return Response(status=415) activity = Activity().deserialize(body) auth_header = req.headers[ "Authorization"] if "Authorization" in req.headers else "" try: invoke_response = await ADAPTER.process_activity( activity, auth_header, BOT.on_turn) if invoke_response: return json_response(data=invoke_response.body, status=invoke_response.status) return Response(status=201) except PermissionError: return Response(status=401) except Exception: return Response(status=500)
async def on_error(self, 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) # Send a message to the user await context.send_activity("The bot encounted 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") # Send a trace activity, which will be displayed in Bot Framework Emulator await context.send_activity(trace_activity)
async def test_get_participant(self): adapter = SimpleAdapterWithCreateConversation() activity = Activity( type="message", text="Test-get_participant", channel_id=Channels.ms_teams, from_property=ChannelAccount(aad_object_id="participantId-1"), channel_data={ "meeting": { "id": "meetingId-1" }, "tenant": { "id": "tenantId-1" }, }, service_url="https://test.coffee", ) turn_context = TurnContext(adapter, activity) handler = TeamsActivityHandler() await handler.on_turn(turn_context)
def create_activity_reply(activity: Activity, text: str = None, locale: str = None): return Activity(type=ActivityTypes.message, timestamp=datetime.utcnow(), from_property=ChannelAccount( id=getattr(activity.recipient, 'id', None), name=getattr(activity.recipient, 'name', None)), recipient=ChannelAccount(id=activity.from_property.id, name=activity.from_property.name), reply_to_id=activity.id, service_url=activity.service_url, channel_id=activity.channel_id, conversation=ConversationAccount( is_group=activity.conversation.is_group, id=activity.conversation.id, name=activity.conversation.name), text=text or '', locale=locale or '', attachments=[], entities=[])
async def interrupt(self, inner_dc: DialogContext) -> DialogTurnResult: if inner_dc.context.activity.type == ActivityTypes.message: text = inner_dc.context.activity.text.lower() help_message = Activity( type=ActivityTypes.message, attachments=[CardFactory.adaptive_card(Cards.help_card)], ) if text in ("help", "?"): await inner_dc.context.send_activity(help_message) return DialogTurnResult(DialogTurnStatus.Waiting) cancel_message_text = "Cancelled." cancel_message = MessageFactory.text( cancel_message_text, cancel_message_text, InputHints.ignoring_input ) if text in ("cancel", "quit", "exit"): await inner_dc.context.send_activity(cancel_message) return await inner_dc.cancel_all_dialogs() return None
def messages(): """Main bot message handler.""" if request.headers['Content-Type'] == 'application/json': body = request.json else: return Response(status=415) activity = Activity().deserialize(body) auth_header = request.headers[ 'Authorization'] if 'Authorization' in request.headers else '' async def aux_func(turn_context): await BOT.on_turn(turn_context) try: future = asyncio.ensure_future(ADAPTER.process_activity( activity, auth_header, aux_func), loop=LOOP) LOOP.run_until_complete(future) return Response(status=201) except Exception as exception: raise exception
async def test_message_reaction(self): # Note the code supports multiple adds and removes in the same activity though # a channel may decide to send separate activities for each. For example, Teams # sends separate activities each with a single add and a single remove. # Arrange activity = Activity( type=ActivityTypes.message_reaction, reactions_added=[MessageReaction(type="sad")], reactions_removed=[MessageReaction(type="angry")], ) turn_context = TurnContext(NotImplementedAdapter(), activity) # Act bot = TestingActivityHandler() await bot.on_turn(turn_context) # Assert assert len(bot.record) == 3 assert bot.record[0] == "on_message_reaction_activity" assert bot.record[1] == "on_reactions_added" assert bot.record[2] == "on_reactions_removed"
def messages(self) -> Response: """Main bot message handler that listens for incoming requests.""" if "application/json" in request.headers["Content-Type"]: body = request.json else: return Response(status=415) activity = Activity().deserialize(body) auth_header = (request.headers["Authorization"] if "Authorization" in request.headers else "") async def aux_func(turn_context): await self.bot.on_turn(turn_context) try: task = self.loop.create_task( self.adapter.process_activity(activity, auth_header, aux_func)) self.loop.run_until_complete(task) return Response(status=201) except Exception as exception: raise exception
def test_conversations_get_activity_members_invalid_conversation_id_fails( self): activity = Activity( type=ActivityTypes.message, channel_id=CHANNEL_ID, recipient=ChannelAccount(id=RECIPIENT_ID), from_property=ChannelAccount(id=BOT_ID), text="Test Activity", ) with pytest.raises(ErrorResponseException) as excinfo: connector = ConnectorClient(self.credentials, base_url=SERVICE_URL) response = self.loop.run_until_complete( connector.conversations.send_to_conversation( CONVERSATION_ID, activity)) self.loop.run_until_complete( connector.conversations.get_activity_members( "INVALID_ID", response.id)) assert excinfo.value.error.error.code == "ServiceError" assert "Invalid ConversationId" in str( excinfo.value.error.error.message)
async def test_on_teams_messaging_extension_bot_message_preview_edit_activity( self): # Arrange activity = Activity( type=ActivityTypes.invoke, name="composeExtension/submitAction", value={ "data": { "key": "value" }, "context": { "theme": "dark" }, "commandId": "test_command", "commandContext": "command_context_test", "botMessagePreviewAction": "edit", "botActivityPreview": [{ "id": "activity123" }], "messagePayload": { "id": "payloadid" }, }, ) turn_context = TurnContext(SimpleAdapter(), activity) # Act bot = TestingTeamsActivityHandler() await bot.on_turn(turn_context) # Assert assert len(bot.record) == 3 assert bot.record[0] == "on_invoke_activity" assert bot.record[ 1] == "on_teams_messaging_extension_submit_action_dispatch" assert bot.record[ 2] == "on_teams_messaging_extension_bot_message_preview_edit"
async def test_delivery_mode_normal(self): mock_credential_provider = unittest.mock.create_autospec(CredentialProvider) settings = BotFrameworkAdapterSettings( app_id="bot_id", credential_provider=mock_credential_provider ) adapter = AdapterUnderTest(settings) async def callback(context: TurnContext): await context.send_activity("activity 1") await context.send_activity("activity 2") await context.send_activity("activity 3") inbound_activity = Activity( type=ActivityTypes.message, channel_id="emulator", service_url="http://tempuri.org/whatever", delivery_mode=DeliveryModes.normal, text="hello world", conversation=ConversationAccount(id="conversationId"), ) identity = ClaimsIdentity( claims={ AuthenticationConstants.AUDIENCE_CLAIM: "bot_id", AuthenticationConstants.APP_ID_CLAIM: "bot_id", AuthenticationConstants.VERSION_CLAIM: "1.0", }, is_authenticated=True, ) invoke_response = await adapter.process_activity_with_identity( inbound_activity, identity, callback ) assert not invoke_response assert ( adapter.connector_client_mock.conversations.send_to_conversation.call_count == 3 )
def test_conversations_send_to_conversation_with_attachment(self): card1 = HeroCard( title="A static image", text="JPEG image", images=[ CardImage( url= "https://docs.com/en-us/bot-framework/media/designing-bots/core/dialogs-screens.png" ) ], ) card2 = HeroCard( title="An animation", subtitle="GIF image", images=[CardImage(url="http://i.giphy.com/Ki55RUbOV5njy.gif")], ) activity = Activity( type=ActivityTypes.message, channel_id=CHANNEL_ID, recipient=ChannelAccount(id=RECIPIENT_ID), from_property=ChannelAccount(id=BOT_ID), attachment_layout=AttachmentLayoutTypes.list, attachments=[ Attachment(content_type="application/vnd.card.hero", content=card1), Attachment(content_type="application/vnd.card.hero", content=card2), ], ) connector = ConnectorClient(self.credentials, base_url=SERVICE_URL) response = self.loop.run_until_complete( connector.conversations.send_to_conversation( CONVERSATION_ID, activity)) assert response is not None
async def test_process_activity_creates_correct_creds_and_client(self): bot_app_id = "00000000-0000-0000-0000-000000000001" identity = ClaimsIdentity( claims={ AuthenticationConstants.AUDIENCE_CLAIM: bot_app_id, AuthenticationConstants.APP_ID_CLAIM: bot_app_id, AuthenticationConstants.VERSION_CLAIM: "1.0", }, is_authenticated=True, ) service_url = "https://smba.trafficmanager.net/amer/" async def callback(context: TurnContext): TestBotFrameworkAdapter.get_creds_and_assert_values( context, bot_app_id, AuthenticationConstants.TO_CHANNEL_FROM_BOT_OAUTH_SCOPE, 1, ) TestBotFrameworkAdapter.get_client_and_assert_values( context, bot_app_id, AuthenticationConstants.TO_CHANNEL_FROM_BOT_OAUTH_SCOPE, service_url, 1, ) scope = context.turn_state[BotFrameworkAdapter.BOT_OAUTH_SCOPE_KEY] assert AuthenticationConstants.TO_CHANNEL_FROM_BOT_OAUTH_SCOPE == scope settings = BotFrameworkAdapterSettings(bot_app_id) sut = BotFrameworkAdapter(settings) await sut.process_activity_with_identity( Activity(channel_id="emulator", service_url=service_url, text="test",), identity, callback, )
async def test_retry_activity_prompt(self): async def exec_test(turn_context: TurnContext): dialog_context = await dialogs.create_context(turn_context) results = await dialog_context.continue_dialog() if results.status == DialogTurnStatus.Empty: options = PromptOptions( prompt=Activity( type=ActivityTypes.message, text="please send an event." ) ) await dialog_context.prompt("EventActivityPrompt", options) elif results.status == DialogTurnStatus.Complete: await turn_context.send_activity(results.result) await convo_state.save_changes(turn_context) # Initialize TestAdapter. adapter = TestAdapter(exec_test) # Create ConversationState with MemoryStorage and register the state as middleware. convo_state = ConversationState(MemoryStorage()) # Create a DialogState property, DialogSet and AttachmentPrompt. dialog_state = convo_state.create_property("dialog_state") dialogs = DialogSet(dialog_state) dialogs.add(SimpleActivityPrompt("EventActivityPrompt", validator)) event_activity = Activity(type=ActivityTypes.event, value=2) step1 = await adapter.send("hello") step2 = await step1.assert_reply("please send an event.") step3 = await step2.send("hello again") step4 = await step3.assert_reply( "Please send an 'event'-type Activity with a value of 2." ) step5 = await step4.send(event_activity) await step5.assert_reply("2")
def test_conversations_create_conversation_with_bot_as_only_member_fails( self): test_object = ChannelAccount(id=BOT_ID) sender = ChannelAccount(id=BOT_ID) create_conversation = ConversationParameters( bot=sender, members=[test_object], activity=Activity( type=ActivityTypes.message, channel_id=CHANNEL_ID, from_property=sender, recipient=test_object, text="Hi there!", ), ) with pytest.raises(ErrorResponseException) as excinfo: connector = ConnectorClient(self.credentials, base_url=SERVICE_URL) connector.conversations.create_conversation(create_conversation) assert excinfo.value.error.error.code == "BadArgument" assert "Bots cannot IM other bots" in str( excinfo.value.error.error.message)
async def exec_test(turn_context: TurnContext): dialog_context = await dialogs.create_context(turn_context) results = await dialog_context.continue_dialog() if results.status == DialogTurnStatus.Empty: options = PromptOptions(prompt=Activity( type=ActivityTypes.message, text="please send an event.")) try: await dialog_context.prompt("EventActivityPrompt", options) await dialog_context.replace_dialog( "Non existent id", options) except Exception as err: self.assertIsNotNone(err.data["DialogContext"] # pylint: disable=no-member ) else: raise Exception("Should have thrown an error.") elif results.status == DialogTurnStatus.Complete: await turn_context.send_activity(results.result) await convo_state.save_changes(turn_context)
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)
async def test_on_teams_task_module_submit(self): # Arrange activity = Activity( type=ActivityTypes.invoke, name="task/submit", value={ "data": { "key": "value" }, "context": TaskModuleRequestContext().serialize(), }, ) turn_context = TurnContext(SimpleAdapter(), activity) # Act bot = TestingTeamsActivityHandler() await bot.on_turn(turn_context) # Assert assert len(bot.record) == 2 assert bot.record[0] == "on_invoke_activity" assert bot.record[1] == "on_teams_task_module_submit"