Skip to content

[Teams] Draft action based fetch task scenario #512

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions scenarios/action-based-messaging-extension-fetch-task/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

import json
import sys
from datetime import datetime

from aiohttp import web
from aiohttp.web import Request, Response, json_response
from botbuilder.core import (
BotFrameworkAdapterSettings,
TurnContext,
BotFrameworkAdapter,
)
from botbuilder.schema import Activity, ActivityTypes
from bots import ActionBasedMessagingExtensionFetchTaskBot
from config import DefaultConfig

CONFIG = DefaultConfig()

# Create adapter.
# See https://aka.ms/about-bot-adapter to learn more about how bots work.
SETTINGS = BotFrameworkAdapterSettings(CONFIG.APP_ID, CONFIG.APP_PASSWORD)
ADAPTER = BotFrameworkAdapter(SETTINGS)


# Catch-all for errors.
async def on_error(context: TurnContext, error: Exception):
# This check writes out errors to console log .vs. app insights.
# NOTE: In production environment, you should consider logging this to Azure
# application insights.
print(f"\n [on_turn_error] unhandled error: {error}", file=sys.stderr)

# Send a message to the user
await context.send_activity("The bot encountered an error or bug.")
await context.send_activity(
"To continue to run this bot, please fix the bot source code."
)
# Send a trace activity if we're talking to the Bot Framework Emulator
if context.activity.channel_id == "emulator":
# Create a trace activity that contains the error object
trace_activity = Activity(
label="TurnError",
name="on_turn_error Trace",
timestamp=datetime.utcnow(),
type=ActivityTypes.trace,
value=f"{error}",
value_type="https://www.botframework.com/schemas/error",
)
# Send a trace activity, which will be displayed in Bot Framework Emulator
await context.send_activity(trace_activity)


ADAPTER.on_turn_error = on_error

# Create the Bot
BOT = ActionBasedMessagingExtensionFetchTaskBot()


# Listen for incoming requests on /api/messages
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)


APP = web.Application()
APP.router.add_post("/api/messages", messages)

if __name__ == "__main__":
try:
web.run_app(APP, host="localhost", port=CONFIG.PORT)
except Exception as error:
raise error
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

from .action_based_messaging_extension_fetch_task_bot import (
ActionBasedMessagingExtensionFetchTaskBot,
)

