-
Notifications
You must be signed in to change notification settings - Fork 294
Added 47.inspection #381
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
Added 47.inspection #381
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
081b5e6
Added 47.inspection, corrected README in 45.state-management
f086caf
Merge branch 'master' into trboehre-47.inspection
tracyboehrer 108e9c6
Merge branch 'master' into trboehre-47.inspection
axelsrz 32ded8c
Changed the on_error function to be unbound for consistency.
d411a46
Merge branch 'master' into trboehre-47.inspection
tracyboehrer File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# Inspection Bot | ||
|
||
Bot Framework v4 Inspection Middleware sample. | ||
|
||
This bot demonstrates a feature called Inspection. This feature allows the Bot Framework Emulator to debug traffic into and out of the bot in addition to looking at the current state of the bot. This is done by having this data sent to the emulator using trace messages. | ||
|
||
This bot has been created using [Bot Framework](https://dev.botframework.com), it shows how to create a simple bot that accepts input from the user and echoes it back. Included in this sample are two counters maintained in User and Conversation state to demonstrate the ability to look at state. | ||
|
||
This runtime behavior is achieved by simply adding a middleware to the Adapter. In this sample you can find that being done in `app.py`. | ||
|
||
More details are available [here](https://github.com/microsoft/BotFramework-Emulator/blob/master/content/CHANNELS.md) | ||
|
||
## 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\47.inspection` 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 version 4.5.0 or greater from [here](https://github.com/Microsoft/BotFramework-Emulator/releases) | ||
|
||
### Connect to the bot using Bot Framework Emulator | ||
|
||
- Launch Bot Framework Emulator | ||
- File -> Open Bot | ||
- Enter a Bot URL of `http://localhost:3978/api/messages` | ||
|
||
### Special Instructions for Running Inspection | ||
|
||
- In the emulator, select Debug -> Start Debugging. | ||
- Enter the endpoint url (http://localhost:8080)/api/messages, and select Connect. | ||
- The result is a trace activity which contains a statement that looks like /INSPECT attach < identifier > | ||
- Right click and copy that response. | ||
- In the original Live Chat session paste the value. | ||
- Now all the traffic will be replicated (as trace activities) to the Emulator Debug tab. | ||
|
||
# Further reading | ||
|
||
- [Getting started with the Bot Inspector](https://github.com/microsoft/BotFramework-Emulator/blob/master/content/CHANNELS.md) | ||
- [Azure Bot Service Introduction](https://docs.microsoft.com/en-us/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0) | ||
- [Bot State](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-storage-concept?view=azure-bot-service-4.0) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
# 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 botbuilder.core.inspection import InspectionMiddleware, InspectionState | ||
from botframework.connector.auth import MicrosoftAppCredentials | ||
from flask import Flask, request, Response | ||
from botbuilder.core import ( | ||
BotFrameworkAdapter, | ||
BotFrameworkAdapterSettings, | ||
ConversationState, | ||
MemoryStorage, | ||
TurnContext, | ||
UserState, | ||
) | ||
from botbuilder.schema import Activity, ActivityTypes | ||
|
||
from bots import EchoBot | ||
|
||
# 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(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) | ||
|
||
# Clear out state | ||
await CONVERSATION_STATE.delete(context) | ||
|
||
# Set the error handler on the Adapter. | ||
# In this case, we want an unbound method, so MethodType is not needed. | ||
ADAPTER.on_turn_error = on_error | ||
|
||
# Create MemoryStorage and state | ||
MEMORY = MemoryStorage() | ||
USER_STATE = UserState(MEMORY) | ||
CONVERSATION_STATE = ConversationState(MEMORY) | ||
|
||
# Create InspectionMiddleware | ||
INSPECTION_MIDDLEWARE = InspectionMiddleware( | ||
inspection_state=InspectionState(MEMORY), | ||
user_state=USER_STATE, | ||
conversation_state=CONVERSATION_STATE, | ||
credentials=MicrosoftAppCredentials( | ||
app_id=APP.config["APP_ID"], | ||
password=APP.config["APP_PASSWORD"] | ||
) | ||
) | ||
ADAPTER.use(INSPECTION_MIDDLEWARE) | ||
|
||
# Create Bot | ||
BOT = EchoBot(CONVERSATION_STATE, USER_STATE) | ||
|
||
|
||
# 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 .echo_bot import EchoBot | ||
|
||
__all__ = ["EchoBot"] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# Copyright (c) Microsoft Corporation. All rights reserved. | ||
# Licensed under the MIT License. | ||
|
||
from botbuilder.core import ActivityHandler, ConversationState, TurnContext, UserState, MessageFactory | ||
from botbuilder.schema import ChannelAccount | ||
|
||
from data_models import CustomState | ||
|
||
|
||
class EchoBot(ActivityHandler): | ||
def __init__(self, conversation_state: ConversationState, user_state: UserState): | ||
if conversation_state is None: | ||
raise TypeError( | ||
"[EchoBot]: Missing parameter. conversation_state is required but None was given" | ||
) | ||
if user_state is None: | ||
raise TypeError( | ||
"[EchoBot]: Missing parameter. user_state is required but None was given" | ||
) | ||
|
||
self.conversation_state = conversation_state | ||
self.user_state = user_state | ||
|
||
self.conversation_state_accessor = self.conversation_state.create_property("CustomState") | ||
self.user_state_accessor = self.user_state.create_property("CustomState") | ||
|
||
async def on_turn(self, turn_context: TurnContext): | ||
await super().on_turn(turn_context) | ||
|
||
await self.conversation_state.save_changes(turn_context) | ||
await self.user_state.save_changes(turn_context) | ||
|
||
async def on_members_added_activity(self, members_added: [ChannelAccount], turn_context: TurnContext): | ||
for member in members_added: | ||
if member.id != turn_context.activity.recipient.id: | ||
await turn_context.send_activity("Hello and welcome!") | ||
|
||
async def on_message_activity(self, turn_context: TurnContext): | ||
# Get the state properties from the turn context. | ||
user_data = await self.user_state_accessor.get(turn_context, CustomState) | ||
conversation_data = await self.conversation_state_accessor.get(turn_context, CustomState) | ||
|
||
await turn_context.send_activity(MessageFactory.text( | ||
f"Echo: {turn_context.activity.text}, " | ||
f"conversation state: {conversation_data.value}, " | ||
f"user state: {user_data.value}")) | ||
|
||
user_data.value = user_data.value + 1 | ||
conversation_data.value = conversation_data.value + 1 | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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", "") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 .custom_state import CustomState | ||
|
||
__all__ = ["CustomState"] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# Copyright (c) Microsoft Corporation. All rights reserved. | ||
# Licensed under the MIT License. | ||
|
||
|
||
class CustomState: | ||
def __init__(self, value: int = 0): | ||
self.value = value |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
botbuilder-core>=4.4.0b1 | ||
flask>=1.0.3 |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.