Skip to content

Added 08.suggested-actions #372

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 7 commits into from
Oct 29, 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
28 changes: 28 additions & 0 deletions samples/08.suggested-actions/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# suggested actions

Bot Framework v4 using adaptive cards bot sample

This bot has been created using [Bot Framework](https://dev.botframework.com), it shows how to use suggested actions. Suggested actions enable your bot to present buttons that the user can tap to provide input.

## Running the sample
- Clone the repository
```bash
git clone https://github.com/Microsoft/botbuilder-python.git
```
- Bring up a terminal, navigate to `botbuilder-python\samples\08.suggested-actions` folder
- In the terminal, type `pip install -r requirements.txt`
- In the terminal, type `python app.py`

## Testing the bot using Bot Framework Emulator
[Microsoft Bot Framework Emulator](https://github.com/microsoft/botframework-emulator) is a desktop application that allows bot developers to test and debug their bots on localhost or running remotely through a tunnel.

- Install the Bot Framework emulator from [here](https://github.com/Microsoft/BotFramework-Emulator/releases)

### Connect to bot using Bot Framework Emulator
- Launch Bot Framework Emulator
- File -> Open Bot
- Paste this URL in the emulator window - http://localhost:3978/api/messages

## Suggested actions

Suggested actions enable your bot to present buttons that the user can tap to provide input. Suggested actions appear close to the composer and enhance user experience.
84 changes: 84 additions & 0 deletions samples/08.suggested-actions/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

import asyncio
import sys
from datetime import datetime
from types import MethodType

from flask import Flask, request, Response
from botbuilder.core import BotFrameworkAdapterSettings, BotFrameworkAdapter, TurnContext
from botbuilder.schema import Activity, ActivityTypes

from bots import SuggestActionsBot

# Create the loop and Flask app
LOOP = asyncio.get_event_loop()
APP = Flask(__name__, instance_relative_config=True)
APP.config.from_object("config.DefaultConfig")

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


# Catch-all for errors.
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 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 = MethodType(on_error, ADAPTER)

# Create Bot
BOT = SuggestActionsBot()


# Listen for incoming requests on /api/messages.
@APP.route("/api/messages", methods=["POST"])
def messages():
# Main bot message handler.
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 ""
)

try:
task = LOOP.create_task(
ADAPTER.process_activity(activity, auth_header, BOT.on_turn)
)
LOOP.run_until_complete(task)
return Response(status=201)
except Exception as exception:
raise exception


if __name__ == "__main__":
try:
APP.run(debug=False, port=APP.config["PORT"]) # nosec debug
except Exception as exception:
raise exception
6 changes: 6 additions & 0 deletions samples/08.suggested-actions/bots/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

from .suggested_actions_bot import SuggestActionsBot

__all__ = ["SuggestActionsBot"]
88 changes: 88 additions & 0 deletions samples/08.suggested-actions/bots/suggested_actions_bot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

from botbuilder.core import ActivityHandler, MessageFactory, TurnContext
from botbuilder.schema import ChannelAccount, CardAction, ActionTypes, SuggestedActions

"""
This bot will respond to the user's input with suggested actions.
Suggested actions enable your bot to present buttons that the user
can tap to provide input.
"""


class SuggestActionsBot(ActivityHandler):
async def on_members_added_activity(self, members_added: [ChannelAccount], turn_context: TurnContext):
"""
Send a welcome message to the user and tell them what actions they may perform to use this bot
"""

return await self._send_welcome_message(turn_context)

async def on_message_activity(self, turn_context: TurnContext):
"""
Respond to the users choice and display the suggested actions again.
"""

text = turn_context.activity.text.lower()
response_text = self._process_input(text)

await turn_context.send_activity(MessageFactory.text(response_text))

return await self._send_suggested_actions(turn_context)

async def _send_welcome_message(self, turn_context: TurnContext):
for member in turn_context.activity.members_added:
if member.id != turn_context.activity.recipient.id:
await turn_context.send_activity(MessageFactory.text(
f"Welcome to SuggestedActionsBot {member.name}. This bot will introduce you to suggestedActions. "
f"Please answer the question: "
))

await self._send_suggested_actions(turn_context)

def _process_input(self, text: str):
color_text = "is the best color, I agree."

if text == "red":
return f"Red {color_text}"

if text == "yellow":
return f"Yellow {color_text}"

if text == "blue":
return f"Blue {color_text}"

return "Please select a color from the suggested action choices"

async def _send_suggested_actions(self, turn_context: TurnContext):
"""
Creates and sends an activity with suggested actions to the user. When the user
clicks one of the buttons the text value from the "CardAction" will be displayed
in the channel just as if the user entered the text. There are multiple
"ActionTypes" that may be used for different situations.
"""

reply = MessageFactory.text("What is your favorite color?")

reply.suggested_actions = SuggestedActions(
actions=[
CardAction(
title="Red",
type=ActionTypes.im_back,
value="Read"
),
CardAction(
title="Yellow",
type=ActionTypes.im_back,
value="Yellow"
),
CardAction(
title="Blue",
type=ActionTypes.im_back,
value="Blue"
)
]
)

return await turn_context.send_activity(reply)
15 changes: 15 additions & 0 deletions samples/08.suggested-actions/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env python3
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

import os

""" Bot Configuration """


class DefaultConfig:
""" Bot Configuration """

PORT = 3978
APP_ID = os.environ.get("MicrosoftAppId", "")
APP_PASSWORD = os.environ.get("MicrosoftAppPassword", "")
2 changes: 2 additions & 0 deletions samples/08.suggested-actions/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
botbuilder-core>=4.4.0b1
flask>=1.0.3