__all__ = ["ActionBasedMessagingExtensionFetchTaskBot"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
# Copyright (c) Microsoft Corp. All rights reserved.
# Licensed under the MIT License.

from typing import List
import random
from botbuilder.core import (
CardFactory,
MessageFactory,
TurnContext,
)
from botbuilder.schema import Attachment
from botbuilder.schema.teams import (
MessagingExtensionAction,
MessagingExtensionActionResponse,
TaskModuleContinueResponse,
MessagingExtensionResult,
TaskModuleTaskInfo,
)
from botbuilder.core.teams import TeamsActivityHandler
from example_data import ExampleData


class ActionBasedMessagingExtensionFetchTaskBot(TeamsActivityHandler):
async def on_message_activity(self, turn_context: TurnContext):
value = turn_context.activity.value
if value is not None:
# This was a message from the card.
answer = value["Answer"]
choices = value["Choices"]
reply = MessageFactory.text(
f"{turn_context.activity.from_property.name} answered '{answer}' and chose '{choices}'."
)
await turn_context.send_activity(reply)
else:
# This is a regular text message.
reply = MessageFactory.text(
"Hello from ActionBasedMessagingExtensionFetchTaskBot."
)
await turn_context.send_activity(reply)

async def on_teams_messaging_extension_fetch_task(
self, turn_context: TurnContext, action: MessagingExtensionAction
) -> MessagingExtensionActionResponse:
card = self._create_adaptive_card_editor()
task_info = TaskModuleTaskInfo(
card=card, height=450, title="Task Module Fetch Example", width=500
)
continue_response = TaskModuleContinueResponse(type="continue", value=task_info)
return MessagingExtensionActionResponse(task=continue_response)

async def on_teams_messaging_extension_submit_action( # pylint: disable=unused-argument
self, turn_context: TurnContext, action: MessagingExtensionAction
) -> MessagingExtensionActionResponse:
question = action.data["Question"]
multi_select = action.data["MultiSelect"]
option1 = action.data["Option1"]
option2 = action.data["Option2"]
option3 = action.data["Option3"]
preview_card = self._create_adaptive_card_preview(
user_text=question,
is_multi_select=multi_select,
option1=option1,
option2=option2,
option3=option3,
)

extension_result = MessagingExtensionResult(
type="botMessagePreview",
activity_preview=MessageFactory.attachment(preview_card),
)
return MessagingExtensionActionResponse(compose_extension=extension_result)

async def on_teams_messaging_extension_bot_message_preview_edit( # pylint: disable=unused-argument
self, turn_context: TurnContext, action: MessagingExtensionAction
) -> MessagingExtensionActionResponse:
activity_preview = action.bot_activity_preview[0]
content = activity_preview.attachments[0].content
data = self._get_example_data(content)
card = self._create_adaptive_card_editor(
data.question,
data.is_multi_select,
data.option1,
data.option2,
data.option3,
)
task_info = TaskModuleTaskInfo(
card=card, height=450, title="Task Module Fetch Example", width=500
)
continue_response = TaskModuleContinueResponse(type="continue", value=task_info)
return MessagingExtensionActionResponse(task=continue_response)

async def on_teams_messaging_extension_bot_message_preview_send( # pylint: disable=unused-argument
self, turn_context: TurnContext, action: MessagingExtensionAction
) -> MessagingExtensionActionResponse:
activity_preview = action.bot_activity_preview[0]
content = activity_preview.attachments[0].content
data = self._get_example_data(content)
card = self._create_adaptive_card_preview(
data.question,
data.is_multi_select,
data.option1,
data.option2,
data.option3,
)
message = MessageFactory.attachment(card)
await turn_context.send_activity(message)

def _get_example_data(self, content: dict) -> ExampleData:
body = content["body"]
question = body[1]["text"]
choice_set = body[3]
multi_select = "isMultiSelect" in choice_set
option1 = choice_set["choices"][0]["value"]
option2 = choice_set["choices"][1]["value"]
option3 = choice_set["choices"][2]["value"]
return ExampleData(question, multi_select, option1, option2, option3)

def _create_adaptive_card_editor(
self,
user_text: str = None,
is_multi_select: bool = False,
option1: str = None,
option2: str = None,
option3: str = None,
) -> Attachment:
return CardFactory.adaptive_card(
{
"actions": [
{
"data": {"submitLocation": "messagingExtensionFetchTask"},
"title": "Submit",
"type": "Action.Submit",
}
],
"body": [
{
"text": "This is an Adaptive Card within a Task Module",
"type": "TextBlock",
"weight": "bolder",
},
{"type": "TextBlock", "text": "Enter text for Question:"},
{
"id": "Question",
"placeholder": "Question text here",
"type": "Input.Text",
"value": user_text,
},
{"type": "TextBlock", "text": "Options for Question:"},
{"type": "TextBlock", "text": "Is Multi-Select:"},
{
"choices": [
{"title": "True", "value": "true"},
{"title": "False", "value": "false"},
],
"id": "MultiSelect",
"isMultiSelect": "false",
"style": "expanded",
"type": "Input.ChoiceSet",
"value": "true" if is_multi_select else "false",
},
{
"id": "Option1",
"placeholder": "Option 1 here",
"type": "Input.Text",
"value": option1,
},
{
"id": "Option2",
"placeholder": "Option 2 here",
"type": "Input.Text",
"value": option2,
},
{
"id": "Option3",
"placeholder": "Option 3 here",
"type": "Input.Text",
"value": option3,
},
],
"type": "AdaptiveCard",
"version": "1.0",
}
)

def _create_adaptive_card_preview(
self,
user_text: str = None,
is_multi_select: bool = False,
option1: str = None,
option2: str = None,
option3: str = None,
) -> Attachment:
return CardFactory.adaptive_card(
{
"actions": [
{
"type": "Action.Submit",
"title": "Submit",
"data": {"submitLocation": "messagingExtensionSubmit"},
}
],
"body": [
{
"text": "Adaptive Card from Task Module",
"type": "TextBlock",
"weight": "bolder",
},
{"text": user_text, "type": "TextBlock", "id": "Question"},
{
"id": "Answer",
"placeholder": "Answer here...",
"type": "Input.Text",
},
{
"choices": [
{"title": option1, "value": option1},
{"title": option2, "value": option2},
{"title": option3, "value": option3},
],
"id": "Choices",
"isMultiSelect": is_multi_select,
"style": "expanded",
"type": "Input.ChoiceSet",
},
],
"type": "AdaptiveCard",
"version": "1.0",
}
)
13 changes: 13 additions & 0 deletions scenarios/action-based-messaging-extension-fetch-task/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env python3
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

import os


class DefaultConfig:
""" Bot Configuration """

PORT = 3978
APP_ID = os.environ.get("MicrosoftAppId", "")
APP_PASSWORD = os.environ.get("MicrosoftAppPassword", "")
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.


class ExampleData(object):
def __init__(
self,
question: str = None,
is_multi_select: bool = False,
option1: str = None,
option2: str = None,
option3: str = None,
):
self.question = question
self.is_multi_select = is_multi_select
self.option1 = option1
self.option2 = option2
self.option3 = option3
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
botbuilder-core>=4.4.0b1
flask>=1.0.3
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading