Removed incomplete/wip Python samples
This commit is contained in:
Родитель
b1e16992d8
Коммит
1e259b0d6d
|
@ -1,216 +0,0 @@
|
|||
# Setting up LUIS via CLI:
|
||||
|
||||
This README contains information on how to create and deploy a LUIS application. When the bot is ready to be deployed to production, we recommend creating a LUIS Endpoint Resource for usage with your LUIS App.
|
||||
|
||||
> _For instructions on how to create a LUIS Application via the LUIS portal, see these Quickstart steps:_
|
||||
> 1. _[Quickstart: Create a new app in the LUIS portal][Quickstart-create]_
|
||||
> 2. _[Quickstart: Deploy an app in the LUIS portal][Quickstart-deploy]_
|
||||
|
||||
[Quickstart-create]: https://docs.microsoft.com/azure/cognitive-services/luis/get-started-portal-build-app
|
||||
[Quickstart-deploy]:https://docs.microsoft.com/azure/cognitive-services/luis/get-started-portal-deploy-app
|
||||
|
||||
## Table of Contents:
|
||||
|
||||
- [Prerequisites](#Prerequisites)
|
||||
- [Import a new LUIS Application using a local LUIS application](#Import-a-new-LUIS-Application-using-a-local-LUIS-application)
|
||||
- [How to create a LUIS Endpoint resource in Azure and pair it with a LUIS Application](#How-to-create-a-LUIS-Endpoint-resource-in-Azure-and-pair-it-with-a-LUIS-Application)
|
||||
|
||||
___
|
||||
|
||||
## [Prerequisites](#Table-of-Contents):
|
||||
|
||||
#### Install Azure CLI >=2.0.61:
|
||||
|
||||
Visit the following page to find the correct installer for your OS:
|
||||
- https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest
|
||||
|
||||
#### Install LUIS CLI >=2.4.0:
|
||||
|
||||
Open a CLI of your choice and type the following:
|
||||
|
||||
```bash
|
||||
npm i -g luis-apis@^2.4.0
|
||||
```
|
||||
|
||||
#### LUIS portal account:
|
||||
|
||||
You should already have a LUIS account with either https://luis.ai, https://eu.luis.ai, or https://au.luis.ai. To determine where to create a LUIS account, consider where you will deploy your LUIS applications, and then place them in [the corresponding region][LUIS-Authoring-Regions].
|
||||
|
||||
After you've created your account, you need your [Authoring Key][LUIS-AKey] and a LUIS application ID.
|
||||
|
||||
[LUIS-Authoring-Regions]: https://docs.microsoft.com/azure/cognitive-services/luis/luis-reference-regions#luis-authoring-regions]
|
||||
[LUIS-AKey]: https://docs.microsoft.com/azure/cognitive-services/luis/luis-concept-keys#authoring-key
|
||||
|
||||
___
|
||||
|
||||
## [Import a new LUIS Application using a local LUIS application](#Table-of-Contents)
|
||||
|
||||
### 1. Import the local LUIS application to luis.ai
|
||||
|
||||
```bash
|
||||
luis import application --region "LuisAppAuthoringRegion" --authoringKey "LuisAuthoringKey" --appName "FlightBooking" --in "./cognitiveModels/FlightBooking.json"
|
||||
```
|
||||
|
||||
Outputs the following JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "########-####-####-####-############",
|
||||
"name": "FlightBooking",
|
||||
"description": "A LUIS model that uses intent and entities.",
|
||||
"culture": "en-us",
|
||||
"usageScenario": "",
|
||||
"domain": "",
|
||||
"versionsCount": 1,
|
||||
"createdDateTime": "2019-03-29T18:32:02Z",
|
||||
"endpoints": {},
|
||||
"endpointHitsCount": 0,
|
||||
"activeVersion": "0.1",
|
||||
"ownerEmail": "bot@contoso.com",
|
||||
"tokenizerVersion": "1.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
For the next step, you'll need the `"id"` value for `--appId` and the `"activeVersion"` value for `--versionId`.
|
||||
|
||||
### 2. Train the LUIS Application
|
||||
|
||||
```bash
|
||||
luis train version --region "LuisAppAuthoringRegion" --authoringKey "LuisAuthoringKey" --appId "LuisAppId" --versionId "LuisAppversion" --wait
|
||||
```
|
||||
|
||||
### 3. Publish the LUIS Application
|
||||
|
||||
```bash
|
||||
luis publish version --region "LuisAppAuthoringRegion" --authoringKey "LuisAuthoringKey" --appId "LuisAppId" --versionId "LuisAppversion" --publishRegion "LuisAppPublishRegion"
|
||||
```
|
||||
|
||||
> `--region` corresponds to the region you _author_ your application in. The regions available for this are "westus", "westeurope" and "australiaeast". <br/>
|
||||
> These regions correspond to the three available portals, https://luis.ai, https://eu.luis.ai, or https://au.luis.ai. <br/>
|
||||
> `--publishRegion` corresponds to the region of the endpoint you're publishing to, (e.g. "westus", "southeastasia", "westeurope", "brazilsouth"). <br/>
|
||||
> See the [reference docs][Endpoint-API] for a list of available publish/endpoint regions.
|
||||
|
||||
[Endpoint-API]: https://westus.dev.cognitive.microsoft.com/docs/services/5819c76f40a6350ce09de1ac/operations/5819c77140a63516d81aee78
|
||||
|
||||
Outputs the following:
|
||||
|
||||
```json
|
||||
{
|
||||
"versionId": "0.1",
|
||||
"isStaging": false,
|
||||
"endpointUrl": "https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/########-####-####-####-############",
|
||||
"region": "westus",
|
||||
"assignedEndpointKey": null,
|
||||
"endpointRegion": "westus",
|
||||
"failedRegions": "",
|
||||
"publishedDateTime": "2019-03-29T18:40:32Z",
|
||||
"directVersionPublish": false
|
||||
}
|
||||
```
|
||||
|
||||
To see how to create an LUIS Cognitive Service Resource in Azure, please see [the next README][README-LUIS]. This Resource should be used when you want to move your bot to production. The instructions will show you how to create and pair the resource with a LUIS Application.
|
||||
|
||||
[README-LUIS]: ./README-LUIS.md
|
||||
|
||||
___
|
||||
|
||||
## [How to create a LUIS Endpoint resource in Azure and pair it with a LUIS Application](#Table-of-Contents)
|
||||
|
||||
### 1. Create a new LUIS Cognitive Services resource on Azure via Azure CLI
|
||||
|
||||
> _Note:_ <br/>
|
||||
> _If you don't have a Resource Group in your Azure subscription, you can create one through the Azure portal or through using:_
|
||||
> ```bash
|
||||
> az group create --subscription "AzureSubscriptionGuid" --location "westus" --name "ResourceGroupName"
|
||||
> ```
|
||||
> _To see a list of valid locations, use `az account list-locations`_
|
||||
|
||||
|
||||
```bash
|
||||
# Use Azure CLI to create the LUIS Key resource on Azure
|
||||
az cognitiveservices account create --kind "luis" --name "NewLuisResourceName" --sku "S0" --location "westus" --subscription "AzureSubscriptionGuid" -g "ResourceGroupName"
|
||||
```
|
||||
|
||||
The command will output a response similar to the JSON below:
|
||||
|
||||
```json
|
||||
{
|
||||
"endpoint": "https://westus.api.cognitive.microsoft.com/luis/v2.0",
|
||||
"etag": "\"########-####-####-####-############\"",
|
||||
"id": "/subscriptions/########-####-####-####-############/resourceGroups/ResourceGroupName/providers/Microsoft.CognitiveServices/accounts/NewLuisResourceName",
|
||||
"internalId": "################################",
|
||||
"kind": "luis",
|
||||
"location": "westus",
|
||||
"name": "NewLuisResourceName",
|
||||
"provisioningState": "Succeeded",
|
||||
"resourceGroup": "ResourceGroupName",
|
||||
"sku": {
|
||||
"name": "S0",
|
||||
"tier": null
|
||||
},
|
||||
"tags": null,
|
||||
"type": "Microsoft.CognitiveServices/accounts"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
Take the output from the previous command and create a JSON file in the following format:
|
||||
|
||||
```json
|
||||
{
|
||||
"azureSubscriptionId": "00000000-0000-0000-0000-000000000000",
|
||||
"resourceGroup": "ResourceGroupName",
|
||||
"accountName": "NewLuisResourceName"
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Retrieve ARM access token via Azure CLI
|
||||
|
||||
```bash
|
||||
az account get-access-token --subscription "AzureSubscriptionGuid"
|
||||
```
|
||||
|
||||
This will return an object that looks like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"accessToken": "eyJ0eXAiOiJKVtokentokentokentokentokeng1dCI6Ik4tbEMwbi05REFMcXdodUhZbkhRNjNHZUNYYyIsItokenI6Ik4tbEMwbi05REFMcXdodUhZbkhRNjNHZUNYYyJ9.eyJhdWQiOiJodHRwczovL21hbmFnZW1lbnQuY29yZS53aW5kb3dzLm5ldC8iLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC83MmY5ODhiZi04NmYxLTQxYWYtOTFhYi0yZDdjZDAxMWRiNDcvIiwiaWF0IjoxNTUzODc3MTUwLCJuYmYiOjE1NTM4NzcxNTAsImV4cCI6MTU1Mzg4MTA1MCwiX2NsYWltX25hbWVzIjp7Imdyb3VwcyI6InNyYzEifSwiX2NsYWltX3NvdXJjZXMiOnsic3JjMSI6eyJlbmRwb2ludCI6Imh0dHBzOi8vZ3JhcGgud2luZG93cy5uZXQvNzJmOTg4YmYtODZmMS00MWFmLTkxYWItMmQ3Y2QwMTFkYjQ3L3VzZXJzL2ZmZTQyM2RkLWJhM2YtNDg0Ny04NjgyLWExNTI5MDA4MjM4Ny9nZXRNZW1iZXJPYmplY3RzIn19LCJhY3IiOiIxIiwiYWlvIjoiQVZRQXEvOEtBQUFBeGVUc201NDlhVHg4RE1mMFlRVnhGZmxxOE9RSC9PODR3QktuSmRqV1FqTkkwbmxLYzB0bHJEZzMyMFZ5bWZGaVVBSFBvNUFFUTNHL0FZNDRjdk01T3M0SEt0OVJkcE5JZW9WU0dzd0kvSkk9IiwiYW1yIjpbIndpYSIsIm1mYSJdLCJhcHBpZCI6IjA0YjA3Nzk1LThkZGItNDYxYS1iYmVlLTAyZjllMWJmN2I0NiIsImFwcGlkYWNyIjoiMCIsImRldmljZWlkIjoiNDhmNDVjNjEtMTg3Zi00MjUxLTlmZWItMTllZGFkZmMwMmE3IiwiZmFtaWx5X25hbWUiOiJHdW0iLCJnaXZlbl9uYW1lIjoiU3RldmVuIiwiaXBhZGRyIjoiMTY3LjIyMC4yLjU1IiwibmFtZSI6IlN0ZXZlbiBHdW0iLCJvaWQiOiJmZmU0MjNkZC1iYTNmLTQ4NDctODY4Mi1hMTUyOTAwODIzODciLCJvbnByZW1fc2lkIjoiUy0xLTUtMjEtMjEyNzUyMTE4NC0xNjA0MDEyOTIwLTE4ODc5Mjc1MjctMjYwOTgyODUiLCJwdWlkIjoiMTAwMzdGRkVBMDQ4NjlBNyIsInJoIjoiSSIsInNjcCI6InVzZXJfaW1wZXJzb25hdGlvbiIsInN1YiI6Ik1rMGRNMWszN0U5ckJyMjhieUhZYjZLSU85LXVFQVVkZFVhNWpkSUd1Nk0iLCJ0aWQiOiI3MmY5ODhiZi04NmYxLTQxYWYtOTFhYi0yZDdjZDAxMWRiNDciLCJ1bmlxdWVfbmFtZSI6InN0Z3VtQG1pY3Jvc29mdC5jb20iLCJ1cG4iOiJzdGd1bUBtaWNyb3NvZnQuY29tIiwidXRpIjoiT2w2NGN0TXY4RVNEQzZZQWRqRUFtokenInZlciI6IjEuMCJ9.kFAsEilE0mlS1pcpqxf4rEnRKeYsehyk-gz-zJHUrE__oad3QjgDSBDPrR_ikLdweynxbj86pgG4QFaHURNCeE6SzrbaIrNKw-n9jrEtokenlosOxg_0l2g1LeEUOi5Q4gQREAU_zvSbl-RY6sAadpOgNHtGvz3Rc6FZRITfkckSLmsKAOFoh-aWC6tFKG8P52rtB0qVVRz9tovBeNqkMYL49s9ypduygbXNVwSQhm5JszeWDgrFuVFHBUP_iENCQYGQpEZf_KvjmX1Ur1F9Eh9nb4yI2gFlKncKNsQl-tokenK7-tokentokentokentokentokentokenatoken",
|
||||
"expiresOn": "2200-12-31 23:59:59.999999",
|
||||
"subscription": "AzureSubscriptionGuid",
|
||||
"tenant": "tenant-guid",
|
||||
"tokenType": "Bearer"
|
||||
}
|
||||
```
|
||||
|
||||
The value needed for the next step is the `"accessToken"`.
|
||||
|
||||
### 3. Use `luis add appazureaccount` to pair your LUIS resource with a LUIS Application
|
||||
|
||||
```bash
|
||||
luis add appazureaccount --in "path/to/created/requestBody.json" --appId "LuisAppId" --authoringKey "LuisAuthoringKey" --armToken "accessToken"
|
||||
```
|
||||
|
||||
If successful, it should yield a response like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "Success",
|
||||
"message": "Operation Successful"
|
||||
}
|
||||
```
|
||||
|
||||
### 4. See the LUIS Cognitive Services' keys
|
||||
|
||||
```bash
|
||||
az cognitiveservices account keys list --name "NewLuisResourceName" --subscription "AzureSubscriptionGuid" -g "ResourceGroupName"
|
||||
```
|
||||
|
||||
This will return an object that looks like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"key1": "9a69####dc8f####8eb4####399f####",
|
||||
"key2": "####f99e####4b1a####fb3b####6b9f"
|
||||
}
|
||||
```
|
|
@ -1,60 +0,0 @@
|
|||
# CoreBot
|
||||
|
||||
Bot Framework v4 core bot sample.
|
||||
|
||||
This bot has been created using [Bot Framework](https://dev.botframework.com), it shows how to:
|
||||
|
||||
- Use [LUIS](https://www.luis.ai) to implement core AI capabilities
|
||||
- Implement a multi-turn conversation using Dialogs
|
||||
- Handle user interruptions for such things as `Help` or `Cancel`
|
||||
- Prompt for and validate requests for information from the user
|
||||
|
||||
## Prerequisites
|
||||
|
||||
This sample **requires** prerequisites in order to run.
|
||||
|
||||
### Overview
|
||||
|
||||
This bot uses [LUIS](https://www.luis.ai), an AI based cognitive service, to implement language understanding.
|
||||
|
||||
### Install Python 3.6
|
||||
|
||||
|
||||
### Create a LUIS Application to enable language understanding
|
||||
|
||||
LUIS language model setup, training, and application configuration steps can be found [here](https://docs.microsoft.com/azure/bot-service/bot-builder-howto-v4-luis?view=azure-bot-service-4.0&tabs=cs).
|
||||
|
||||
If you wish to create a LUIS application via the CLI, these steps can be found in the [README-LUIS.md](README-LUIS.md).
|
||||
|
||||
|
||||
### Configure your bot to use your LUIS app
|
||||
|
||||
Update config.py with your newly imported LUIS app id, LUIS API key from https://<LUIS portal>/user/settings, LUIS API host name, ie <your authoring region>.api.cognitive.microsoft.com. LUIS authoring region is listed on https://<LUIS portal>/user/settings.
|
||||
|
||||
|
||||
## Testing the bot using Bot Framework Emulator
|
||||
|
||||
[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.3.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`
|
||||
|
||||
|
||||
## Further reading
|
||||
|
||||
- [Bot Framework Documentation](https://docs.botframework.com)
|
||||
- [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0)
|
||||
- [Dialogs](https://docs.microsoft.com/azure/bot-service/bot-builder-concept-dialog?view=azure-bot-service-4.0)
|
||||
- [Gathering Input Using Prompts](https://docs.microsoft.com/azure/bot-service/bot-builder-prompts?view=azure-bot-service-4.0&tabs=csharp)
|
||||
- [Activity processing](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0)
|
||||
- [Azure Bot Service Introduction](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0)
|
||||
- [Azure Bot Service Documentation](https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0)
|
||||
- [Azure CLI](https://docs.microsoft.com/cli/azure/?view=azure-cli-latest)
|
||||
- [Azure Portal](https://portal.azure.com)
|
||||
- [Language Understanding using LUIS](https://docs.microsoft.com/azure/cognitive-services/luis/)
|
||||
- [Channels and Bot Connector Service](https://docs.microsoft.com/azure/bot-service/bot-concepts?view=azure-bot-service-4.0)
|
|
@ -1,15 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
"""Booking detail."""
|
||||
|
||||
|
||||
class BookingDetails:
|
||||
"""Booking detail implementation"""
|
||||
|
||||
def __init__(
|
||||
self, destination: str = None, origin: str = None, travel_date: str = None
|
||||
):
|
||||
self.destination = destination
|
||||
self.origin = origin
|
||||
self.travel_date = travel_date
|
|
@ -1,8 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
"""bots module."""
|
||||
|
||||
from .dialog_bot import DialogBot
|
||||
from .dialog_and_welcome_bot import DialogAndWelcomeBot
|
||||
|
||||
__all__ = ["DialogBot", "DialogAndWelcomeBot"]
|
|
@ -1,54 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
""" Bot initialization """
|
||||
# pylint: disable=line-too-long
|
||||
import sys
|
||||
import asyncio
|
||||
from django.apps import AppConfig
|
||||
from botbuilder.core import (
|
||||
BotFrameworkAdapter,
|
||||
BotFrameworkAdapterSettings,
|
||||
TurnContext,
|
||||
ConversationState,
|
||||
MemoryStorage,
|
||||
UserState,
|
||||
)
|
||||
from dialogs import MainDialog
|
||||
from bots import DialogAndWelcomeBot
|
||||
import config
|
||||
|
||||
|
||||
class BotConfig(AppConfig):
|
||||
""" Bot initialization """
|
||||
|
||||
name = "bots"
|
||||
appConfig = config.DefaultConfig
|
||||
|
||||
SETTINGS = BotFrameworkAdapterSettings(appConfig.APP_ID, appConfig.APP_PASSWORD)
|
||||
ADAPTER = BotFrameworkAdapter(SETTINGS)
|
||||
LOOP = asyncio.get_event_loop()
|
||||
|
||||
# Create MemoryStorage, UserState and ConversationState
|
||||
memory = MemoryStorage()
|
||||
user_state = UserState(memory)
|
||||
conversation_state = ConversationState(memory)
|
||||
|
||||
dialog = MainDialog(appConfig)
|
||||
bot = DialogAndWelcomeBot(conversation_state, user_state, dialog)
|
||||
|
||||
async def on_error(self, context: TurnContext, error: Exception):
|
||||
"""
|
||||
Catch-all for errors.
|
||||
This check writes out errors to console log
|
||||
NOTE: In production environment, you should consider logging this to Azure
|
||||
application insights.
|
||||
"""
|
||||
print(f"\n [on_turn_error]: { error }", file=sys.stderr)
|
||||
# Send a message to the user
|
||||
await context.send_activity("Oops. Something went wrong!")
|
||||
# Clear out state
|
||||
await self.conversation_state.delete(context)
|
||||
|
||||
def ready(self):
|
||||
self.ADAPTER.on_turn_error = self.on_error
|
|
@ -1,44 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
"""Main dialog to welcome users."""
|
||||
import json
|
||||
import os.path
|
||||
from typing import List
|
||||
from botbuilder.core import TurnContext
|
||||
from botbuilder.schema import Activity, Attachment, ChannelAccount
|
||||
from helpers.activity_helper import create_activity_reply
|
||||
from .dialog_bot import DialogBot
|
||||
|
||||
|
||||
class DialogAndWelcomeBot(DialogBot):
|
||||
"""Main dialog to welcome users implementation."""
|
||||
|
||||
async def on_members_added_activity(
|
||||
self, members_added: List[ChannelAccount], turn_context: TurnContext
|
||||
):
|
||||
for member in members_added:
|
||||
# Greet anyone that was not the target (recipient) of this message.
|
||||
# To learn more about Adaptive Cards, see https://aka.ms/msbot-adaptivecards
|
||||
# for more details.
|
||||
if member.id != turn_context.activity.recipient.id:
|
||||
welcome_card = self.create_adaptive_card_attachment()
|
||||
response = self.create_response(turn_context.activity, welcome_card)
|
||||
await turn_context.send_activity(response)
|
||||
|
||||
def create_response(self, activity: Activity, attachment: Attachment):
|
||||
"""Create an attachment message response."""
|
||||
response = create_activity_reply(activity)
|
||||
response.attachments = [attachment]
|
||||
return response
|
||||
|
||||
# Load attachment from file.
|
||||
def create_adaptive_card_attachment(self):
|
||||
"""Create an adaptive card."""
|
||||
relative_path = os.path.abspath(os.path.dirname(__file__))
|
||||
path = os.path.join(relative_path, "resources/welcomeCard.json")
|
||||
with open(path) as card_file:
|
||||
card = json.load(card_file)
|
||||
|
||||
return Attachment(
|
||||
content_type="application/vnd.microsoft.card.adaptive", content=card
|
||||
)
|
|
@ -1,47 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
"""Implements bot Activity handler."""
|
||||
|
||||
from botbuilder.core import ActivityHandler, ConversationState, UserState, TurnContext
|
||||
from botbuilder.dialogs import Dialog
|
||||
from helpers.dialog_helper import DialogHelper
|
||||
|
||||
|
||||
class DialogBot(ActivityHandler):
|
||||
"""Main activity handler for the bot."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
conversation_state: ConversationState,
|
||||
user_state: UserState,
|
||||
dialog: Dialog,
|
||||
):
|
||||
if conversation_state is None:
|
||||
raise Exception(
|
||||
"[DialogBot]: Missing parameter. conversation_state is required"
|
||||
)
|
||||
if user_state is None:
|
||||
raise Exception("[DialogBot]: Missing parameter. user_state is required")
|
||||
if dialog is None:
|
||||
raise Exception("[DialogBot]: Missing parameter. dialog is required")
|
||||
|
||||
self.conversation_state = conversation_state
|
||||
self.user_state = user_state
|
||||
self.dialog = dialog
|
||||
self.dialogState = self.conversation_state.create_property(
|
||||
"DialogState"
|
||||
) # pylint: disable=C0103
|
||||
|
||||
async def on_turn(self, turn_context: TurnContext):
|
||||
await super().on_turn(turn_context)
|
||||
|
||||
# Save any state changes that might have occured during the turn.
|
||||
await self.conversation_state.save_changes(turn_context, False)
|
||||
await self.user_state.save_changes(turn_context, False)
|
||||
|
||||
async def on_message_activity(self, turn_context: TurnContext):
|
||||
await DialogHelper.run_dialog(
|
||||
self.dialog,
|
||||
turn_context,
|
||||
self.conversation_state.create_property("DialogState"),
|
||||
) # pylint: disable=C0103
|
|
@ -1,46 +0,0 @@
|
|||
{
|
||||
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
|
||||
"type": "AdaptiveCard",
|
||||
"version": "1.0",
|
||||
"body": [
|
||||
{
|
||||
"type": "Image",
|
||||
"url": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQtB3AwMUeNoq4gUBGe6Ocj8kyh3bXa9ZbV7u1fVKQoyKFHdkqU",
|
||||
"size": "stretch"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"spacing": "medium",
|
||||
"size": "default",
|
||||
"weight": "bolder",
|
||||
"text": "Welcome to Bot Framework!",
|
||||
"wrap": true,
|
||||
"maxLines": 0
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"size": "default",
|
||||
"isSubtle": "true",
|
||||
"text": "Now that you have successfully run your bot, follow the links in this Adaptive Card to expand your knowledge of Bot Framework.",
|
||||
"wrap": true,
|
||||
"maxLines": 0
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": "Action.OpenUrl",
|
||||
"title": "Get an overview",
|
||||
"url": "https://docs.microsoft.com/en-us/azure/bot-service/?view=azure-bot-service-4.0"
|
||||
},
|
||||
{
|
||||
"type": "Action.OpenUrl",
|
||||
"title": "Ask a question",
|
||||
"url": "https://stackoverflow.com/questions/tagged/botframework"
|
||||
},
|
||||
{
|
||||
"type": "Action.OpenUrl",
|
||||
"title": "Learn how to deploy",
|
||||
"url": "https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-deploy-azure?view=azure-bot-service-4.0"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
"""
|
||||
Django settings for bots project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 2.2.1.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/2.2/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/2.2/ref/settings/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = "My Secret Key"
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
"django.contrib.admin",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
"bots.bots.BotConfig",
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
]
|
||||
|
||||
ROOT_URLCONF = "bots.urls"
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": [],
|
||||
"APP_DIRS": True,
|
||||
"OPTIONS": {
|
||||
"context_processors": [
|
||||
"django.template.context_processors.debug",
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
]
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = "bots.wsgi.application"
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.sqlite3",
|
||||
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"
|
||||
},
|
||||
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"},
|
||||
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
|
||||
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/2.2/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = "en-us"
|
||||
|
||||
TIME_ZONE = "UTC"
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/2.2/howto/static-files/
|
||||
|
||||
STATIC_URL = "/static/"
|
|
@ -1,15 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
""" URL configuration for bot message handler """
|
||||
|
||||
from django.urls import path
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from . import views
|
||||
|
||||
# pylint:disable=invalid-name
|
||||
urlpatterns = [
|
||||
path("", views.home, name="home"),
|
||||
path("api/messages", csrf_exempt(views.messages), name="messages"),
|
||||
]
|
|
@ -1,53 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
"""
|
||||
This sample shows how to create a bot that demonstrates the following:
|
||||
- Use [LUIS](https://www.luis.ai) to implement core AI capabilities.
|
||||
- Implement a multi-turn conversation using Dialogs.
|
||||
- Handle user interruptions for such things as `Help` or `Cancel`.
|
||||
- Prompt for and validate requests for information from the user.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from django.http import HttpResponse
|
||||
from django.apps import apps
|
||||
from botbuilder.schema import Activity
|
||||
|
||||
# pylint: disable=line-too-long
|
||||
def home():
|
||||
"""Default handler."""
|
||||
return HttpResponse("Hello!")
|
||||
|
||||
|
||||
def messages(request):
|
||||
"""Main bot message handler."""
|
||||
if "application/json" in request.headers["Content-Type"]:
|
||||
body = json.loads(request.body.decode("utf-8"))
|
||||
else:
|
||||
return HttpResponse(status=415)
|
||||
|
||||
activity = Activity().deserialize(body)
|
||||
auth_header = (
|
||||
request.headers["Authorization"] if "Authorization" in request.headers else ""
|
||||
)
|
||||
|
||||
bot_app = apps.get_app_config("bots")
|
||||
bot = bot_app.bot
|
||||
loop = bot_app.LOOP
|
||||
adapter = bot_app.ADAPTER
|
||||
|
||||
async def aux_func(turn_context):
|
||||
await bot.on_turn(turn_context)
|
||||
|
||||
try:
|
||||
task = asyncio.ensure_future(
|
||||
adapter.process_activity(activity, auth_header, aux_func), loop=loop
|
||||
)
|
||||
loop.run_until_complete(task)
|
||||
return HttpResponse(status=201)
|
||||
except Exception as exception:
|
||||
raise exception
|
||||
return HttpResponse("This is message processing!")
|
|
@ -1,19 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
"""
|
||||
WSGI config for bots project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
# pylint:disable=invalid-name
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bots.settings")
|
||||
application = get_wsgi_application()
|
|
@ -1,226 +0,0 @@
|
|||
{
|
||||
"luis_schema_version": "3.2.0",
|
||||
"versionId": "0.1",
|
||||
"name": "Airline Reservation",
|
||||
"desc": "A LUIS model that uses intent and entities.",
|
||||
"culture": "en-us",
|
||||
"tokenizerVersion": "1.0.0",
|
||||
"intents": [
|
||||
{
|
||||
"name": "Book flight"
|
||||
},
|
||||
{
|
||||
"name": "Cancel"
|
||||
},
|
||||
{
|
||||
"name": "None"
|
||||
}
|
||||
],
|
||||
"entities": [],
|
||||
"composites": [
|
||||
{
|
||||
"name": "From",
|
||||
"children": [
|
||||
"Airport"
|
||||
],
|
||||
"roles": []
|
||||
},
|
||||
{
|
||||
"name": "To",
|
||||
"children": [
|
||||
"Airport"
|
||||
],
|
||||
"roles": []
|
||||
}
|
||||
],
|
||||
"closedLists": [
|
||||
{
|
||||
"name": "Airport",
|
||||
"subLists": [
|
||||
{
|
||||
"canonicalForm": "Paris",
|
||||
"list": [
|
||||
"paris"
|
||||
]
|
||||
},
|
||||
{
|
||||
"canonicalForm": "London",
|
||||
"list": [
|
||||
"london"
|
||||
]
|
||||
},
|
||||
{
|
||||
"canonicalForm": "Berlin",
|
||||
"list": [
|
||||
"berlin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"canonicalForm": "New York",
|
||||
"list": [
|
||||
"new york"
|
||||
]
|
||||
}
|
||||
],
|
||||
"roles": []
|
||||
}
|
||||
],
|
||||
"patternAnyEntities": [],
|
||||
"regex_entities": [],
|
||||
"prebuiltEntities": [
|
||||
{
|
||||
"name": "datetimeV2",
|
||||
"roles": []
|
||||
}
|
||||
],
|
||||
"model_features": [],
|
||||
"regex_features": [],
|
||||
"patterns": [],
|
||||
"utterances": [
|
||||
{
|
||||
"text": "book flight from london to paris on feb 14th",
|
||||
"intent": "Book flight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 27,
|
||||
"endPos": 31
|
||||
},
|
||||
{
|
||||
"entity": "From",
|
||||
"startPos": 17,
|
||||
"endPos": 22
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "book flight to berlin on feb 14th",
|
||||
"intent": "Book flight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 15,
|
||||
"endPos": 20
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "book me a flight from london to paris",
|
||||
"intent": "Book flight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "From",
|
||||
"startPos": 22,
|
||||
"endPos": 27
|
||||
},
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 32,
|
||||
"endPos": 36
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "bye",
|
||||
"intent": "Cancel",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "cancel booking",
|
||||
"intent": "Cancel",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "exit",
|
||||
"intent": "Cancel",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "flight to paris",
|
||||
"intent": "Book flight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 10,
|
||||
"endPos": 14
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "flight to paris from london on feb 14th",
|
||||
"intent": "Book flight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 10,
|
||||
"endPos": 14
|
||||
},
|
||||
{
|
||||
"entity": "From",
|
||||
"startPos": 21,
|
||||
"endPos": 26
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "fly from berlin to paris on may 5th",
|
||||
"intent": "Book flight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 19,
|
||||
"endPos": 23
|
||||
},
|
||||
{
|
||||
"entity": "From",
|
||||
"startPos": 9,
|
||||
"endPos": 14
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "go to paris",
|
||||
"intent": "Book flight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 6,
|
||||
"endPos": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "going from paris to berlin",
|
||||
"intent": "Book flight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 20,
|
||||
"endPos": 25
|
||||
},
|
||||
{
|
||||
"entity": "From",
|
||||
"startPos": 11,
|
||||
"endPos": 15
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "ignore",
|
||||
"intent": "Cancel",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "travel to paris",
|
||||
"intent": "Book flight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 10,
|
||||
"endPos": 14
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"settings": []
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
""" Bot Configuration """
|
||||
|
||||
|
||||
class DefaultConfig(object):
|
||||
""" Bot Configuration """
|
||||
|
||||
PORT = 3978
|
||||
APP_ID = ""
|
||||
APP_PASSWORD = ""
|
||||
|
||||
LUIS_APP_ID = ""
|
||||
# LUIS authoring key from LUIS portal or LUIS Cognitive Service subscription key
|
||||
LUIS_API_KEY = ""
|
||||
# LUIS endpoint host name, ie "https://westus.api.cognitive.microsoft.com"
|
||||
LUIS_API_HOST_NAME = ""
|
|
@ -1,265 +0,0 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"groupLocation": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Specifies the location of the Resource Group."
|
||||
}
|
||||
},
|
||||
"groupName": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Specifies the name of the Resource Group."
|
||||
}
|
||||
},
|
||||
"appId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"appSecret": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"botId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable."
|
||||
}
|
||||
},
|
||||
"botSku": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanName": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The name of the App Service Plan."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanSku": {
|
||||
"type": "object",
|
||||
"defaultValue": {
|
||||
"name": "S1",
|
||||
"tier": "Standard",
|
||||
"size": "S1",
|
||||
"family": "S",
|
||||
"capacity": 1
|
||||
},
|
||||
"metadata": {
|
||||
"description": "The SKU of the App Service Plan. Defaults to Standard values."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanLocation": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The location of the App Service Plan. Defaults to \"westus\"."
|
||||
}
|
||||
},
|
||||
"newWebAppName": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"."
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"appServicePlanName": "[parameters('newAppServicePlanName')]",
|
||||
"resourcesLocation": "[parameters('newAppServicePlanLocation')]",
|
||||
"webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]",
|
||||
"siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]",
|
||||
"botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]",
|
||||
"publishingUsername": "[concat('$', parameters('newWebAppName'))]",
|
||||
"resourceGroupId": "[concat(subscription().id, '/resourceGroups/', parameters('groupName'))]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"name": "[parameters('groupName')]",
|
||||
"type": "Microsoft.Resources/resourceGroups",
|
||||
"apiVersion": "2018-05-01",
|
||||
"location": "[parameters('groupLocation')]",
|
||||
"properties": {}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Resources/deployments",
|
||||
"apiVersion": "2018-05-01",
|
||||
"name": "storageDeployment",
|
||||
"resourceGroup": "[parameters('groupName')]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Resources/resourceGroups/', parameters('groupName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"mode": "Incremental",
|
||||
"template": {
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {},
|
||||
"variables": {},
|
||||
"resources": [
|
||||
{
|
||||
"comments": "Create a new Linux App Service Plan if no existing App Service Plan name was passed in.",
|
||||
"type": "Microsoft.Web/serverfarms",
|
||||
"name": "[variables('appServicePlanName')]",
|
||||
"apiVersion": "2018-02-01",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"sku": "[parameters('newAppServicePlanSku')]",
|
||||
"kind": "linux",
|
||||
"properties": {
|
||||
"name": "[variables('appServicePlanName')]",
|
||||
"perSiteScaling": false,
|
||||
"reserved": true,
|
||||
"targetWorkerCount": 0,
|
||||
"targetWorkerSizeId": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"comments": "Create a Web App using a Linux App Service Plan",
|
||||
"type": "Microsoft.Web/sites",
|
||||
"apiVersion": "2015-08-01",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"kind": "app,linux",
|
||||
"dependsOn": [
|
||||
"[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/serverfarms/', variables('appServicePlanName'))]"
|
||||
],
|
||||
"name": "[variables('webAppName')]",
|
||||
"properties": {
|
||||
"name": "[variables('webAppName')]",
|
||||
"hostNameSslStates": [
|
||||
{
|
||||
"name": "[concat(parameters('newWebAppName'), '.azurewebsites.net')]",
|
||||
"sslState": "Disabled",
|
||||
"hostType": "Standard"
|
||||
},
|
||||
{
|
||||
"name": "[concat(parameters('newWebAppName'), '.scm.azurewebsites.net')]",
|
||||
"sslState": "Disabled",
|
||||
"hostType": "Repository"
|
||||
}
|
||||
],
|
||||
"serverFarmId": "[variables('appServicePlanName')]",
|
||||
"siteConfig": {
|
||||
"appSettings": [
|
||||
{
|
||||
"name": "SCM_DO_BUILD_DURING_DEPLOYMENT",
|
||||
"value": "true"
|
||||
},
|
||||
{
|
||||
"name": "MicrosoftAppId",
|
||||
"value": "[parameters('appId')]"
|
||||
},
|
||||
{
|
||||
"name": "MicrosoftAppPassword",
|
||||
"value": "[parameters('appSecret')]"
|
||||
}
|
||||
],
|
||||
"cors": {
|
||||
"allowedOrigins": [
|
||||
"https://botservice.hosting.portal.azure.net",
|
||||
"https://hosting.onecloud.azure-test.net/"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Web/sites/config",
|
||||
"apiVersion": "2016-08-01",
|
||||
"name": "[concat(variables('webAppName'), '/web')]",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"dependsOn": [
|
||||
"[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/sites/', variables('webAppName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"numberOfWorkers": 1,
|
||||
"defaultDocuments": [
|
||||
"Default.htm",
|
||||
"Default.html",
|
||||
"Default.asp",
|
||||
"index.htm",
|
||||
"index.html",
|
||||
"iisstart.htm",
|
||||
"default.aspx",
|
||||
"index.php",
|
||||
"hostingstart.html"
|
||||
],
|
||||
"netFrameworkVersion": "v4.0",
|
||||
"phpVersion": "",
|
||||
"pythonVersion": "",
|
||||
"nodeVersion": "",
|
||||
"linuxFxVersion": "PYTHON|3.7",
|
||||
"requestTracingEnabled": false,
|
||||
"remoteDebuggingEnabled": false,
|
||||
"remoteDebuggingVersion": "VS2017",
|
||||
"httpLoggingEnabled": true,
|
||||
"logsDirectorySizeLimit": 35,
|
||||
"detailedErrorLoggingEnabled": false,
|
||||
"publishingUsername": "[variables('publishingUsername')]",
|
||||
"scmType": "None",
|
||||
"use32BitWorkerProcess": true,
|
||||
"webSocketsEnabled": false,
|
||||
"alwaysOn": false,
|
||||
"appCommandLine": "gunicorn --bind 0.0.0.0 --worker-class aiohttp.worker.GunicornWebWorker --timeout 600 app:APP",
|
||||
"managedPipelineMode": "Integrated",
|
||||
"virtualApplications": [
|
||||
{
|
||||
"virtualPath": "/",
|
||||
"physicalPath": "site\\wwwroot",
|
||||
"preloadEnabled": false,
|
||||
"virtualDirectories": null
|
||||
}
|
||||
],
|
||||
"winAuthAdminState": 0,
|
||||
"winAuthTenantState": 0,
|
||||
"customAppPoolIdentityAdminState": false,
|
||||
"customAppPoolIdentityTenantState": false,
|
||||
"loadBalancing": "LeastRequests",
|
||||
"routingRules": [],
|
||||
"experiments": {
|
||||
"rampUpRules": []
|
||||
},
|
||||
"autoHealEnabled": false,
|
||||
"vnetName": "",
|
||||
"minTlsVersion": "1.2",
|
||||
"ftpsState": "AllAllowed",
|
||||
"reservedInstanceCount": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2021-03-01",
|
||||
"type": "Microsoft.BotService/botServices",
|
||||
"name": "[parameters('botId')]",
|
||||
"location": "global",
|
||||
"kind": "azurebot",
|
||||
"sku": {
|
||||
"name": "[parameters('botSku')]"
|
||||
},
|
||||
"properties": {
|
||||
"name": "[parameters('botId')]",
|
||||
"displayName": "[parameters('botId')]",
|
||||
"iconUrl": "https://docs.botframework.com/static/devportal/client/images/bot-framework-default.png",
|
||||
"endpoint": "[variables('botEndpoint')]",
|
||||
"msaAppId": "[parameters('appId')]",
|
||||
"luisAppIds": [],
|
||||
"schemaTransformationVersion": "1.3",
|
||||
"isCmekEnabled": false,
|
||||
"isIsolated": false
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/sites/', variables('webAppName'))]"
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputs": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,243 +0,0 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"appId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"appSecret": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"botId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable."
|
||||
}
|
||||
},
|
||||
"botSku": {
|
||||
"defaultValue": "F0",
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanName": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "The name of the new App Service Plan."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanSku": {
|
||||
"type": "object",
|
||||
"defaultValue": {
|
||||
"name": "S1",
|
||||
"tier": "Standard",
|
||||
"size": "S1",
|
||||
"family": "S",
|
||||
"capacity": 1
|
||||
},
|
||||
"metadata": {
|
||||
"description": "The SKU of the App Service Plan. Defaults to Standard values."
|
||||
}
|
||||
},
|
||||
"appServicePlanLocation": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The location of the App Service Plan."
|
||||
}
|
||||
},
|
||||
"existingAppServicePlan": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "Name of the existing App Service Plan used to create the Web App for the bot."
|
||||
}
|
||||
},
|
||||
"newWebAppName": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"."
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"defaultAppServicePlanName": "[if(empty(parameters('existingAppServicePlan')), 'createNewAppServicePlan', parameters('existingAppServicePlan'))]",
|
||||
"useExistingAppServicePlan": "[not(equals(variables('defaultAppServicePlanName'), 'createNewAppServicePlan'))]",
|
||||
"servicePlanName": "[if(variables('useExistingAppServicePlan'), parameters('existingAppServicePlan'), parameters('newAppServicePlanName'))]",
|
||||
"publishingUsername": "[concat('$', parameters('newWebAppName'))]",
|
||||
"resourcesLocation": "[parameters('appServicePlanLocation')]",
|
||||
"webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]",
|
||||
"siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]",
|
||||
"botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"comments": "Create a new Linux App Service Plan if no existing App Service Plan name was passed in.",
|
||||
"type": "Microsoft.Web/serverfarms",
|
||||
"apiVersion": "2016-09-01",
|
||||
"name": "[variables('servicePlanName')]",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"sku": "[parameters('newAppServicePlanSku')]",
|
||||
"kind": "linux",
|
||||
"properties": {
|
||||
"name": "[variables('servicePlanName')]",
|
||||
"perSiteScaling": false,
|
||||
"reserved": true,
|
||||
"targetWorkerCount": 0,
|
||||
"targetWorkerSizeId": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"comments": "Create a Web App using a Linux App Service Plan",
|
||||
"type": "Microsoft.Web/sites",
|
||||
"apiVersion": "2016-08-01",
|
||||
"name": "[variables('webAppName')]",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]"
|
||||
],
|
||||
"kind": "app,linux",
|
||||
"properties": {
|
||||
"enabled": true,
|
||||
"hostNameSslStates": [
|
||||
{
|
||||
"name": "[concat(parameters('newWebAppName'), '.azurewebsites.net')]",
|
||||
"sslState": "Disabled",
|
||||
"hostType": "Standard"
|
||||
},
|
||||
{
|
||||
"name": "[concat(parameters('newWebAppName'), '.scm.azurewebsites.net')]",
|
||||
"sslState": "Disabled",
|
||||
"hostType": "Repository"
|
||||
}
|
||||
],
|
||||
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]",
|
||||
"reserved": true,
|
||||
"scmSiteAlsoStopped": false,
|
||||
"clientAffinityEnabled": false,
|
||||
"clientCertEnabled": false,
|
||||
"hostNamesDisabled": false,
|
||||
"containerSize": 0,
|
||||
"dailyMemoryTimeQuota": 0,
|
||||
"httpsOnly": false,
|
||||
"siteConfig": {
|
||||
"appSettings": [
|
||||
{
|
||||
"name": "MicrosoftAppId",
|
||||
"value": "[parameters('appId')]"
|
||||
},
|
||||
{
|
||||
"name": "MicrosoftAppPassword",
|
||||
"value": "[parameters('appSecret')]"
|
||||
},
|
||||
{
|
||||
"name": "SCM_DO_BUILD_DURING_DEPLOYMENT",
|
||||
"value": "true"
|
||||
}
|
||||
],
|
||||
"cors": {
|
||||
"allowedOrigins": [
|
||||
"https://botservice.hosting.portal.azure.net",
|
||||
"https://hosting.onecloud.azure-test.net/"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Web/sites/config",
|
||||
"apiVersion": "2016-08-01",
|
||||
"name": "[concat(variables('webAppName'), '/web')]",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Web/sites', variables('webAppName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"numberOfWorkers": 1,
|
||||
"defaultDocuments": [
|
||||
"Default.htm",
|
||||
"Default.html",
|
||||
"Default.asp",
|
||||
"index.htm",
|
||||
"index.html",
|
||||
"iisstart.htm",
|
||||
"default.aspx",
|
||||
"index.php",
|
||||
"hostingstart.html"
|
||||
],
|
||||
"netFrameworkVersion": "v4.0",
|
||||
"phpVersion": "",
|
||||
"pythonVersion": "",
|
||||
"nodeVersion": "",
|
||||
"linuxFxVersion": "PYTHON|3.7",
|
||||
"requestTracingEnabled": false,
|
||||
"remoteDebuggingEnabled": false,
|
||||
"remoteDebuggingVersion": "VS2017",
|
||||
"httpLoggingEnabled": true,
|
||||
"logsDirectorySizeLimit": 35,
|
||||
"detailedErrorLoggingEnabled": false,
|
||||
"publishingUsername": "[variables('publishingUsername')]",
|
||||
"scmType": "None",
|
||||
"use32BitWorkerProcess": true,
|
||||
"webSocketsEnabled": false,
|
||||
"alwaysOn": false,
|
||||
"appCommandLine": "gunicorn --bind 0.0.0.0 --worker-class aiohttp.worker.GunicornWebWorker --timeout 600 app:APP",
|
||||
"managedPipelineMode": "Integrated",
|
||||
"virtualApplications": [
|
||||
{
|
||||
"virtualPath": "/",
|
||||
"physicalPath": "site\\wwwroot",
|
||||
"preloadEnabled": false,
|
||||
"virtualDirectories": null
|
||||
}
|
||||
],
|
||||
"winAuthAdminState": 0,
|
||||
"winAuthTenantState": 0,
|
||||
"customAppPoolIdentityAdminState": false,
|
||||
"customAppPoolIdentityTenantState": false,
|
||||
"loadBalancing": "LeastRequests",
|
||||
"routingRules": [],
|
||||
"experiments": {
|
||||
"rampUpRules": []
|
||||
},
|
||||
"autoHealEnabled": false,
|
||||
"vnetName": "",
|
||||
"minTlsVersion": "1.2",
|
||||
"ftpsState": "AllAllowed",
|
||||
"reservedInstanceCount": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2021-03-01",
|
||||
"type": "Microsoft.BotService/botServices",
|
||||
"name": "[parameters('botId')]",
|
||||
"location": "global",
|
||||
"kind": "azurebot",
|
||||
"sku": {
|
||||
"name": "[parameters('botSku')]"
|
||||
},
|
||||
"properties": {
|
||||
"name": "[parameters('botId')]",
|
||||
"displayName": "[parameters('botId')]",
|
||||
"iconUrl": "https://docs.botframework.com/static/devportal/client/images/bot-framework-default.png",
|
||||
"endpoint": "[variables('botEndpoint')]",
|
||||
"msaAppId": "[parameters('appId')]",
|
||||
"luisAppIds": [],
|
||||
"schemaTransformationVersion": "1.3",
|
||||
"isCmekEnabled": false,
|
||||
"isIsolated": false
|
||||
},
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Web/sites/', variables('webAppName'))]"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
"""Dialogs module"""
|
||||
from .booking_dialog import BookingDialog
|
||||
from .cancel_and_help_dialog import CancelAndHelpDialog
|
||||
from .date_resolver_dialog import DateResolverDialog
|
||||
from .main_dialog import MainDialog
|
||||
|
||||
__all__ = ["BookingDialog", "CancelAndHelpDialog", "DateResolverDialog", "MainDialog"]
|
|
@ -1,119 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
"""Flight booking dialog."""
|
||||
|
||||
from botbuilder.dialogs import WaterfallDialog, WaterfallStepContext, DialogTurnResult
|
||||
from botbuilder.dialogs.prompts import ConfirmPrompt, TextPrompt, PromptOptions
|
||||
from botbuilder.core import MessageFactory
|
||||
from datatypes_date_time.timex import Timex
|
||||
from .cancel_and_help_dialog import CancelAndHelpDialog
|
||||
from .date_resolver_dialog import DateResolverDialog
|
||||
|
||||
|
||||
class BookingDialog(CancelAndHelpDialog):
|
||||
"""Flight booking implementation."""
|
||||
|
||||
def __init__(self, dialog_id: str = None):
|
||||
super(BookingDialog, self).__init__(dialog_id or BookingDialog.__name__)
|
||||
|
||||
self.add_dialog(TextPrompt(TextPrompt.__name__))
|
||||
# self.add_dialog(ConfirmPrompt(ConfirmPrompt.__name__))
|
||||
self.add_dialog(DateResolverDialog(DateResolverDialog.__name__))
|
||||
self.add_dialog(
|
||||
WaterfallDialog(
|
||||
WaterfallDialog.__name__,
|
||||
[
|
||||
self.destination_step,
|
||||
self.origin_step,
|
||||
self.travel_date_step,
|
||||
# self.confirm_step,
|
||||
self.final_step,
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
self.initial_dialog_id = WaterfallDialog.__name__
|
||||
|
||||
async def destination_step(
|
||||
self, step_context: WaterfallStepContext
|
||||
) -> DialogTurnResult:
|
||||
"""Prompt for destination."""
|
||||
booking_details = step_context.options
|
||||
|
||||
if booking_details.destination is None:
|
||||
return await step_context.prompt(
|
||||
TextPrompt.__name__,
|
||||
PromptOptions(
|
||||
prompt=MessageFactory.text("To what city would you like to travel?")
|
||||
),
|
||||
) # pylint: disable=line-too-long,bad-continuation
|
||||
else:
|
||||
return await step_context.next(booking_details.destination)
|
||||
|
||||
async def origin_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
|
||||
"""Prompt for origin city."""
|
||||
booking_details = step_context.options
|
||||
|
||||
# Capture the response to the previous step's prompt
|
||||
booking_details.destination = step_context.result
|
||||
if booking_details.origin is None:
|
||||
return await step_context.prompt(
|
||||
TextPrompt.__name__,
|
||||
PromptOptions(
|
||||
prompt=MessageFactory.text("From what city will you be travelling?")
|
||||
),
|
||||
) # pylint: disable=line-too-long,bad-continuation
|
||||
else:
|
||||
return await step_context.next(booking_details.origin)
|
||||
|
||||
async def travel_date_step(
|
||||
self, step_context: WaterfallStepContext
|
||||
) -> DialogTurnResult:
|
||||
"""Prompt for travel date.
|
||||
This will use the DATE_RESOLVER_DIALOG."""
|
||||
|
||||
booking_details = step_context.options
|
||||
|
||||
# Capture the results of the previous step
|
||||
booking_details.origin = step_context.result
|
||||
if not booking_details.travel_date or self.is_ambiguous(
|
||||
booking_details.travel_date
|
||||
):
|
||||
return await step_context.begin_dialog(
|
||||
DateResolverDialog.__name__, booking_details.travel_date
|
||||
) # pylint: disable=line-too-long
|
||||
else:
|
||||
return await step_context.next(booking_details.travel_date)
|
||||
|
||||
async def confirm_step(
|
||||
self, step_context: WaterfallStepContext
|
||||
) -> DialogTurnResult:
|
||||
"""Confirm the information the user has provided."""
|
||||
booking_details = step_context.options
|
||||
|
||||
# Capture the results of the previous step
|
||||
booking_details.travel_date = step_context.result
|
||||
msg = (
|
||||
f"Please confirm, I have you traveling to: { booking_details.destination }"
|
||||
f" from: { booking_details.origin } on: { booking_details.travel_date}."
|
||||
)
|
||||
|
||||
# Offer a YES/NO prompt.
|
||||
return await step_context.prompt(
|
||||
ConfirmPrompt.__name__, PromptOptions(prompt=MessageFactory.text(msg))
|
||||
)
|
||||
|
||||
async def final_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
|
||||
"""Complete the interaction and end the dialog."""
|
||||
if step_context.result:
|
||||
booking_details = step_context.options
|
||||
booking_details.travel_date = step_context.result
|
||||
|
||||
return await step_context.end_dialog(booking_details)
|
||||
else:
|
||||
return await step_context.end_dialog()
|
||||
|
||||
def is_ambiguous(self, timex: str) -> bool:
|
||||
"""Ensure time is correct."""
|
||||
timex_property = Timex(timex)
|
||||
return "definite" not in timex_property.types
|
|
@ -1,45 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
"""Handle cancel and help intents."""
|
||||
from botbuilder.dialogs import (
|
||||
ComponentDialog,
|
||||
DialogContext,
|
||||
DialogTurnResult,
|
||||
DialogTurnStatus,
|
||||
)
|
||||
from botbuilder.schema import ActivityTypes
|
||||
|
||||
|
||||
class CancelAndHelpDialog(ComponentDialog):
|
||||
"""Implementation of handling cancel and help."""
|
||||
|
||||
async def on_begin_dialog(
|
||||
self, inner_dc: DialogContext, options: object
|
||||
) -> DialogTurnResult:
|
||||
result = await self.interrupt(inner_dc)
|
||||
if result is not None:
|
||||
return result
|
||||
|
||||
return await super(CancelAndHelpDialog, self).on_begin_dialog(inner_dc, options)
|
||||
|
||||
async def on_continue_dialog(self, inner_dc: DialogContext) -> DialogTurnResult:
|
||||
result = await self.interrupt(inner_dc)
|
||||
if result is not None:
|
||||
return result
|
||||
|
||||
return await super(CancelAndHelpDialog, self).on_continue_dialog(inner_dc)
|
||||
|
||||
async def interrupt(self, inner_dc: DialogContext) -> DialogTurnResult:
|
||||
"""Detect interruptions."""
|
||||
if inner_dc.context.activity.type == ActivityTypes.message:
|
||||
text = inner_dc.context.activity.text.lower()
|
||||
|
||||
if text == "help" or text == "?":
|
||||
await inner_dc.context.send_activity("Show Help...")
|
||||
return DialogTurnResult(DialogTurnStatus.Waiting)
|
||||
|
||||
if text == "cancel" or text == "quit":
|
||||
await inner_dc.context.send_activity("Cancelling")
|
||||
return await inner_dc.cancel_all_dialogs()
|
||||
|
||||
return None
|
|
@ -1,82 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
"""Handle date/time resolution for booking dialog."""
|
||||
from botbuilder.core import MessageFactory
|
||||
from botbuilder.dialogs import WaterfallDialog, DialogTurnResult, WaterfallStepContext
|
||||
from botbuilder.dialogs.prompts import (
|
||||
DateTimePrompt,
|
||||
PromptValidatorContext,
|
||||
PromptOptions,
|
||||
DateTimeResolution,
|
||||
)
|
||||
from datatypes_date_time.timex import Timex
|
||||
from .cancel_and_help_dialog import CancelAndHelpDialog
|
||||
|
||||
|
||||
class DateResolverDialog(CancelAndHelpDialog):
|
||||
"""Resolve the date"""
|
||||
|
||||
def __init__(self, dialog_id: str = None):
|
||||
super(DateResolverDialog, self).__init__(
|
||||
dialog_id or DateResolverDialog.__name__
|
||||
)
|
||||
|
||||
self.add_dialog(
|
||||
DateTimePrompt(
|
||||
DateTimePrompt.__name__, DateResolverDialog.datetime_prompt_validator
|
||||
)
|
||||
)
|
||||
self.add_dialog(
|
||||
WaterfallDialog(
|
||||
WaterfallDialog.__name__ + "2", [self.initial_step, self.final_step]
|
||||
)
|
||||
)
|
||||
|
||||
self.initial_dialog_id = WaterfallDialog.__name__ + "2"
|
||||
|
||||
async def initial_step(
|
||||
self, step_context: WaterfallStepContext
|
||||
) -> DialogTurnResult:
|
||||
"""Prompt for the date."""
|
||||
timex = step_context.options
|
||||
|
||||
prompt_msg = "On what date would you like to travel?"
|
||||
reprompt_msg = (
|
||||
"I'm sorry, for best results, please enter your travel "
|
||||
"date including the month, day and year."
|
||||
)
|
||||
|
||||
if timex is None:
|
||||
# We were not given any date at all so prompt the user.
|
||||
return await step_context.prompt(
|
||||
DateTimePrompt.__name__,
|
||||
PromptOptions( # pylint: disable=bad-continuation
|
||||
prompt=MessageFactory.text(prompt_msg),
|
||||
retry_prompt=MessageFactory.text(reprompt_msg),
|
||||
),
|
||||
)
|
||||
else:
|
||||
# We have a Date we just need to check it is unambiguous.
|
||||
if "definite" in Timex(timex).types:
|
||||
# This is essentially a "reprompt" of the data we were given up front.
|
||||
return await step_context.prompt(
|
||||
DateTimePrompt.__name__, PromptOptions(prompt=reprompt_msg)
|
||||
)
|
||||
else:
|
||||
return await step_context.next(DateTimeResolution(timex=timex))
|
||||
|
||||
async def final_step(self, step_context: WaterfallStepContext):
|
||||
"""Cleanup - set final return value and end dialog."""
|
||||
timex = step_context.result[0].timex
|
||||
return await step_context.end_dialog(timex)
|
||||
|
||||
@staticmethod
|
||||
async def datetime_prompt_validator(prompt_context: PromptValidatorContext) -> bool:
|
||||
""" Validate the date provided is in proper form. """
|
||||
if prompt_context.recognized.succeeded:
|
||||
timex = prompt_context.recognized.value[0].timex.split("T")[0]
|
||||
|
||||
# TODO: Needs TimexProperty
|
||||
return "definite" in Timex(timex).types
|
||||
|
||||
return False
|
|
@ -1,83 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
"""Main dialog. """
|
||||
from botbuilder.dialogs import (
|
||||
ComponentDialog,
|
||||
WaterfallDialog,
|
||||
WaterfallStepContext,
|
||||
DialogTurnResult,
|
||||
)
|
||||
from botbuilder.dialogs.prompts import TextPrompt, PromptOptions
|
||||
from botbuilder.core import MessageFactory
|
||||
from booking_details import BookingDetails
|
||||
from helpers.luis_helper import LuisHelper
|
||||
from .booking_dialog import BookingDialog
|
||||
|
||||
|
||||
class MainDialog(ComponentDialog):
|
||||
"""Main dialog. """
|
||||
|
||||
def __init__(self, configuration: dict, dialog_id: str = None):
|
||||
super(MainDialog, self).__init__(dialog_id or MainDialog.__name__)
|
||||
|
||||
self._configuration = configuration
|
||||
|
||||
self.add_dialog(TextPrompt(TextPrompt.__name__))
|
||||
self.add_dialog(BookingDialog())
|
||||
self.add_dialog(
|
||||
WaterfallDialog(
|
||||
"WFDialog", [self.intro_step, self.act_step, self.final_step]
|
||||
)
|
||||
)
|
||||
|
||||
self.initial_dialog_id = "WFDialog"
|
||||
|
||||
async def intro_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
|
||||
"""Initial prompt."""
|
||||
return await step_context.prompt(
|
||||
TextPrompt.__name__,
|
||||
PromptOptions(
|
||||
prompt=MessageFactory.text("What can I help you with today?")
|
||||
),
|
||||
)
|
||||
|
||||
async def act_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
|
||||
"""Use language understanding to gather details about booking."""
|
||||
|
||||
# In this sample we only have a single Intent we are concerned with.
|
||||
# However, typically a scenario will have multiple different Intents
|
||||
# each corresponding to starting a different child Dialog.
|
||||
booking_details = (
|
||||
await LuisHelper.execute_luis_query(
|
||||
self._configuration, step_context.context
|
||||
)
|
||||
if step_context.result is not None
|
||||
else BookingDetails()
|
||||
)
|
||||
|
||||
# Run the BookingDialog giving it whatever details we have from the
|
||||
# model. The dialog will prompt to find out the remaining details.
|
||||
return await step_context.begin_dialog(BookingDialog.__name__, booking_details)
|
||||
|
||||
async def final_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
|
||||
"""Complete dialog.
|
||||
At this step, with details from the user, display the completed
|
||||
flight booking to the user.
|
||||
"""
|
||||
# If the child dialog ("BookingDialog") was cancelled or the user failed
|
||||
# to confirm, the Result here will be null.
|
||||
if step_context.result is not None:
|
||||
result = step_context.result
|
||||
|
||||
# Now we have all the booking details call the booking service.
|
||||
# If the call to the booking service was successful tell the user.
|
||||
# time_property = Timex(result.travel_date)
|
||||
# travel_date_msg = time_property.to_natural_language(datetime.now())
|
||||
msg = (
|
||||
f"I have you booked to {result.destination} from"
|
||||
f" {result.origin} on {result.travel_date}."
|
||||
)
|
||||
await step_context.context.send_activity(MessageFactory.text(msg))
|
||||
else:
|
||||
await step_context.context.send_activity(MessageFactory.text("Thank you."))
|
||||
return await step_context.end_dialog()
|
|
@ -1,7 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
"""Helpers module."""
|
||||
from . import activity_helper, luis_helper, dialog_helper
|
||||
|
||||
__all__ = ["activity_helper", "dialog_helper", "luis_helper"]
|
|
@ -1,38 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
"""Helper to create reply object."""
|
||||
|
||||
from datetime import datetime
|
||||
from botbuilder.schema import (
|
||||
Activity,
|
||||
ActivityTypes,
|
||||
ChannelAccount,
|
||||
ConversationAccount,
|
||||
)
|
||||
|
||||
|
||||
def create_activity_reply(activity: Activity, text: str = None, locale: str = None):
|
||||
"""Helper to create reply object."""
|
||||
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=[],
|
||||
)
|
|
@ -1,22 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
"""Utility to run dialogs."""
|
||||
from botbuilder.core import StatePropertyAccessor, TurnContext
|
||||
from botbuilder.dialogs import Dialog, DialogSet, DialogTurnStatus
|
||||
|
||||
|
||||
class DialogHelper:
|
||||
"""Dialog Helper implementation."""
|
||||
|
||||
@staticmethod
|
||||
async def run_dialog(
|
||||
dialog: Dialog, turn_context: TurnContext, accessor: StatePropertyAccessor
|
||||
): # pylint: disable=line-too-long
|
||||
"""Run dialog."""
|
||||
dialog_set = DialogSet(accessor)
|
||||
dialog_set.add(dialog)
|
||||
|
||||
dialog_context = await dialog_set.create_context(turn_context)
|
||||
results = await dialog_context.continue_dialog()
|
||||
if results.status == DialogTurnStatus.Empty:
|
||||
await dialog_context.begin_dialog(dialog.id)
|
|
@ -1,63 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
"""Helper to call LUIS service."""
|
||||
from botbuilder.ai.luis import LuisRecognizer, LuisApplication
|
||||
from botbuilder.core import TurnContext
|
||||
|
||||
from booking_details import BookingDetails
|
||||
|
||||
# pylint: disable=line-too-long
|
||||
class LuisHelper:
|
||||
"""LUIS helper implementation."""
|
||||
|
||||
@staticmethod
|
||||
async def execute_luis_query(
|
||||
configuration, turn_context: TurnContext
|
||||
) -> BookingDetails:
|
||||
"""Invoke LUIS service to perform prediction/evaluation of utterance."""
|
||||
booking_details = BookingDetails()
|
||||
|
||||
# pylint:disable=broad-except
|
||||
try:
|
||||
luis_application = LuisApplication(
|
||||
configuration.LUIS_APP_ID,
|
||||
configuration.LUIS_API_KEY,
|
||||
configuration.LUIS_API_HOST_NAME,
|
||||
)
|
||||
|
||||
recognizer = LuisRecognizer(luis_application)
|
||||
recognizer_result = await recognizer.recognize(turn_context)
|
||||
|
||||
if recognizer_result.intents:
|
||||
intent = sorted(
|
||||
recognizer_result.intents,
|
||||
key=recognizer_result.intents.get,
|
||||
reverse=True,
|
||||
)[:1][0]
|
||||
if intent == "Book_flight":
|
||||
# We need to get the result from the LUIS JSON which at every level returns an array.
|
||||
to_entities = recognizer_result.entities.get("$instance", {}).get(
|
||||
"To", []
|
||||
)
|
||||
if to_entities:
|
||||
booking_details.destination = to_entities[0]["text"]
|
||||
from_entities = recognizer_result.entities.get("$instance", {}).get(
|
||||
"From", []
|
||||
)
|
||||
if from_entities:
|
||||
booking_details.origin = from_entities[0]["text"]
|
||||
|
||||
# This value will be a TIMEX. And we are only interested in a Date so grab the first result and drop the Time part.
|
||||
# TIMEX is a format that represents DateTime expressions that include some ambiguity. e.g. missing a Year.
|
||||
date_entities = recognizer_result.entities.get("$instance", {}).get(
|
||||
"datetime", []
|
||||
)
|
||||
if date_entities:
|
||||
booking_details.travel_date = (
|
||||
None
|
||||
) # Set when we get a timex format
|
||||
except Exception as exception:
|
||||
print(exception)
|
||||
|
||||
return booking_details
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
import os
|
||||
import sys
|
||||
from django.core.management.commands.runserver import Command as runserver
|
||||
import config
|
||||
|
||||
|
||||
def main():
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
runserver.default_port = config.DefaultConfig.PORT
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bots.settings")
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
) from exc
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,10 +0,0 @@
|
|||
# Django 2.2 before 2.2.10 has a security vulnerability.
|
||||
Django~=2.2.24
|
||||
requests==2.23.0
|
||||
botframework-connector>=4.4.0.b1
|
||||
botbuilder-schema>=4.4.0.b1
|
||||
botbuilder-core>=4.4.0.b1
|
||||
botbuilder-dialogs>=4.4.0.b1
|
||||
botbuilder-ai>=4.4.0.b1
|
||||
datatypes-date-time>=1.0.0.a1
|
||||
azure-cognitiveservices-language-luis>=0.2.0
|
|
@ -1,30 +0,0 @@
|
|||
# EchoBot
|
||||
|
||||
Bot Framework v4 echo bot sample.
|
||||
|
||||
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.
|
||||
|
||||
## Running the sample
|
||||
- Clone the repository
|
||||
```bash
|
||||
git clone https://github.com/Microsoft/botbuilder-python.git
|
||||
```
|
||||
- Activate your desired virtual environment
|
||||
- Bring up a terminal, navigate to `botbuilder-python\samples\02.echo-bot` 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
|
||||
- Paste this URL in the emulator window - http://localhost:3978/api/messages
|
||||
|
||||
## Further reading
|
||||
|
||||
- [Bot Framework Documentation](https://docs.botframework.com)
|
||||
- [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0)
|
||||
- [Activity processing](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0)
|
|
@ -1,77 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
from quart import Quart, request, Response
|
||||
from botbuilder.core import BotFrameworkAdapterSettings, TurnContext, BotFrameworkAdapter
|
||||
from botbuilder.schema import Activity, ActivityTypes
|
||||
|
||||
from bots import EchoBot
|
||||
|
||||
# Create the loop and Quart APP
|
||||
APP = Quart(__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)
|
||||
|
||||
ADAPTER.on_turn_error = on_error
|
||||
|
||||
# Create the Bot
|
||||
BOT = EchoBot()
|
||||
|
||||
# Listen for incoming requests on /api/messages
|
||||
@APP.route("/api/messages", methods=["POST"])
|
||||
async def messages():
|
||||
# Main bot message handler.
|
||||
if "application/json" in request.headers["Content-Type"]:
|
||||
body = await request.json
|
||||
else:
|
||||
return Response("", status=415)
|
||||
|
||||
activity = Activity().deserialize(body)
|
||||
auth_header = (
|
||||
request.headers["Authorization"] if "Authorization" in request.headers else ""
|
||||
)
|
||||
|
||||
try:
|
||||
await ADAPTER.process_activity(activity, auth_header, BOT.on_turn)
|
||||
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
|
|
@ -1,6 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from .echo_bot import EchoBot
|
||||
|
||||
__all__ = ["EchoBot"]
|
|
@ -1,17 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from botbuilder.core import ActivityHandler, MessageFactory, TurnContext
|
||||
from botbuilder.schema import ChannelAccount
|
||||
|
||||
|
||||
class EchoBot(ActivityHandler):
|
||||
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):
|
||||
return await turn_context.send_activity(MessageFactory.text(f"Echo: {turn_context.activity.text}"))
|
|
@ -1,15 +0,0 @@
|
|||
#!/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", "")
|
|
@ -1,265 +0,0 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"groupLocation": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Specifies the location of the Resource Group."
|
||||
}
|
||||
},
|
||||
"groupName": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Specifies the name of the Resource Group."
|
||||
}
|
||||
},
|
||||
"appId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"appSecret": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"botId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable."
|
||||
}
|
||||
},
|
||||
"botSku": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanName": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The name of the App Service Plan."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanSku": {
|
||||
"type": "object",
|
||||
"defaultValue": {
|
||||
"name": "S1",
|
||||
"tier": "Standard",
|
||||
"size": "S1",
|
||||
"family": "S",
|
||||
"capacity": 1
|
||||
},
|
||||
"metadata": {
|
||||
"description": "The SKU of the App Service Plan. Defaults to Standard values."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanLocation": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The location of the App Service Plan. Defaults to \"westus\"."
|
||||
}
|
||||
},
|
||||
"newWebAppName": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"."
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"appServicePlanName": "[parameters('newAppServicePlanName')]",
|
||||
"resourcesLocation": "[parameters('newAppServicePlanLocation')]",
|
||||
"webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]",
|
||||
"siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]",
|
||||
"botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]",
|
||||
"publishingUsername": "[concat('$', parameters('newWebAppName'))]",
|
||||
"resourceGroupId": "[concat(subscription().id, '/resourceGroups/', parameters('groupName'))]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"name": "[parameters('groupName')]",
|
||||
"type": "Microsoft.Resources/resourceGroups",
|
||||
"apiVersion": "2018-05-01",
|
||||
"location": "[parameters('groupLocation')]",
|
||||
"properties": {}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Resources/deployments",
|
||||
"apiVersion": "2018-05-01",
|
||||
"name": "storageDeployment",
|
||||
"resourceGroup": "[parameters('groupName')]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Resources/resourceGroups/', parameters('groupName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"mode": "Incremental",
|
||||
"template": {
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {},
|
||||
"variables": {},
|
||||
"resources": [
|
||||
{
|
||||
"comments": "Create a new Linux App Service Plan if no existing App Service Plan name was passed in.",
|
||||
"type": "Microsoft.Web/serverfarms",
|
||||
"name": "[variables('appServicePlanName')]",
|
||||
"apiVersion": "2018-02-01",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"sku": "[parameters('newAppServicePlanSku')]",
|
||||
"kind": "linux",
|
||||
"properties": {
|
||||
"name": "[variables('appServicePlanName')]",
|
||||
"perSiteScaling": false,
|
||||
"reserved": true,
|
||||
"targetWorkerCount": 0,
|
||||
"targetWorkerSizeId": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"comments": "Create a Web App using a Linux App Service Plan",
|
||||
"type": "Microsoft.Web/sites",
|
||||
"apiVersion": "2015-08-01",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"kind": "app,linux",
|
||||
"dependsOn": [
|
||||
"[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/serverfarms/', variables('appServicePlanName'))]"
|
||||
],
|
||||
"name": "[variables('webAppName')]",
|
||||
"properties": {
|
||||
"name": "[variables('webAppName')]",
|
||||
"hostNameSslStates": [
|
||||
{
|
||||
"name": "[concat(parameters('newWebAppName'), '.azurewebsites.net')]",
|
||||
"sslState": "Disabled",
|
||||
"hostType": "Standard"
|
||||
},
|
||||
{
|
||||
"name": "[concat(parameters('newWebAppName'), '.scm.azurewebsites.net')]",
|
||||
"sslState": "Disabled",
|
||||
"hostType": "Repository"
|
||||
}
|
||||
],
|
||||
"serverFarmId": "[variables('appServicePlanName')]",
|
||||
"siteConfig": {
|
||||
"appSettings": [
|
||||
{
|
||||
"name": "SCM_DO_BUILD_DURING_DEPLOYMENT",
|
||||
"value": "true"
|
||||
},
|
||||
{
|
||||
"name": "MicrosoftAppId",
|
||||
"value": "[parameters('appId')]"
|
||||
},
|
||||
{
|
||||
"name": "MicrosoftAppPassword",
|
||||
"value": "[parameters('appSecret')]"
|
||||
}
|
||||
],
|
||||
"cors": {
|
||||
"allowedOrigins": [
|
||||
"https://botservice.hosting.portal.azure.net",
|
||||
"https://hosting.onecloud.azure-test.net/"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Web/sites/config",
|
||||
"apiVersion": "2016-08-01",
|
||||
"name": "[concat(variables('webAppName'), '/web')]",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"dependsOn": [
|
||||
"[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/sites/', variables('webAppName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"numberOfWorkers": 1,
|
||||
"defaultDocuments": [
|
||||
"Default.htm",
|
||||
"Default.html",
|
||||
"Default.asp",
|
||||
"index.htm",
|
||||
"index.html",
|
||||
"iisstart.htm",
|
||||
"default.aspx",
|
||||
"index.php",
|
||||
"hostingstart.html"
|
||||
],
|
||||
"netFrameworkVersion": "v4.0",
|
||||
"phpVersion": "",
|
||||
"pythonVersion": "",
|
||||
"nodeVersion": "",
|
||||
"linuxFxVersion": "PYTHON|3.7",
|
||||
"requestTracingEnabled": false,
|
||||
"remoteDebuggingEnabled": false,
|
||||
"remoteDebuggingVersion": "VS2017",
|
||||
"httpLoggingEnabled": true,
|
||||
"logsDirectorySizeLimit": 35,
|
||||
"detailedErrorLoggingEnabled": false,
|
||||
"publishingUsername": "[variables('publishingUsername')]",
|
||||
"scmType": "None",
|
||||
"use32BitWorkerProcess": true,
|
||||
"webSocketsEnabled": false,
|
||||
"alwaysOn": false,
|
||||
"appCommandLine": "gunicorn --bind 0.0.0.0 --worker-class aiohttp.worker.GunicornWebWorker --timeout 600 app:APP",
|
||||
"managedPipelineMode": "Integrated",
|
||||
"virtualApplications": [
|
||||
{
|
||||
"virtualPath": "/",
|
||||
"physicalPath": "site\\wwwroot",
|
||||
"preloadEnabled": false,
|
||||
"virtualDirectories": null
|
||||
}
|
||||
],
|
||||
"winAuthAdminState": 0,
|
||||
"winAuthTenantState": 0,
|
||||
"customAppPoolIdentityAdminState": false,
|
||||
"customAppPoolIdentityTenantState": false,
|
||||
"loadBalancing": "LeastRequests",
|
||||
"routingRules": [],
|
||||
"experiments": {
|
||||
"rampUpRules": []
|
||||
},
|
||||
"autoHealEnabled": false,
|
||||
"vnetName": "",
|
||||
"minTlsVersion": "1.2",
|
||||
"ftpsState": "AllAllowed",
|
||||
"reservedInstanceCount": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2021-03-01",
|
||||
"type": "Microsoft.BotService/botServices",
|
||||
"name": "[parameters('botId')]",
|
||||
"location": "global",
|
||||
"kind": "azurebot",
|
||||
"sku": {
|
||||
"name": "[parameters('botSku')]"
|
||||
},
|
||||
"properties": {
|
||||
"name": "[parameters('botId')]",
|
||||
"displayName": "[parameters('botId')]",
|
||||
"iconUrl": "https://docs.botframework.com/static/devportal/client/images/bot-framework-default.png",
|
||||
"endpoint": "[variables('botEndpoint')]",
|
||||
"msaAppId": "[parameters('appId')]",
|
||||
"luisAppIds": [],
|
||||
"schemaTransformationVersion": "1.3",
|
||||
"isCmekEnabled": false,
|
||||
"isIsolated": false
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/sites/', variables('webAppName'))]"
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputs": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,243 +0,0 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"appId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"appSecret": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"botId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable."
|
||||
}
|
||||
},
|
||||
"botSku": {
|
||||
"defaultValue": "F0",
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanName": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "The name of the new App Service Plan."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanSku": {
|
||||
"type": "object",
|
||||
"defaultValue": {
|
||||
"name": "S1",
|
||||
"tier": "Standard",
|
||||
"size": "S1",
|
||||
"family": "S",
|
||||
"capacity": 1
|
||||
},
|
||||
"metadata": {
|
||||
"description": "The SKU of the App Service Plan. Defaults to Standard values."
|
||||
}
|
||||
},
|
||||
"appServicePlanLocation": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The location of the App Service Plan."
|
||||
}
|
||||
},
|
||||
"existingAppServicePlan": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "Name of the existing App Service Plan used to create the Web App for the bot."
|
||||
}
|
||||
},
|
||||
"newWebAppName": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"."
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"defaultAppServicePlanName": "[if(empty(parameters('existingAppServicePlan')), 'createNewAppServicePlan', parameters('existingAppServicePlan'))]",
|
||||
"useExistingAppServicePlan": "[not(equals(variables('defaultAppServicePlanName'), 'createNewAppServicePlan'))]",
|
||||
"servicePlanName": "[if(variables('useExistingAppServicePlan'), parameters('existingAppServicePlan'), parameters('newAppServicePlanName'))]",
|
||||
"publishingUsername": "[concat('$', parameters('newWebAppName'))]",
|
||||
"resourcesLocation": "[parameters('appServicePlanLocation')]",
|
||||
"webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]",
|
||||
"siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]",
|
||||
"botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"comments": "Create a new Linux App Service Plan if no existing App Service Plan name was passed in.",
|
||||
"type": "Microsoft.Web/serverfarms",
|
||||
"apiVersion": "2016-09-01",
|
||||
"name": "[variables('servicePlanName')]",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"sku": "[parameters('newAppServicePlanSku')]",
|
||||
"kind": "linux",
|
||||
"properties": {
|
||||
"name": "[variables('servicePlanName')]",
|
||||
"perSiteScaling": false,
|
||||
"reserved": true,
|
||||
"targetWorkerCount": 0,
|
||||
"targetWorkerSizeId": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"comments": "Create a Web App using a Linux App Service Plan",
|
||||
"type": "Microsoft.Web/sites",
|
||||
"apiVersion": "2016-08-01",
|
||||
"name": "[variables('webAppName')]",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]"
|
||||
],
|
||||
"kind": "app,linux",
|
||||
"properties": {
|
||||
"enabled": true,
|
||||
"hostNameSslStates": [
|
||||
{
|
||||
"name": "[concat(parameters('newWebAppName'), '.azurewebsites.net')]",
|
||||
"sslState": "Disabled",
|
||||
"hostType": "Standard"
|
||||
},
|
||||
{
|
||||
"name": "[concat(parameters('newWebAppName'), '.scm.azurewebsites.net')]",
|
||||
"sslState": "Disabled",
|
||||
"hostType": "Repository"
|
||||
}
|
||||
],
|
||||
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]",
|
||||
"reserved": true,
|
||||
"scmSiteAlsoStopped": false,
|
||||
"clientAffinityEnabled": false,
|
||||
"clientCertEnabled": false,
|
||||
"hostNamesDisabled": false,
|
||||
"containerSize": 0,
|
||||
"dailyMemoryTimeQuota": 0,
|
||||
"httpsOnly": false,
|
||||
"siteConfig": {
|
||||
"appSettings": [
|
||||
{
|
||||
"name": "MicrosoftAppId",
|
||||
"value": "[parameters('appId')]"
|
||||
},
|
||||
{
|
||||
"name": "MicrosoftAppPassword",
|
||||
"value": "[parameters('appSecret')]"
|
||||
},
|
||||
{
|
||||
"name": "SCM_DO_BUILD_DURING_DEPLOYMENT",
|
||||
"value": "true"
|
||||
}
|
||||
],
|
||||
"cors": {
|
||||
"allowedOrigins": [
|
||||
"https://botservice.hosting.portal.azure.net",
|
||||
"https://hosting.onecloud.azure-test.net/"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Web/sites/config",
|
||||
"apiVersion": "2016-08-01",
|
||||
"name": "[concat(variables('webAppName'), '/web')]",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Web/sites', variables('webAppName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"numberOfWorkers": 1,
|
||||
"defaultDocuments": [
|
||||
"Default.htm",
|
||||
"Default.html",
|
||||
"Default.asp",
|
||||
"index.htm",
|
||||
"index.html",
|
||||
"iisstart.htm",
|
||||
"default.aspx",
|
||||
"index.php",
|
||||
"hostingstart.html"
|
||||
],
|
||||
"netFrameworkVersion": "v4.0",
|
||||
"phpVersion": "",
|
||||
"pythonVersion": "",
|
||||
"nodeVersion": "",
|
||||
"linuxFxVersion": "PYTHON|3.7",
|
||||
"requestTracingEnabled": false,
|
||||
"remoteDebuggingEnabled": false,
|
||||
"remoteDebuggingVersion": "VS2017",
|
||||
"httpLoggingEnabled": true,
|
||||
"logsDirectorySizeLimit": 35,
|
||||
"detailedErrorLoggingEnabled": false,
|
||||
"publishingUsername": "[variables('publishingUsername')]",
|
||||
"scmType": "None",
|
||||
"use32BitWorkerProcess": true,
|
||||
"webSocketsEnabled": false,
|
||||
"alwaysOn": false,
|
||||
"appCommandLine": "gunicorn --bind 0.0.0.0 --worker-class aiohttp.worker.GunicornWebWorker --timeout 600 app:APP",
|
||||
"managedPipelineMode": "Integrated",
|
||||
"virtualApplications": [
|
||||
{
|
||||
"virtualPath": "/",
|
||||
"physicalPath": "site\\wwwroot",
|
||||
"preloadEnabled": false,
|
||||
"virtualDirectories": null
|
||||
}
|
||||
],
|
||||
"winAuthAdminState": 0,
|
||||
"winAuthTenantState": 0,
|
||||
"customAppPoolIdentityAdminState": false,
|
||||
"customAppPoolIdentityTenantState": false,
|
||||
"loadBalancing": "LeastRequests",
|
||||
"routingRules": [],
|
||||
"experiments": {
|
||||
"rampUpRules": []
|
||||
},
|
||||
"autoHealEnabled": false,
|
||||
"vnetName": "",
|
||||
"minTlsVersion": "1.2",
|
||||
"ftpsState": "AllAllowed",
|
||||
"reservedInstanceCount": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2021-03-01",
|
||||
"type": "Microsoft.BotService/botServices",
|
||||
"name": "[parameters('botId')]",
|
||||
"location": "global",
|
||||
"kind": "azurebot",
|
||||
"sku": {
|
||||
"name": "[parameters('botSku')]"
|
||||
},
|
||||
"properties": {
|
||||
"name": "[parameters('botId')]",
|
||||
"displayName": "[parameters('botId')]",
|
||||
"iconUrl": "https://docs.botframework.com/static/devportal/client/images/bot-framework-default.png",
|
||||
"endpoint": "[variables('botEndpoint')]",
|
||||
"msaAppId": "[parameters('appId')]",
|
||||
"luisAppIds": [],
|
||||
"schemaTransformationVersion": "1.3",
|
||||
"isCmekEnabled": false,
|
||||
"isIsolated": false
|
||||
},
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Web/sites/', variables('webAppName'))]"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
botbuilder-core>=4.4.0b1
|
||||
quart>=0.10.0
|
|
@ -1,216 +0,0 @@
|
|||
# Setting up LUIS via CLI:
|
||||
|
||||
This README contains information on how to create and deploy a LUIS application. When the bot is ready to be deployed to production, we recommend creating a LUIS Endpoint Resource for usage with your LUIS App.
|
||||
|
||||
> _For instructions on how to create a LUIS Application via the LUIS portal, see these Quickstart steps:_
|
||||
> 1. _[Quickstart: Create a new app in the LUIS portal][Quickstart-create]_
|
||||
> 2. _[Quickstart: Deploy an app in the LUIS portal][Quickstart-deploy]_
|
||||
|
||||
[Quickstart-create]: https://docs.microsoft.com/azure/cognitive-services/luis/get-started-portal-build-app
|
||||
[Quickstart-deploy]:https://docs.microsoft.com/azure/cognitive-services/luis/get-started-portal-deploy-app
|
||||
|
||||
## Table of Contents:
|
||||
|
||||
- [Prerequisites](#Prerequisites)
|
||||
- [Import a new LUIS Application using a local LUIS application](#Import-a-new-LUIS-Application-using-a-local-LUIS-application)
|
||||
- [How to create a LUIS Endpoint resource in Azure and pair it with a LUIS Application](#How-to-create-a-LUIS-Endpoint-resource-in-Azure-and-pair-it-with-a-LUIS-Application)
|
||||
|
||||
___
|
||||
|
||||
## [Prerequisites](#Table-of-Contents):
|
||||
|
||||
#### Install Azure CLI >=2.0.61:
|
||||
|
||||
Visit the following page to find the correct installer for your OS:
|
||||
- https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest
|
||||
|
||||
#### Install LUIS CLI >=2.4.0:
|
||||
|
||||
Open a CLI of your choice and type the following:
|
||||
|
||||
```bash
|
||||
npm i -g luis-apis@^2.4.0
|
||||
```
|
||||
|
||||
#### LUIS portal account:
|
||||
|
||||
You should already have a LUIS account with either https://luis.ai, https://eu.luis.ai, or https://au.luis.ai. To determine where to create a LUIS account, consider where you will deploy your LUIS applications, and then place them in [the corresponding region][LUIS-Authoring-Regions].
|
||||
|
||||
After you've created your account, you need your [Authoring Key][LUIS-AKey] and a LUIS application ID.
|
||||
|
||||
[LUIS-Authoring-Regions]: https://docs.microsoft.com/azure/cognitive-services/luis/luis-reference-regions#luis-authoring-regions]
|
||||
[LUIS-AKey]: https://docs.microsoft.com/azure/cognitive-services/luis/luis-concept-keys#authoring-key
|
||||
|
||||
___
|
||||
|
||||
## [Import a new LUIS Application using a local LUIS application](#Table-of-Contents)
|
||||
|
||||
### 1. Import the local LUIS application to luis.ai
|
||||
|
||||
```bash
|
||||
luis import application --region "LuisAppAuthoringRegion" --authoringKey "LuisAuthoringKey" --appName "FlightBooking" --in "./cognitiveModels/FlightBooking.json"
|
||||
```
|
||||
|
||||
Outputs the following JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "########-####-####-####-############",
|
||||
"name": "FlightBooking",
|
||||
"description": "A LUIS model that uses intent and entities.",
|
||||
"culture": "en-us",
|
||||
"usageScenario": "",
|
||||
"domain": "",
|
||||
"versionsCount": 1,
|
||||
"createdDateTime": "2019-03-29T18:32:02Z",
|
||||
"endpoints": {},
|
||||
"endpointHitsCount": 0,
|
||||
"activeVersion": "0.1",
|
||||
"ownerEmail": "bot@contoso.com",
|
||||
"tokenizerVersion": "1.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
For the next step, you'll need the `"id"` value for `--appId` and the `"activeVersion"` value for `--versionId`.
|
||||
|
||||
### 2. Train the LUIS Application
|
||||
|
||||
```bash
|
||||
luis train version --region "LuisAppAuthoringRegion" --authoringKey "LuisAuthoringKey" --appId "LuisAppId" --versionId "LuisAppversion" --wait
|
||||
```
|
||||
|
||||
### 3. Publish the LUIS Application
|
||||
|
||||
```bash
|
||||
luis publish version --region "LuisAppAuthoringRegion" --authoringKey "LuisAuthoringKey" --appId "LuisAppId" --versionId "LuisAppversion" --publishRegion "LuisAppPublishRegion"
|
||||
```
|
||||
|
||||
> `--region` corresponds to the region you _author_ your application in. The regions available for this are "westus", "westeurope" and "australiaeast". <br/>
|
||||
> These regions correspond to the three available portals, https://luis.ai, https://eu.luis.ai, or https://au.luis.ai. <br/>
|
||||
> `--publishRegion` corresponds to the region of the endpoint you're publishing to, (e.g. "westus", "southeastasia", "westeurope", "brazilsouth"). <br/>
|
||||
> See the [reference docs][Endpoint-API] for a list of available publish/endpoint regions.
|
||||
|
||||
[Endpoint-API]: https://westus.dev.cognitive.microsoft.com/docs/services/5819c76f40a6350ce09de1ac/operations/5819c77140a63516d81aee78
|
||||
|
||||
Outputs the following:
|
||||
|
||||
```json
|
||||
{
|
||||
"versionId": "0.1",
|
||||
"isStaging": false,
|
||||
"endpointUrl": "https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/########-####-####-####-############",
|
||||
"region": "westus",
|
||||
"assignedEndpointKey": null,
|
||||
"endpointRegion": "westus",
|
||||
"failedRegions": "",
|
||||
"publishedDateTime": "2019-03-29T18:40:32Z",
|
||||
"directVersionPublish": false
|
||||
}
|
||||
```
|
||||
|
||||
To see how to create an LUIS Cognitive Service Resource in Azure, please see [the next README][README-LUIS]. This Resource should be used when you want to move your bot to production. The instructions will show you how to create and pair the resource with a LUIS Application.
|
||||
|
||||
[README-LUIS]: ./README-LUIS.md
|
||||
|
||||
___
|
||||
|
||||
## [How to create a LUIS Endpoint resource in Azure and pair it with a LUIS Application](#Table-of-Contents)
|
||||
|
||||
### 1. Create a new LUIS Cognitive Services resource on Azure via Azure CLI
|
||||
|
||||
> _Note:_ <br/>
|
||||
> _If you don't have a Resource Group in your Azure subscription, you can create one through the Azure portal or through using:_
|
||||
> ```bash
|
||||
> az group create --subscription "AzureSubscriptionGuid" --location "westus" --name "ResourceGroupName"
|
||||
> ```
|
||||
> _To see a list of valid locations, use `az account list-locations`_
|
||||
|
||||
|
||||
```bash
|
||||
# Use Azure CLI to create the LUIS Key resource on Azure
|
||||
az cognitiveservices account create --kind "luis" --name "NewLuisResourceName" --sku "S0" --location "westus" --subscription "AzureSubscriptionGuid" -g "ResourceGroupName"
|
||||
```
|
||||
|
||||
The command will output a response similar to the JSON below:
|
||||
|
||||
```json
|
||||
{
|
||||
"endpoint": "https://westus.api.cognitive.microsoft.com/luis/v2.0",
|
||||
"etag": "\"########-####-####-####-############\"",
|
||||
"id": "/subscriptions/########-####-####-####-############/resourceGroups/ResourceGroupName/providers/Microsoft.CognitiveServices/accounts/NewLuisResourceName",
|
||||
"internalId": "################################",
|
||||
"kind": "luis",
|
||||
"location": "westus",
|
||||
"name": "NewLuisResourceName",
|
||||
"provisioningState": "Succeeded",
|
||||
"resourceGroup": "ResourceGroupName",
|
||||
"sku": {
|
||||
"name": "S0",
|
||||
"tier": null
|
||||
},
|
||||
"tags": null,
|
||||
"type": "Microsoft.CognitiveServices/accounts"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
Take the output from the previous command and create a JSON file in the following format:
|
||||
|
||||
```json
|
||||
{
|
||||
"azureSubscriptionId": "00000000-0000-0000-0000-000000000000",
|
||||
"resourceGroup": "ResourceGroupName",
|
||||
"accountName": "NewLuisResourceName"
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Retrieve ARM access token via Azure CLI
|
||||
|
||||
```bash
|
||||
az account get-access-token --subscription "AzureSubscriptionGuid"
|
||||
```
|
||||
|
||||
This will return an object that looks like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"accessToken": "eyJ0eXAiOiJKVtokentokentokentokentokeng1dCI6Ik4tbEMwbi05REFMcXdodUhZbkhRNjNHZUNYYyIsItokenI6Ik4tbEMwbi05REFMcXdodUhZbkhRNjNHZUNYYyJ9.eyJhdWQiOiJodHRwczovL21hbmFnZW1lbnQuY29yZS53aW5kb3dzLm5ldC8iLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC83MmY5ODhiZi04NmYxLTQxYWYtOTFhYi0yZDdjZDAxMWRiNDcvIiwiaWF0IjoxNTUzODc3MTUwLCJuYmYiOjE1NTM4NzcxNTAsImV4cCI6MTU1Mzg4MTA1MCwiX2NsYWltX25hbWVzIjp7Imdyb3VwcyI6InNyYzEifSwiX2NsYWltX3NvdXJjZXMiOnsic3JjMSI6eyJlbmRwb2ludCI6Imh0dHBzOi8vZ3JhcGgud2luZG93cy5uZXQvNzJmOTg4YmYtODZmMS00MWFmLTkxYWItMmQ3Y2QwMTFkYjQ3L3VzZXJzL2ZmZTQyM2RkLWJhM2YtNDg0Ny04NjgyLWExNTI5MDA4MjM4Ny9nZXRNZW1iZXJPYmplY3RzIn19LCJhY3IiOiIxIiwiYWlvIjoiQVZRQXEvOEtBQUFBeGVUc201NDlhVHg4RE1mMFlRVnhGZmxxOE9RSC9PODR3QktuSmRqV1FqTkkwbmxLYzB0bHJEZzMyMFZ5bWZGaVVBSFBvNUFFUTNHL0FZNDRjdk01T3M0SEt0OVJkcE5JZW9WU0dzd0kvSkk9IiwiYW1yIjpbIndpYSIsIm1mYSJdLCJhcHBpZCI6IjA0YjA3Nzk1LThkZGItNDYxYS1iYmVlLTAyZjllMWJmN2I0NiIsImFwcGlkYWNyIjoiMCIsImRldmljZWlkIjoiNDhmNDVjNjEtMTg3Zi00MjUxLTlmZWItMTllZGFkZmMwMmE3IiwiZmFtaWx5X25hbWUiOiJHdW0iLCJnaXZlbl9uYW1lIjoiU3RldmVuIiwiaXBhZGRyIjoiMTY3LjIyMC4yLjU1IiwibmFtZSI6IlN0ZXZlbiBHdW0iLCJvaWQiOiJmZmU0MjNkZC1iYTNmLTQ4NDctODY4Mi1hMTUyOTAwODIzODciLCJvbnByZW1fc2lkIjoiUy0xLTUtMjEtMjEyNzUyMTE4NC0xNjA0MDEyOTIwLTE4ODc5Mjc1MjctMjYwOTgyODUiLCJwdWlkIjoiMTAwMzdGRkVBMDQ4NjlBNyIsInJoIjoiSSIsInNjcCI6InVzZXJfaW1wZXJzb25hdGlvbiIsInN1YiI6Ik1rMGRNMWszN0U5ckJyMjhieUhZYjZLSU85LXVFQVVkZFVhNWpkSUd1Nk0iLCJ0aWQiOiI3MmY5ODhiZi04NmYxLTQxYWYtOTFhYi0yZDdjZDAxMWRiNDciLCJ1bmlxdWVfbmFtZSI6InN0Z3VtQG1pY3Jvc29mdC5jb20iLCJ1cG4iOiJzdGd1bUBtaWNyb3NvZnQuY29tIiwidXRpIjoiT2w2NGN0TXY4RVNEQzZZQWRqRUFtokenInZlciI6IjEuMCJ9.kFAsEilE0mlS1pcpqxf4rEnRKeYsehyk-gz-zJHUrE__oad3QjgDSBDPrR_ikLdweynxbj86pgG4QFaHURNCeE6SzrbaIrNKw-n9jrEtokenlosOxg_0l2g1LeEUOi5Q4gQREAU_zvSbl-RY6sAadpOgNHtGvz3Rc6FZRITfkckSLmsKAOFoh-aWC6tFKG8P52rtB0qVVRz9tovBeNqkMYL49s9ypduygbXNVwSQhm5JszeWDgrFuVFHBUP_iENCQYGQpEZf_KvjmX1Ur1F9Eh9nb4yI2gFlKncKNsQl-tokenK7-tokentokentokentokentokentokenatoken",
|
||||
"expiresOn": "2200-12-31 23:59:59.999999",
|
||||
"subscription": "AzureSubscriptionGuid",
|
||||
"tenant": "tenant-guid",
|
||||
"tokenType": "Bearer"
|
||||
}
|
||||
```
|
||||
|
||||
The value needed for the next step is the `"accessToken"`.
|
||||
|
||||
### 3. Use `luis add appazureaccount` to pair your LUIS resource with a LUIS Application
|
||||
|
||||
```bash
|
||||
luis add appazureaccount --in "path/to/created/requestBody.json" --appId "LuisAppId" --authoringKey "LuisAuthoringKey" --armToken "accessToken"
|
||||
```
|
||||
|
||||
If successful, it should yield a response like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "Success",
|
||||
"message": "Operation Successful"
|
||||
}
|
||||
```
|
||||
|
||||
### 4. See the LUIS Cognitive Services' keys
|
||||
|
||||
```bash
|
||||
az cognitiveservices account keys list --name "NewLuisResourceName" --subscription "AzureSubscriptionGuid" -g "ResourceGroupName"
|
||||
```
|
||||
|
||||
This will return an object that looks like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"key1": "9a69####dc8f####8eb4####399f####",
|
||||
"key2": "####f99e####4b1a####fb3b####6b9f"
|
||||
}
|
||||
```
|
|
@ -1,64 +0,0 @@
|
|||
# CoreBot
|
||||
|
||||
Bot Framework v4 core bot sample.
|
||||
|
||||
This bot has been created using [Bot Framework](https://dev.botframework.com), it shows how to:
|
||||
|
||||
- Use [LUIS](https://www.luis.ai) to implement core AI capabilities
|
||||
- Implement a multi-turn conversation using Dialogs
|
||||
- Handle user interruptions for such things as `Help` or `Cancel`
|
||||
- Prompt for and validate requests for information from the user
|
||||
|
||||
## Prerequisites
|
||||
|
||||
This sample **requires** prerequisites in order to run.
|
||||
|
||||
### Overview
|
||||
|
||||
This bot uses [LUIS](https://www.luis.ai), an AI based cognitive service, to implement language understanding.
|
||||
|
||||
### Install Python 3.7
|
||||
|
||||
|
||||
### Create a LUIS Application to enable language understanding
|
||||
|
||||
LUIS language model setup, training, and application configuration steps can be found [here](https://docs.microsoft.com/azure/bot-service/bot-builder-howto-v4-luis?view=azure-bot-service-4.0&tabs=cs).
|
||||
|
||||
If you wish to create a LUIS application via the CLI, these steps can be found in the [README-LUIS.md](README-LUIS.md).
|
||||
|
||||
## Running the sample
|
||||
- Bring up a terminal, navigate to `botbuilder-samples\samples\python\13.core-bot` folder
|
||||
- Activate your desired virtual environment
|
||||
- Run `pip install -r requirements.txt` to install all dependencies
|
||||
- Update LuisAppId, LuisAPIKey and LuisAPIHostName in `config.py` with the information retrieved from the [LUIS portal](https://www.luis.ai)
|
||||
- Run your bot with `python app.py`
|
||||
|
||||
|
||||
## Testing the bot using Bot Framework Emulator
|
||||
|
||||
[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.3.0 or greater from [here](https://github.com/Microsoft/BotFramework-Emulator/releases)
|
||||
|
||||
### Connect to the bot using Bot Framework Emulator
|
||||
|
||||
- Launch Bot Framework Emulator
|
||||
- Enter a Bot URL of `http://localhost:3978/api/messages`
|
||||
|
||||
## Deploy the bot to Azure
|
||||
|
||||
To learn more about deploying a bot to Azure, see [Deploy your bot to Azure](https://aka.ms/azuredeployment) for a complete list of deployment instructions.
|
||||
|
||||
# Further reading
|
||||
|
||||
- [Bot Framework Documentation](https://docs.botframework.com)
|
||||
- [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0)
|
||||
- [Dialogs](https://docs.microsoft.com/azure/bot-service/bot-builder-concept-dialog?view=azure-bot-service-4.0)
|
||||
- [Gathering Input Using Prompts](https://docs.microsoft.com/azure/bot-service/bot-builder-prompts?view=azure-bot-service-4.0&tabs=csharp)
|
||||
- [Activity processing](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0)
|
||||
- [Azure Bot Service Introduction](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0)
|
||||
- [Azure Bot Service Documentation](https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0)
|
||||
- [Azure CLI](https://docs.microsoft.com/cli/azure/?view=azure-cli-latest)
|
||||
- [Azure Portal](https://portal.azure.com)
|
||||
- [Language Understanding using LUIS](https://docs.microsoft.com/azure/cognitive-services/luis/)
|
||||
- [Channels and Bot Connector Service](https://docs.microsoft.com/azure/bot-service/bot-concepts?view=azure-bot-service-4.0)
|
|
@ -1,56 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
import sys
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
|
||||
from botbuilder.core import (
|
||||
BotFrameworkAdapter,
|
||||
BotFrameworkAdapterSettings,
|
||||
ConversationState,
|
||||
TurnContext,
|
||||
)
|
||||
from botbuilder.schema import ActivityTypes, Activity
|
||||
|
||||
|
||||
class AdapterWithErrorHandler(BotFrameworkAdapter):
|
||||
def __init__(
|
||||
self,
|
||||
settings: BotFrameworkAdapterSettings,
|
||||
conversation_state: ConversationState,
|
||||
):
|
||||
super().__init__(settings)
|
||||
self._conversation_state = conversation_state
|
||||
|
||||
# Catch-all for errors.
|
||||
async def on_error(context: TurnContext, error: Exception):
|
||||
# This check writes out errors to console log
|
||||
# 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)
|
||||
traceback.print_exc()
|
||||
|
||||
# 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
|
||||
nonlocal self
|
||||
await self._conversation_state.delete(context)
|
||||
|
||||
self.on_turn_error = on_error
|
|
@ -1,75 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
"""
|
||||
This sample shows how to create a bot that demonstrates the following:
|
||||
- Use [LUIS](https://www.luis.ai) to implement core AI capabilities.
|
||||
- Implement a multi-turn conversation using Dialogs.
|
||||
- Handle user interruptions for such things as `Help` or `Cancel`.
|
||||
- Prompt for and validate requests for information from the user.
|
||||
"""
|
||||
|
||||
from quart import Quart, request, Response
|
||||
from botbuilder.core import (
|
||||
BotFrameworkAdapterSettings,
|
||||
ConversationState,
|
||||
MemoryStorage,
|
||||
UserState,
|
||||
)
|
||||
from botbuilder.schema import Activity
|
||||
|
||||
from dialogs import MainDialog, BookingDialog
|
||||
from bots import DialogAndWelcomeBot
|
||||
|
||||
from adapter_with_error_handler import AdapterWithErrorHandler
|
||||
from flight_booking_recognizer import FlightBookingRecognizer
|
||||
|
||||
APP = Quart(__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"])
|
||||
|
||||
# Create MemoryStorage, UserState and ConversationState
|
||||
MEMORY = MemoryStorage()
|
||||
USER_STATE = UserState(MEMORY)
|
||||
CONVERSATION_STATE = ConversationState(MEMORY)
|
||||
|
||||
# Create adapter.
|
||||
# See https://aka.ms/about-bot-adapter to learn more about how bots work.
|
||||
ADAPTER = AdapterWithErrorHandler(SETTINGS, CONVERSATION_STATE)
|
||||
|
||||
# Create dialogs and Bot
|
||||
RECOGNIZER = FlightBookingRecognizer(APP.config)
|
||||
BOOKING_DIALOG = BookingDialog()
|
||||
DIALOG = MainDialog(RECOGNIZER, BOOKING_DIALOG)
|
||||
BOT = DialogAndWelcomeBot(CONVERSATION_STATE, USER_STATE, DIALOG)
|
||||
|
||||
|
||||
# Listen for incoming requests on /api/messages
|
||||
@APP.route("/api/messages", methods=["POST"])
|
||||
async def messages():
|
||||
# Main bot message handler.
|
||||
if "application/json" in request.headers["Content-Type"]:
|
||||
body = await request.json
|
||||
else:
|
||||
return Response("", status=415)
|
||||
|
||||
activity = Activity().deserialize(body)
|
||||
auth_header = (
|
||||
request.headers["Authorization"] if "Authorization" in request.headers else ""
|
||||
)
|
||||
|
||||
try:
|
||||
await ADAPTER.process_activity(activity, auth_header, BOT.on_turn)
|
||||
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
|
|
@ -1,18 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
|
||||
class BookingDetails:
|
||||
def __init__(
|
||||
self,
|
||||
destination: str = None,
|
||||
origin: str = None,
|
||||
travel_date: str = None,
|
||||
unsupported_airports=None,
|
||||
):
|
||||
if unsupported_airports is None:
|
||||
unsupported_airports = []
|
||||
self.destination = destination
|
||||
self.origin = origin
|
||||
self.travel_date = travel_date
|
||||
self.unsupported_airports = unsupported_airports
|
|
@ -1,7 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from .dialog_bot import DialogBot
|
||||
from .dialog_and_welcome_bot import DialogAndWelcomeBot
|
||||
|
||||
__all__ = ["DialogBot", "DialogAndWelcomeBot"]
|
|
@ -1,57 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import json
|
||||
import os.path
|
||||
|
||||
from typing import List
|
||||
from botbuilder.core import (
|
||||
ConversationState,
|
||||
MessageFactory,
|
||||
UserState,
|
||||
TurnContext,
|
||||
)
|
||||
from botbuilder.dialogs import Dialog
|
||||
from botbuilder.schema import Attachment, ChannelAccount
|
||||
from helpers.dialog_helper import DialogHelper
|
||||
|
||||
from .dialog_bot import DialogBot
|
||||
|
||||
|
||||
class DialogAndWelcomeBot(DialogBot):
|
||||
def __init__(
|
||||
self,
|
||||
conversation_state: ConversationState,
|
||||
user_state: UserState,
|
||||
dialog: Dialog,
|
||||
):
|
||||
super(DialogAndWelcomeBot, self).__init__(
|
||||
conversation_state, user_state, dialog
|
||||
)
|
||||
|
||||
async def on_members_added_activity(
|
||||
self, members_added: List[ChannelAccount], turn_context: TurnContext
|
||||
):
|
||||
for member in members_added:
|
||||
# Greet anyone that was not the target (recipient) of this message.
|
||||
# To learn more about Adaptive Cards, see https://aka.ms/msbot-adaptivecards for more details.
|
||||
if member.id != turn_context.activity.recipient.id:
|
||||
welcome_card = self.create_adaptive_card_attachment()
|
||||
response = MessageFactory.attachment(welcome_card)
|
||||
await turn_context.send_activity(response)
|
||||
await DialogHelper.run_dialog(
|
||||
self.dialog,
|
||||
turn_context,
|
||||
self.conversation_state.create_property("DialogState"),
|
||||
)
|
||||
|
||||
# Load attachment from file.
|
||||
def create_adaptive_card_attachment(self):
|
||||
relative_path = os.path.abspath(os.path.dirname(__file__))
|
||||
path = os.path.join(relative_path, "../cards/welcomeCard.json")
|
||||
with open(path) as in_file:
|
||||
card = json.load(in_file)
|
||||
|
||||
return Attachment(
|
||||
content_type="application/vnd.microsoft.card.adaptive", content=card
|
||||
)
|
|
@ -1,41 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from botbuilder.core import ActivityHandler, ConversationState, UserState, TurnContext
|
||||
from botbuilder.dialogs import Dialog
|
||||
from helpers.dialog_helper import DialogHelper
|
||||
|
||||
|
||||
class DialogBot(ActivityHandler):
|
||||
def __init__(
|
||||
self,
|
||||
conversation_state: ConversationState,
|
||||
user_state: UserState,
|
||||
dialog: Dialog,
|
||||
):
|
||||
if conversation_state is None:
|
||||
raise Exception(
|
||||
"[DialogBot]: Missing parameter. conversation_state is required"
|
||||
)
|
||||
if user_state is None:
|
||||
raise Exception("[DialogBot]: Missing parameter. user_state is required")
|
||||
if dialog is None:
|
||||
raise Exception("[DialogBot]: Missing parameter. dialog is required")
|
||||
|
||||
self.conversation_state = conversation_state
|
||||
self.user_state = user_state
|
||||
self.dialog = dialog
|
||||
|
||||
async def on_turn(self, turn_context: TurnContext):
|
||||
await super().on_turn(turn_context)
|
||||
|
||||
# Save any state changes that might have occurred during the turn.
|
||||
await self.conversation_state.save_changes(turn_context, False)
|
||||
await self.user_state.save_changes(turn_context, False)
|
||||
|
||||
async def on_message_activity(self, turn_context: TurnContext):
|
||||
await DialogHelper.run_dialog(
|
||||
self.dialog,
|
||||
turn_context,
|
||||
self.conversation_state.create_property("DialogState"),
|
||||
)
|
|
@ -1,46 +0,0 @@
|
|||
{
|
||||
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
|
||||
"type": "AdaptiveCard",
|
||||
"version": "1.0",
|
||||
"body": [
|
||||
{
|
||||
"type": "Image",
|
||||
"url": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQtB3AwMUeNoq4gUBGe6Ocj8kyh3bXa9ZbV7u1fVKQoyKFHdkqU",
|
||||
"size": "stretch"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"spacing": "medium",
|
||||
"size": "default",
|
||||
"weight": "bolder",
|
||||
"text": "Welcome to Bot Framework!",
|
||||
"wrap": true,
|
||||
"maxLines": 0
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"size": "default",
|
||||
"isSubtle": "true",
|
||||
"text": "Now that you have successfully run your bot, follow the links in this Adaptive Card to expand your knowledge of Bot Framework.",
|
||||
"wrap": true,
|
||||
"maxLines": 0
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": "Action.OpenUrl",
|
||||
"title": "Get an overview",
|
||||
"url": "https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0"
|
||||
},
|
||||
{
|
||||
"type": "Action.OpenUrl",
|
||||
"title": "Ask a question",
|
||||
"url": "https://stackoverflow.com/questions/tagged/botframework"
|
||||
},
|
||||
{
|
||||
"type": "Action.OpenUrl",
|
||||
"title": "Learn how to deploy",
|
||||
"url": "https://docs.microsoft.com/azure/bot-service/bot-builder-howto-deploy-azure?view=azure-bot-service-4.0"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,339 +0,0 @@
|
|||
{
|
||||
"luis_schema_version": "3.2.0",
|
||||
"versionId": "0.1",
|
||||
"name": "FlightBooking",
|
||||
"desc": "Luis Model for CoreBot",
|
||||
"culture": "en-us",
|
||||
"tokenizerVersion": "1.0.0",
|
||||
"intents": [
|
||||
{
|
||||
"name": "BookFlight"
|
||||
},
|
||||
{
|
||||
"name": "Cancel"
|
||||
},
|
||||
{
|
||||
"name": "GetWeather"
|
||||
},
|
||||
{
|
||||
"name": "None"
|
||||
}
|
||||
],
|
||||
"entities": [],
|
||||
"composites": [
|
||||
{
|
||||
"name": "From",
|
||||
"children": [
|
||||
"Airport"
|
||||
],
|
||||
"roles": []
|
||||
},
|
||||
{
|
||||
"name": "To",
|
||||
"children": [
|
||||
"Airport"
|
||||
],
|
||||
"roles": []
|
||||
}
|
||||
],
|
||||
"closedLists": [
|
||||
{
|
||||
"name": "Airport",
|
||||
"subLists": [
|
||||
{
|
||||
"canonicalForm": "Paris",
|
||||
"list": [
|
||||
"paris",
|
||||
"cdg"
|
||||
]
|
||||
},
|
||||
{
|
||||
"canonicalForm": "London",
|
||||
"list": [
|
||||
"london",
|
||||
"lhr"
|
||||
]
|
||||
},
|
||||
{
|
||||
"canonicalForm": "Berlin",
|
||||
"list": [
|
||||
"berlin",
|
||||
"txl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"canonicalForm": "New York",
|
||||
"list": [
|
||||
"new york",
|
||||
"jfk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"canonicalForm": "Seattle",
|
||||
"list": [
|
||||
"seattle",
|
||||
"sea"
|
||||
]
|
||||
}
|
||||
],
|
||||
"roles": []
|
||||
}
|
||||
],
|
||||
"patternAnyEntities": [],
|
||||
"regex_entities": [],
|
||||
"prebuiltEntities": [
|
||||
{
|
||||
"name": "datetimeV2",
|
||||
"roles": []
|
||||
}
|
||||
],
|
||||
"model_features": [],
|
||||
"regex_features": [],
|
||||
"patterns": [],
|
||||
"utterances": [
|
||||
{
|
||||
"text": "book a flight",
|
||||
"intent": "BookFlight",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "book a flight from new york",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "From",
|
||||
"startPos": 19,
|
||||
"endPos": 26
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "book a flight from seattle",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "From",
|
||||
"startPos": 19,
|
||||
"endPos": 25
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "book a hotel in new york",
|
||||
"intent": "None",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "book a restaurant",
|
||||
"intent": "None",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "book flight from london to paris on feb 14th",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "From",
|
||||
"startPos": 17,
|
||||
"endPos": 22
|
||||
},
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 27,
|
||||
"endPos": 31
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "book flight to berlin on feb 14th",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 15,
|
||||
"endPos": 20
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "book me a flight from london to paris",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "From",
|
||||
"startPos": 22,
|
||||
"endPos": 27
|
||||
},
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 32,
|
||||
"endPos": 36
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "bye",
|
||||
"intent": "Cancel",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "cancel booking",
|
||||
"intent": "Cancel",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "exit",
|
||||
"intent": "Cancel",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "find an airport near me",
|
||||
"intent": "None",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "flight to paris",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 10,
|
||||
"endPos": 14
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "flight to paris from london on feb 14th",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 10,
|
||||
"endPos": 14
|
||||
},
|
||||
{
|
||||
"entity": "From",
|
||||
"startPos": 21,
|
||||
"endPos": 26
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "fly from berlin to paris on may 5th",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "From",
|
||||
"startPos": 9,
|
||||
"endPos": 14
|
||||
},
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 19,
|
||||
"endPos": 23
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "go to paris",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 6,
|
||||
"endPos": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "going from paris to berlin",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "From",
|
||||
"startPos": 11,
|
||||
"endPos": 15
|
||||
},
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 20,
|
||||
"endPos": 25
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "i'd like to rent a car",
|
||||
"intent": "None",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "ignore",
|
||||
"intent": "Cancel",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "travel from new york to paris",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "From",
|
||||
"startPos": 12,
|
||||
"endPos": 19
|
||||
},
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 24,
|
||||
"endPos": 28
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "travel to new york",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 10,
|
||||
"endPos": 17
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "travel to paris",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 10,
|
||||
"endPos": 14
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "what's the forecast for this friday?",
|
||||
"intent": "GetWeather",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "what's the weather like for tomorrow",
|
||||
"intent": "GetWeather",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "what's the weather like in new york",
|
||||
"intent": "GetWeather",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "what's the weather like?",
|
||||
"intent": "GetWeather",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "winter is coming",
|
||||
"intent": "None",
|
||||
"entities": []
|
||||
}
|
||||
],
|
||||
"settings": []
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
#!/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", "")
|
||||
LUIS_APP_ID = os.environ.get("LuisAppId", "")
|
||||
LUIS_API_KEY = os.environ.get("LuisAPIKey", "")
|
||||
# LUIS endpoint host name, ie "westus.api.cognitive.microsoft.com"
|
||||
LUIS_API_HOST_NAME = os.environ.get("LuisAPIHostName", "")
|
|
@ -1,265 +0,0 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"groupLocation": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Specifies the location of the Resource Group."
|
||||
}
|
||||
},
|
||||
"groupName": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Specifies the name of the Resource Group."
|
||||
}
|
||||
},
|
||||
"appId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"appSecret": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"botId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable."
|
||||
}
|
||||
},
|
||||
"botSku": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanName": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The name of the App Service Plan."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanSku": {
|
||||
"type": "object",
|
||||
"defaultValue": {
|
||||
"name": "S1",
|
||||
"tier": "Standard",
|
||||
"size": "S1",
|
||||
"family": "S",
|
||||
"capacity": 1
|
||||
},
|
||||
"metadata": {
|
||||
"description": "The SKU of the App Service Plan. Defaults to Standard values."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanLocation": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The location of the App Service Plan. Defaults to \"westus\"."
|
||||
}
|
||||
},
|
||||
"newWebAppName": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"."
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"appServicePlanName": "[parameters('newAppServicePlanName')]",
|
||||
"resourcesLocation": "[parameters('newAppServicePlanLocation')]",
|
||||
"webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]",
|
||||
"siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]",
|
||||
"botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]",
|
||||
"publishingUsername": "[concat('$', parameters('newWebAppName'))]",
|
||||
"resourceGroupId": "[concat(subscription().id, '/resourceGroups/', parameters('groupName'))]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"name": "[parameters('groupName')]",
|
||||
"type": "Microsoft.Resources/resourceGroups",
|
||||
"apiVersion": "2018-05-01",
|
||||
"location": "[parameters('groupLocation')]",
|
||||
"properties": {}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Resources/deployments",
|
||||
"apiVersion": "2018-05-01",
|
||||
"name": "storageDeployment",
|
||||
"resourceGroup": "[parameters('groupName')]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Resources/resourceGroups/', parameters('groupName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"mode": "Incremental",
|
||||
"template": {
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {},
|
||||
"variables": {},
|
||||
"resources": [
|
||||
{
|
||||
"comments": "Create a new Linux App Service Plan if no existing App Service Plan name was passed in.",
|
||||
"type": "Microsoft.Web/serverfarms",
|
||||
"name": "[variables('appServicePlanName')]",
|
||||
"apiVersion": "2018-02-01",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"sku": "[parameters('newAppServicePlanSku')]",
|
||||
"kind": "linux",
|
||||
"properties": {
|
||||
"name": "[variables('appServicePlanName')]",
|
||||
"perSiteScaling": false,
|
||||
"reserved": true,
|
||||
"targetWorkerCount": 0,
|
||||
"targetWorkerSizeId": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"comments": "Create a Web App using a Linux App Service Plan",
|
||||
"type": "Microsoft.Web/sites",
|
||||
"apiVersion": "2015-08-01",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"kind": "app,linux",
|
||||
"dependsOn": [
|
||||
"[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/serverfarms/', variables('appServicePlanName'))]"
|
||||
],
|
||||
"name": "[variables('webAppName')]",
|
||||
"properties": {
|
||||
"name": "[variables('webAppName')]",
|
||||
"hostNameSslStates": [
|
||||
{
|
||||
"name": "[concat(parameters('newWebAppName'), '.azurewebsites.net')]",
|
||||
"sslState": "Disabled",
|
||||
"hostType": "Standard"
|
||||
},
|
||||
{
|
||||
"name": "[concat(parameters('newWebAppName'), '.scm.azurewebsites.net')]",
|
||||
"sslState": "Disabled",
|
||||
"hostType": "Repository"
|
||||
}
|
||||
],
|
||||
"serverFarmId": "[variables('appServicePlanName')]",
|
||||
"siteConfig": {
|
||||
"appSettings": [
|
||||
{
|
||||
"name": "SCM_DO_BUILD_DURING_DEPLOYMENT",
|
||||
"value": "true"
|
||||
},
|
||||
{
|
||||
"name": "MicrosoftAppId",
|
||||
"value": "[parameters('appId')]"
|
||||
},
|
||||
{
|
||||
"name": "MicrosoftAppPassword",
|
||||
"value": "[parameters('appSecret')]"
|
||||
}
|
||||
],
|
||||
"cors": {
|
||||
"allowedOrigins": [
|
||||
"https://botservice.hosting.portal.azure.net",
|
||||
"https://hosting.onecloud.azure-test.net/"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Web/sites/config",
|
||||
"apiVersion": "2016-08-01",
|
||||
"name": "[concat(variables('webAppName'), '/web')]",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"dependsOn": [
|
||||
"[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/sites/', variables('webAppName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"numberOfWorkers": 1,
|
||||
"defaultDocuments": [
|
||||
"Default.htm",
|
||||
"Default.html",
|
||||
"Default.asp",
|
||||
"index.htm",
|
||||
"index.html",
|
||||
"iisstart.htm",
|
||||
"default.aspx",
|
||||
"index.php",
|
||||
"hostingstart.html"
|
||||
],
|
||||
"netFrameworkVersion": "v4.0",
|
||||
"phpVersion": "",
|
||||
"pythonVersion": "",
|
||||
"nodeVersion": "",
|
||||
"linuxFxVersion": "PYTHON|3.7",
|
||||
"requestTracingEnabled": false,
|
||||
"remoteDebuggingEnabled": false,
|
||||
"remoteDebuggingVersion": "VS2017",
|
||||
"httpLoggingEnabled": true,
|
||||
"logsDirectorySizeLimit": 35,
|
||||
"detailedErrorLoggingEnabled": false,
|
||||
"publishingUsername": "[variables('publishingUsername')]",
|
||||
"scmType": "None",
|
||||
"use32BitWorkerProcess": true,
|
||||
"webSocketsEnabled": false,
|
||||
"alwaysOn": false,
|
||||
"appCommandLine": "gunicorn --bind 0.0.0.0 --worker-class aiohttp.worker.GunicornWebWorker --timeout 600 app:APP",
|
||||
"managedPipelineMode": "Integrated",
|
||||
"virtualApplications": [
|
||||
{
|
||||
"virtualPath": "/",
|
||||
"physicalPath": "site\\wwwroot",
|
||||
"preloadEnabled": false,
|
||||
"virtualDirectories": null
|
||||
}
|
||||
],
|
||||
"winAuthAdminState": 0,
|
||||
"winAuthTenantState": 0,
|
||||
"customAppPoolIdentityAdminState": false,
|
||||
"customAppPoolIdentityTenantState": false,
|
||||
"loadBalancing": "LeastRequests",
|
||||
"routingRules": [],
|
||||
"experiments": {
|
||||
"rampUpRules": []
|
||||
},
|
||||
"autoHealEnabled": false,
|
||||
"vnetName": "",
|
||||
"minTlsVersion": "1.2",
|
||||
"ftpsState": "AllAllowed",
|
||||
"reservedInstanceCount": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2021-03-01",
|
||||
"type": "Microsoft.BotService/botServices",
|
||||
"name": "[parameters('botId')]",
|
||||
"location": "global",
|
||||
"kind": "azurebot",
|
||||
"sku": {
|
||||
"name": "[parameters('botSku')]"
|
||||
},
|
||||
"properties": {
|
||||
"name": "[parameters('botId')]",
|
||||
"displayName": "[parameters('botId')]",
|
||||
"iconUrl": "https://docs.botframework.com/static/devportal/client/images/bot-framework-default.png",
|
||||
"endpoint": "[variables('botEndpoint')]",
|
||||
"msaAppId": "[parameters('appId')]",
|
||||
"luisAppIds": [],
|
||||
"schemaTransformationVersion": "1.3",
|
||||
"isCmekEnabled": false,
|
||||
"isIsolated": false
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/sites/', variables('webAppName'))]"
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputs": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,243 +0,0 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"appId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"appSecret": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"botId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable."
|
||||
}
|
||||
},
|
||||
"botSku": {
|
||||
"defaultValue": "F0",
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanName": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "The name of the new App Service Plan."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanSku": {
|
||||
"type": "object",
|
||||
"defaultValue": {
|
||||
"name": "S1",
|
||||
"tier": "Standard",
|
||||
"size": "S1",
|
||||
"family": "S",
|
||||
"capacity": 1
|
||||
},
|
||||
"metadata": {
|
||||
"description": "The SKU of the App Service Plan. Defaults to Standard values."
|
||||
}
|
||||
},
|
||||
"appServicePlanLocation": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The location of the App Service Plan."
|
||||
}
|
||||
},
|
||||
"existingAppServicePlan": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "Name of the existing App Service Plan used to create the Web App for the bot."
|
||||
}
|
||||
},
|
||||
"newWebAppName": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"."
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"defaultAppServicePlanName": "[if(empty(parameters('existingAppServicePlan')), 'createNewAppServicePlan', parameters('existingAppServicePlan'))]",
|
||||
"useExistingAppServicePlan": "[not(equals(variables('defaultAppServicePlanName'), 'createNewAppServicePlan'))]",
|
||||
"servicePlanName": "[if(variables('useExistingAppServicePlan'), parameters('existingAppServicePlan'), parameters('newAppServicePlanName'))]",
|
||||
"publishingUsername": "[concat('$', parameters('newWebAppName'))]",
|
||||
"resourcesLocation": "[parameters('appServicePlanLocation')]",
|
||||
"webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]",
|
||||
"siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]",
|
||||
"botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"comments": "Create a new Linux App Service Plan if no existing App Service Plan name was passed in.",
|
||||
"type": "Microsoft.Web/serverfarms",
|
||||
"apiVersion": "2016-09-01",
|
||||
"name": "[variables('servicePlanName')]",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"sku": "[parameters('newAppServicePlanSku')]",
|
||||
"kind": "linux",
|
||||
"properties": {
|
||||
"name": "[variables('servicePlanName')]",
|
||||
"perSiteScaling": false,
|
||||
"reserved": true,
|
||||
"targetWorkerCount": 0,
|
||||
"targetWorkerSizeId": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"comments": "Create a Web App using a Linux App Service Plan",
|
||||
"type": "Microsoft.Web/sites",
|
||||
"apiVersion": "2016-08-01",
|
||||
"name": "[variables('webAppName')]",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]"
|
||||
],
|
||||
"kind": "app,linux",
|
||||
"properties": {
|
||||
"enabled": true,
|
||||
"hostNameSslStates": [
|
||||
{
|
||||
"name": "[concat(parameters('newWebAppName'), '.azurewebsites.net')]",
|
||||
"sslState": "Disabled",
|
||||
"hostType": "Standard"
|
||||
},
|
||||
{
|
||||
"name": "[concat(parameters('newWebAppName'), '.scm.azurewebsites.net')]",
|
||||
"sslState": "Disabled",
|
||||
"hostType": "Repository"
|
||||
}
|
||||
],
|
||||
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]",
|
||||
"reserved": true,
|
||||
"scmSiteAlsoStopped": false,
|
||||
"clientAffinityEnabled": false,
|
||||
"clientCertEnabled": false,
|
||||
"hostNamesDisabled": false,
|
||||
"containerSize": 0,
|
||||
"dailyMemoryTimeQuota": 0,
|
||||
"httpsOnly": false,
|
||||
"siteConfig": {
|
||||
"appSettings": [
|
||||
{
|
||||
"name": "MicrosoftAppId",
|
||||
"value": "[parameters('appId')]"
|
||||
},
|
||||
{
|
||||
"name": "MicrosoftAppPassword",
|
||||
"value": "[parameters('appSecret')]"
|
||||
},
|
||||
{
|
||||
"name": "SCM_DO_BUILD_DURING_DEPLOYMENT",
|
||||
"value": "true"
|
||||
}
|
||||
],
|
||||
"cors": {
|
||||
"allowedOrigins": [
|
||||
"https://botservice.hosting.portal.azure.net",
|
||||
"https://hosting.onecloud.azure-test.net/"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Web/sites/config",
|
||||
"apiVersion": "2016-08-01",
|
||||
"name": "[concat(variables('webAppName'), '/web')]",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Web/sites', variables('webAppName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"numberOfWorkers": 1,
|
||||
"defaultDocuments": [
|
||||
"Default.htm",
|
||||
"Default.html",
|
||||
"Default.asp",
|
||||
"index.htm",
|
||||
"index.html",
|
||||
"iisstart.htm",
|
||||
"default.aspx",
|
||||
"index.php",
|
||||
"hostingstart.html"
|
||||
],
|
||||
"netFrameworkVersion": "v4.0",
|
||||
"phpVersion": "",
|
||||
"pythonVersion": "",
|
||||
"nodeVersion": "",
|
||||
"linuxFxVersion": "PYTHON|3.7",
|
||||
"requestTracingEnabled": false,
|
||||
"remoteDebuggingEnabled": false,
|
||||
"remoteDebuggingVersion": "VS2017",
|
||||
"httpLoggingEnabled": true,
|
||||
"logsDirectorySizeLimit": 35,
|
||||
"detailedErrorLoggingEnabled": false,
|
||||
"publishingUsername": "[variables('publishingUsername')]",
|
||||
"scmType": "None",
|
||||
"use32BitWorkerProcess": true,
|
||||
"webSocketsEnabled": false,
|
||||
"alwaysOn": false,
|
||||
"appCommandLine": "gunicorn --bind 0.0.0.0 --worker-class aiohttp.worker.GunicornWebWorker --timeout 600 app:APP",
|
||||
"managedPipelineMode": "Integrated",
|
||||
"virtualApplications": [
|
||||
{
|
||||
"virtualPath": "/",
|
||||
"physicalPath": "site\\wwwroot",
|
||||
"preloadEnabled": false,
|
||||
"virtualDirectories": null
|
||||
}
|
||||
],
|
||||
"winAuthAdminState": 0,
|
||||
"winAuthTenantState": 0,
|
||||
"customAppPoolIdentityAdminState": false,
|
||||
"customAppPoolIdentityTenantState": false,
|
||||
"loadBalancing": "LeastRequests",
|
||||
"routingRules": [],
|
||||
"experiments": {
|
||||
"rampUpRules": []
|
||||
},
|
||||
"autoHealEnabled": false,
|
||||
"vnetName": "",
|
||||
"minTlsVersion": "1.2",
|
||||
"ftpsState": "AllAllowed",
|
||||
"reservedInstanceCount": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2021-03-01",
|
||||
"type": "Microsoft.BotService/botServices",
|
||||
"name": "[parameters('botId')]",
|
||||
"location": "global",
|
||||
"kind": "azurebot",
|
||||
"sku": {
|
||||
"name": "[parameters('botSku')]"
|
||||
},
|
||||
"properties": {
|
||||
"name": "[parameters('botId')]",
|
||||
"displayName": "[parameters('botId')]",
|
||||
"iconUrl": "https://docs.botframework.com/static/devportal/client/images/bot-framework-default.png",
|
||||
"endpoint": "[variables('botEndpoint')]",
|
||||
"msaAppId": "[parameters('appId')]",
|
||||
"luisAppIds": [],
|
||||
"schemaTransformationVersion": "1.3",
|
||||
"isCmekEnabled": false,
|
||||
"isIsolated": false
|
||||
},
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Web/sites/', variables('webAppName'))]"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from .booking_dialog import BookingDialog
|
||||
from .cancel_and_help_dialog import CancelAndHelpDialog
|
||||
from .date_resolver_dialog import DateResolverDialog
|
||||
from .main_dialog import MainDialog
|
||||
|
||||
__all__ = ["BookingDialog", "CancelAndHelpDialog", "DateResolverDialog", "MainDialog"]
|
|
@ -1,136 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from datatypes_date_time.timex import Timex
|
||||
|
||||
from botbuilder.dialogs import WaterfallDialog, WaterfallStepContext, DialogTurnResult
|
||||
from botbuilder.dialogs.prompts import ConfirmPrompt, TextPrompt, PromptOptions
|
||||
from botbuilder.core import MessageFactory
|
||||
from botbuilder.schema import InputHints
|
||||
from .cancel_and_help_dialog import CancelAndHelpDialog
|
||||
from .date_resolver_dialog import DateResolverDialog
|
||||
|
||||
|
||||
class BookingDialog(CancelAndHelpDialog):
|
||||
def __init__(self, dialog_id: str = None):
|
||||
super(BookingDialog, self).__init__(dialog_id or BookingDialog.__name__)
|
||||
|
||||
self.add_dialog(TextPrompt(TextPrompt.__name__))
|
||||
self.add_dialog(ConfirmPrompt(ConfirmPrompt.__name__))
|
||||
self.add_dialog(DateResolverDialog(DateResolverDialog.__name__))
|
||||
self.add_dialog(
|
||||
WaterfallDialog(
|
||||
WaterfallDialog.__name__,
|
||||
[
|
||||
self.destination_step,
|
||||
self.origin_step,
|
||||
self.travel_date_step,
|
||||
self.confirm_step,
|
||||
self.final_step,
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
self.initial_dialog_id = WaterfallDialog.__name__
|
||||
|
||||
async def destination_step(
|
||||
self, step_context: WaterfallStepContext
|
||||
) -> DialogTurnResult:
|
||||
"""
|
||||
If a destination city has not been provided, prompt for one.
|
||||
:param step_context:
|
||||
:return DialogTurnResult:
|
||||
"""
|
||||
booking_details = step_context.options
|
||||
|
||||
if booking_details.destination is None:
|
||||
message_text = "Where would you like to travel to?"
|
||||
prompt_message = MessageFactory.text(
|
||||
message_text, message_text, InputHints.expecting_input
|
||||
)
|
||||
return await step_context.prompt(
|
||||
TextPrompt.__name__, PromptOptions(prompt=prompt_message)
|
||||
)
|
||||
return await step_context.next(booking_details.destination)
|
||||
|
||||
async def origin_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
|
||||
"""
|
||||
If an origin city has not been provided, prompt for one.
|
||||
:param step_context:
|
||||
:return DialogTurnResult:
|
||||
"""
|
||||
booking_details = step_context.options
|
||||
|
||||
# Capture the response to the previous step's prompt
|
||||
booking_details.destination = step_context.result
|
||||
if booking_details.origin is None:
|
||||
message_text = "From what city will you be travelling?"
|
||||
prompt_message = MessageFactory.text(
|
||||
message_text, message_text, InputHints.expecting_input
|
||||
)
|
||||
return await step_context.prompt(
|
||||
TextPrompt.__name__, PromptOptions(prompt=prompt_message)
|
||||
)
|
||||
return await step_context.next(booking_details.origin)
|
||||
|
||||
async def travel_date_step(
|
||||
self, step_context: WaterfallStepContext
|
||||
) -> DialogTurnResult:
|
||||
"""
|
||||
If a travel date has not been provided, prompt for one.
|
||||
This will use the DATE_RESOLVER_DIALOG.
|
||||
:param step_context:
|
||||
:return DialogTurnResult:
|
||||
"""
|
||||
booking_details = step_context.options
|
||||
|
||||
# Capture the results of the previous step
|
||||
booking_details.origin = step_context.result
|
||||
if not booking_details.travel_date or self.is_ambiguous(
|
||||
booking_details.travel_date
|
||||
):
|
||||
return await step_context.begin_dialog(
|
||||
DateResolverDialog.__name__, booking_details.travel_date
|
||||
)
|
||||
return await step_context.next(booking_details.travel_date)
|
||||
|
||||
async def confirm_step(
|
||||
self, step_context: WaterfallStepContext
|
||||
) -> DialogTurnResult:
|
||||
"""
|
||||
Confirm the information the user has provided.
|
||||
:param step_context:
|
||||
:return DialogTurnResult:
|
||||
"""
|
||||
booking_details = step_context.options
|
||||
|
||||
# Capture the results of the previous step
|
||||
booking_details.travel_date = step_context.result
|
||||
message_text = (
|
||||
f"Please confirm, I have you traveling to: { booking_details.destination } from: "
|
||||
f"{ booking_details.origin } on: { booking_details.travel_date}."
|
||||
)
|
||||
prompt_message = MessageFactory.text(
|
||||
message_text, message_text, InputHints.expecting_input
|
||||
)
|
||||
|
||||
# Offer a YES/NO prompt.
|
||||
return await step_context.prompt(
|
||||
ConfirmPrompt.__name__, PromptOptions(prompt=prompt_message)
|
||||
)
|
||||
|
||||
async def final_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
|
||||
"""
|
||||
Complete the interaction and end the dialog.
|
||||
:param step_context:
|
||||
:return DialogTurnResult:
|
||||
"""
|
||||
if step_context.result:
|
||||
booking_details = step_context.options
|
||||
|
||||
return await step_context.end_dialog(booking_details)
|
||||
return await step_context.end_dialog()
|
||||
|
||||
def is_ambiguous(self, timex: str) -> bool:
|
||||
timex_property = Timex(timex)
|
||||
return "definite" not in timex_property.types
|
|
@ -1,47 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from botbuilder.dialogs import (
|
||||
ComponentDialog,
|
||||
DialogContext,
|
||||
DialogTurnResult,
|
||||
DialogTurnStatus,
|
||||
)
|
||||
from botbuilder.schema import ActivityTypes, InputHints
|
||||
from botbuilder.core import MessageFactory
|
||||
|
||||
|
||||
class CancelAndHelpDialog(ComponentDialog):
|
||||
def __init__(self, dialog_id: str):
|
||||
super(CancelAndHelpDialog, self).__init__(dialog_id)
|
||||
|
||||
async def on_continue_dialog(self, inner_dc: DialogContext) -> DialogTurnResult:
|
||||
result = await self.interrupt(inner_dc)
|
||||
if result is not None:
|
||||
return result
|
||||
|
||||
return await super(CancelAndHelpDialog, self).on_continue_dialog(inner_dc)
|
||||
|
||||
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_text = "Show Help..."
|
||||
help_message = MessageFactory.text(
|
||||
help_message_text, help_message_text, InputHints.expecting_input
|
||||
)
|
||||
|
||||
if text in ("help", "?"):
|
||||
await inner_dc.context.send_activity(help_message)
|
||||
return DialogTurnResult(DialogTurnStatus.Waiting)
|
||||
|
||||
cancel_message_text = "Cancelling"
|
||||
cancel_message = MessageFactory.text(
|
||||
cancel_message_text, cancel_message_text, InputHints.ignoring_input
|
||||
)
|
||||
|
||||
if text in ("cancel", "quit"):
|
||||
await inner_dc.context.send_activity(cancel_message)
|
||||
return await inner_dc.cancel_all_dialogs()
|
||||
|
||||
return None
|
|
@ -1,80 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from datatypes_date_time.timex import Timex
|
||||
|
||||
from botbuilder.core import MessageFactory
|
||||
from botbuilder.dialogs import WaterfallDialog, DialogTurnResult, WaterfallStepContext
|
||||
from botbuilder.dialogs.prompts import (
|
||||
DateTimePrompt,
|
||||
PromptValidatorContext,
|
||||
PromptOptions,
|
||||
DateTimeResolution,
|
||||
)
|
||||
from botbuilder.schema import InputHints
|
||||
from .cancel_and_help_dialog import CancelAndHelpDialog
|
||||
|
||||
|
||||
class DateResolverDialog(CancelAndHelpDialog):
|
||||
def __init__(self, dialog_id: str = None):
|
||||
super(DateResolverDialog, self).__init__(
|
||||
dialog_id or DateResolverDialog.__name__
|
||||
)
|
||||
|
||||
self.add_dialog(
|
||||
DateTimePrompt(
|
||||
DateTimePrompt.__name__, DateResolverDialog.datetime_prompt_validator
|
||||
)
|
||||
)
|
||||
self.add_dialog(
|
||||
WaterfallDialog(
|
||||
WaterfallDialog.__name__ + "2", [self.initial_step, self.final_step]
|
||||
)
|
||||
)
|
||||
|
||||
self.initial_dialog_id = WaterfallDialog.__name__ + "2"
|
||||
|
||||
async def initial_step(
|
||||
self, step_context: WaterfallStepContext
|
||||
) -> DialogTurnResult:
|
||||
timex = step_context.options
|
||||
|
||||
prompt_msg_text = "On what date would you like to travel?"
|
||||
prompt_msg = MessageFactory.text(
|
||||
prompt_msg_text, prompt_msg_text, InputHints.expecting_input
|
||||
)
|
||||
|
||||
reprompt_msg_text = "I'm sorry, for best results, please enter your travel date including the month, " \
|
||||
"day and year. "
|
||||
reprompt_msg = MessageFactory.text(
|
||||
reprompt_msg_text, reprompt_msg_text, InputHints.expecting_input
|
||||
)
|
||||
|
||||
if timex is None:
|
||||
# We were not given any date at all so prompt the user.
|
||||
return await step_context.prompt(
|
||||
DateTimePrompt.__name__,
|
||||
PromptOptions(prompt=prompt_msg, retry_prompt=reprompt_msg),
|
||||
)
|
||||
# We have a Date we just need to check it is unambiguous.
|
||||
if "definite" not in Timex(timex).types:
|
||||
# This is essentially a "reprompt" of the data we were given up front.
|
||||
return await step_context.prompt(
|
||||
DateTimePrompt.__name__, PromptOptions(prompt=reprompt_msg)
|
||||
)
|
||||
|
||||
return await step_context.next(DateTimeResolution(timex=timex))
|
||||
|
||||
async def final_step(self, step_context: WaterfallStepContext):
|
||||
timex = step_context.result[0].timex
|
||||
return await step_context.end_dialog(timex)
|
||||
|
||||
@staticmethod
|
||||
async def datetime_prompt_validator(prompt_context: PromptValidatorContext) -> bool:
|
||||
if prompt_context.recognized.succeeded:
|
||||
timex = prompt_context.recognized.value[0].timex.split("T")[0]
|
||||
|
||||
# TODO: Needs TimexProperty
|
||||
return "definite" in Timex(timex).types
|
||||
|
||||
return False
|
|
@ -1,132 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from botbuilder.dialogs import (
|
||||
ComponentDialog,
|
||||
WaterfallDialog,
|
||||
WaterfallStepContext,
|
||||
DialogTurnResult,
|
||||
)
|
||||
from botbuilder.dialogs.prompts import TextPrompt, PromptOptions
|
||||
from botbuilder.core import MessageFactory, TurnContext
|
||||
from botbuilder.schema import InputHints
|
||||
|
||||
from booking_details import BookingDetails
|
||||
from flight_booking_recognizer import FlightBookingRecognizer
|
||||
from helpers.luis_helper import LuisHelper, Intent
|
||||
from .booking_dialog import BookingDialog
|
||||
|
||||
|
||||
class MainDialog(ComponentDialog):
|
||||
def __init__(
|
||||
self, luis_recognizer: FlightBookingRecognizer, booking_dialog: BookingDialog
|
||||
):
|
||||
super(MainDialog, self).__init__(MainDialog.__name__)
|
||||
|
||||
self._luis_recognizer = luis_recognizer
|
||||
self._booking_dialog_id = booking_dialog.id
|
||||
|
||||
self.add_dialog(TextPrompt(TextPrompt.__name__))
|
||||
self.add_dialog(booking_dialog)
|
||||
self.add_dialog(
|
||||
WaterfallDialog(
|
||||
"WFDialog", [self.intro_step, self.act_step, self.final_step]
|
||||
)
|
||||
)
|
||||
|
||||
self.initial_dialog_id = "WFDialog"
|
||||
|
||||
async def intro_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
|
||||
if not self._luis_recognizer.is_configured:
|
||||
await step_context.context.send_activity(
|
||||
MessageFactory.text(
|
||||
"NOTE: LUIS is not configured. To enable all capabilities, add 'LuisAppId', 'LuisAPIKey' and "
|
||||
"'LuisAPIHostName' to the appsettings.json file.",
|
||||
input_hint=InputHints.ignoring_input,
|
||||
)
|
||||
)
|
||||
|
||||
return await step_context.next(None)
|
||||
message_text = (
|
||||
str(step_context.options)
|
||||
if step_context.options
|
||||
else "What can I help you with today?"
|
||||
)
|
||||
prompt_message = MessageFactory.text(
|
||||
message_text, message_text, InputHints.expecting_input
|
||||
)
|
||||
|
||||
return await step_context.prompt(
|
||||
TextPrompt.__name__, PromptOptions(prompt=prompt_message)
|
||||
)
|
||||
|
||||
async def act_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
|
||||
if not self._luis_recognizer.is_configured:
|
||||
# LUIS is not configured, we just run the BookingDialog path with an empty BookingDetailsInstance.
|
||||
return await step_context.begin_dialog(
|
||||
self._booking_dialog_id, BookingDetails()
|
||||
)
|
||||
|
||||
# Call LUIS and gather any potential booking details. (Note the TurnContext has the response to the prompt.)
|
||||
intent, luis_result = await LuisHelper.execute_luis_query(
|
||||
self._luis_recognizer, step_context.context
|
||||
)
|
||||
|
||||
if intent == Intent.BOOK_FLIGHT.value and luis_result:
|
||||
# Show a warning for Origin and Destination if we can't resolve them.
|
||||
await MainDialog._show_warning_for_unsupported_cities(
|
||||
step_context.context, luis_result
|
||||
)
|
||||
|
||||
# Run the BookingDialog giving it whatever details we have from the LUIS call.
|
||||
return await step_context.begin_dialog(self._booking_dialog_id, luis_result)
|
||||
|
||||
if intent == Intent.GET_WEATHER.value:
|
||||
get_weather_text = "TODO: get weather flow here"
|
||||
get_weather_message = MessageFactory.text(
|
||||
get_weather_text, get_weather_text, InputHints.ignoring_input
|
||||
)
|
||||
await step_context.context.send_activity(get_weather_message)
|
||||
|
||||
else:
|
||||
didnt_understand_text = (
|
||||
"Sorry, I didn't get that. Please try asking in a different way"
|
||||
)
|
||||
didnt_understand_message = MessageFactory.text(
|
||||
didnt_understand_text, didnt_understand_text, InputHints.ignoring_input
|
||||
)
|
||||
await step_context.context.send_activity(didnt_understand_message)
|
||||
|
||||
return await step_context.next(None)
|
||||
|
||||
async def final_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
|
||||
# If the child dialog ("BookingDialog") was cancelled or the user failed to confirm,
|
||||
# the Result here will be null.
|
||||
if step_context.result is not None:
|
||||
result = step_context.result
|
||||
|
||||
# Now we have all the booking details call the booking service.
|
||||
|
||||
# If the call to the booking service was successful tell the user.
|
||||
# time_property = Timex(result.travel_date)
|
||||
# travel_date_msg = time_property.to_natural_language(datetime.now())
|
||||
msg_txt = f"I have you booked to {result.destination} from {result.origin} on {result.travel_date}"
|
||||
message = MessageFactory.text(msg_txt, msg_txt, InputHints.ignoring_input)
|
||||
await step_context.context.send_activity(message)
|
||||
|
||||
prompt_message = "What else can I do for you?"
|
||||
return await step_context.replace_dialog(self.id, prompt_message)
|
||||
|
||||
@staticmethod
|
||||
async def _show_warning_for_unsupported_cities(
|
||||
context: TurnContext, luis_result: BookingDetails
|
||||
) -> None:
|
||||
if luis_result.unsupported_airports:
|
||||
message_text = (
|
||||
f"Sorry but the following airports are not supported:"
|
||||
f" {', '.join(luis_result.unsupported_airports)}"
|
||||
)
|
||||
message = MessageFactory.text(
|
||||
message_text, message_text, InputHints.ignoring_input
|
||||
)
|
||||
await context.send_activity(message)
|
|
@ -1,34 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from typing import Dict
|
||||
|
||||
from botbuilder.ai.luis import LuisApplication, LuisRecognizer
|
||||
from botbuilder.core import Recognizer, RecognizerResult, TurnContext
|
||||
|
||||
|
||||
class FlightBookingRecognizer(Recognizer):
|
||||
def __init__(self, configuration: Dict):
|
||||
self._recognizer = None
|
||||
|
||||
luis_is_configured = (
|
||||
configuration["LUIS_APP_ID"]
|
||||
and configuration["LUIS_API_KEY"]
|
||||
and configuration["LUIS_API_HOST_NAME"]
|
||||
)
|
||||
if luis_is_configured:
|
||||
luis_application = LuisApplication(
|
||||
configuration["LUIS_APP_ID"],
|
||||
configuration["LUIS_API_KEY"],
|
||||
"https://" + configuration["LUIS_API_HOST_NAME"],
|
||||
)
|
||||
|
||||
self._recognizer = LuisRecognizer(luis_application)
|
||||
|
||||
@property
|
||||
def is_configured(self) -> bool:
|
||||
# Returns true if luis is configured in the config.py and initialized.
|
||||
return self._recognizer is not None
|
||||
|
||||
async def recognize(self, turn_context: TurnContext) -> RecognizerResult:
|
||||
return await self._recognizer.recognize(turn_context)
|
|
@ -1,6 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from . import activity_helper, luis_helper, dialog_helper
|
||||
|
||||
__all__ = ["activity_helper", "dialog_helper", "luis_helper"]
|
|
@ -1,37 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from datetime import datetime
|
||||
from botbuilder.schema import (
|
||||
Activity,
|
||||
ActivityTypes,
|
||||
ChannelAccount,
|
||||
ConversationAccount,
|
||||
)
|
||||
|
||||
|
||||
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=[],
|
||||
)
|
|
@ -1,19 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from botbuilder.core import StatePropertyAccessor, TurnContext
|
||||
from botbuilder.dialogs import Dialog, DialogSet, DialogTurnStatus
|
||||
|
||||
|
||||
class DialogHelper:
|
||||
@staticmethod
|
||||
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)
|
||||
results = await dialog_context.continue_dialog()
|
||||
if results.status == DialogTurnStatus.Empty:
|
||||
await dialog_context.begin_dialog(dialog.id)
|
|
@ -1,102 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
from enum import Enum
|
||||
from typing import Dict
|
||||
from botbuilder.ai.luis import LuisRecognizer
|
||||
from botbuilder.core import IntentScore, TopIntent, TurnContext
|
||||
|
||||
from booking_details import BookingDetails
|
||||
|
||||
|
||||
class Intent(Enum):
|
||||
BOOK_FLIGHT = "BookFlight"
|
||||
CANCEL = "Cancel"
|
||||
GET_WEATHER = "GetWeather"
|
||||
NONE_INTENT = "NoneIntent"
|
||||
|
||||
|
||||
def top_intent(intents: Dict[Intent, dict]) -> TopIntent:
|
||||
max_intent = Intent.NONE_INTENT
|
||||
max_value = 0.0
|
||||
|
||||
for intent, value in intents:
|
||||
intent_score = IntentScore(value)
|
||||
if intent_score.score > max_value:
|
||||
max_intent, max_value = intent, intent_score.score
|
||||
|
||||
return TopIntent(max_intent, max_value)
|
||||
|
||||
|
||||
class LuisHelper:
|
||||
@staticmethod
|
||||
async def execute_luis_query(
|
||||
luis_recognizer: LuisRecognizer, turn_context: TurnContext
|
||||
) -> (Intent, object):
|
||||
"""
|
||||
Returns an object with preformatted LUIS results for the bot's dialogs to consume.
|
||||
"""
|
||||
result = None
|
||||
intent = None
|
||||
|
||||
try:
|
||||
recognizer_result = await luis_recognizer.recognize(turn_context)
|
||||
|
||||
intent = (
|
||||
sorted(
|
||||
recognizer_result.intents,
|
||||
key=recognizer_result.intents.get,
|
||||
reverse=True,
|
||||
)[:1][0]
|
||||
if recognizer_result.intents
|
||||
else None
|
||||
)
|
||||
|
||||
if intent == Intent.BOOK_FLIGHT.value:
|
||||
result = BookingDetails()
|
||||
|
||||
# We need to get the result from the LUIS JSON which at every level returns an array.
|
||||
to_entities = recognizer_result.entities.get("$instance", {}).get(
|
||||
"To", []
|
||||
)
|
||||
if len(to_entities) > 0:
|
||||
if recognizer_result.entities.get("To", [{"$instance": {}}])[0][
|
||||
"$instance"
|
||||
]:
|
||||
result.destination = to_entities[0]["text"].capitalize()
|
||||
else:
|
||||
result.unsupported_airports.append(
|
||||
to_entities[0]["text"].capitalize()
|
||||
)
|
||||
|
||||
from_entities = recognizer_result.entities.get("$instance", {}).get(
|
||||
"From", []
|
||||
)
|
||||
if len(from_entities) > 0:
|
||||
if recognizer_result.entities.get("From", [{"$instance": {}}])[0][
|
||||
"$instance"
|
||||
]:
|
||||
result.origin = from_entities[0]["text"].capitalize()
|
||||
else:
|
||||
result.unsupported_airports.append(
|
||||
from_entities[0]["text"].capitalize()
|
||||
)
|
||||
|
||||
# This value will be a TIMEX. And we are only interested in a Date so grab the first result and drop
|
||||
# the Time part. TIMEX is a format that represents DateTime expressions that include some ambiguity.
|
||||
# e.g. missing a Year.
|
||||
date_entities = recognizer_result.entities.get("datetime", [])
|
||||
if date_entities:
|
||||
timex = date_entities[0]["timex"]
|
||||
|
||||
if timex:
|
||||
datetime = timex[0].split("T")[0]
|
||||
|
||||
result.travel_date = datetime
|
||||
|
||||
else:
|
||||
result.travel_date = None
|
||||
|
||||
except Exception as exception:
|
||||
print(exception)
|
||||
|
||||
return intent, result
|
|
@ -1,5 +0,0 @@
|
|||
quart>=0.10.0
|
||||
botbuilder-dialogs>=4.14.0
|
||||
botbuilder-ai>=4.14.0
|
||||
datatypes-date-time>=1.0.0.a2
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
# EchoBot
|
||||
|
||||
Bot Framework v4 echo bot sample.
|
||||
|
||||
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.
|
||||
|
||||
## Running the sample
|
||||
- Clone the repository
|
||||
```bash
|
||||
git clone https://github.com/Microsoft/botbuilder-python.git
|
||||
```
|
||||
- Activate your desired virtual environment
|
||||
- Bring up a terminal, navigate to `botbuilder-python\samples\02.echo-bot` 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
|
||||
- Paste this URL in the emulator window - http://localhost:3978/api/messages
|
||||
|
||||
## Further reading
|
||||
|
||||
- [Bot Framework Documentation](https://docs.botframework.com)
|
||||
- [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0)
|
||||
- [Activity processing](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0)
|
|
@ -1,92 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
import tornado.ioloop
|
||||
import tornado.web
|
||||
import tornado.escape
|
||||
from tornado.options import define, parse_command_line, options
|
||||
from botbuilder.core import BotFrameworkAdapterSettings, TurnContext, BotFrameworkAdapter
|
||||
from botbuilder.schema import Activity, ActivityTypes
|
||||
|
||||
|
||||
from bots import EchoBot
|
||||
|
||||
define("port", default=3978, help="Application port")
|
||||
define("app_id", default=os.environ.get("MicrosoftAppId", ""), help="Application id from Azure")
|
||||
define(
|
||||
"app_password", default=os.environ.get("MicrosoftAppPassword", ""),
|
||||
help="Application password from Azure"
|
||||
)
|
||||
define("debug", default=True, help="run in debug mode")
|
||||
|
||||
class MessageHandler(tornado.web.RequestHandler):
|
||||
def initialize(self, adapter: BotFrameworkAdapter, bot: EchoBot):
|
||||
self.adapter = adapter
|
||||
self.bot = bot
|
||||
|
||||
async def post(self):
|
||||
# Main bot message handler.
|
||||
if "application/json" in self.request.headers["Content-Type"]:
|
||||
body = tornado.escape.json_decode(self.request.body)
|
||||
else:
|
||||
self.set_status(415)
|
||||
return
|
||||
|
||||
activity = Activity().from_dict(body)
|
||||
auth_header = (
|
||||
self.request.headers["Authorization"] if "Authorization" in self.request.headers else ""
|
||||
)
|
||||
|
||||
try:
|
||||
await self.adapter.process_activity(activity, auth_header, self.bot.on_turn)
|
||||
self.set_status(201)
|
||||
return
|
||||
except Exception as exception:
|
||||
raise exception
|
||||
|
||||
parse_command_line()
|
||||
|
||||
# Create adapter.
|
||||
# See https://aka.ms/about-bot-adapter to learn more about how bots work.
|
||||
SETTINGS = BotFrameworkAdapterSettings(options.app_id, options.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
|
||||
BOT = EchoBot()
|
||||
|
||||
APP = tornado.web.Application(
|
||||
[
|
||||
(r"/api/messages", MessageHandler, dict(adapter=ADAPTER, bot=BOT)),
|
||||
],
|
||||
debug=options.debug,
|
||||
)
|
||||
APP.listen(options.port)
|
||||
tornado.ioloop.IOLoop.current().start()
|
|
@ -1,6 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from .echo_bot import EchoBot
|
||||
|
||||
__all__ = ["EchoBot"]
|
|
@ -1,17 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from botbuilder.core import ActivityHandler, MessageFactory, TurnContext
|
||||
from botbuilder.schema import ChannelAccount
|
||||
|
||||
|
||||
class EchoBot(ActivityHandler):
|
||||
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):
|
||||
return await turn_context.send_activity(MessageFactory.text(f"Echo: {turn_context.activity.text}"))
|
|
@ -1,265 +0,0 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"groupLocation": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Specifies the location of the Resource Group."
|
||||
}
|
||||
},
|
||||
"groupName": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Specifies the name of the Resource Group."
|
||||
}
|
||||
},
|
||||
"appId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"appSecret": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"botId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable."
|
||||
}
|
||||
},
|
||||
"botSku": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanName": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The name of the App Service Plan."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanSku": {
|
||||
"type": "object",
|
||||
"defaultValue": {
|
||||
"name": "S1",
|
||||
"tier": "Standard",
|
||||
"size": "S1",
|
||||
"family": "S",
|
||||
"capacity": 1
|
||||
},
|
||||
"metadata": {
|
||||
"description": "The SKU of the App Service Plan. Defaults to Standard values."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanLocation": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The location of the App Service Plan. Defaults to \"westus\"."
|
||||
}
|
||||
},
|
||||
"newWebAppName": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"."
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"appServicePlanName": "[parameters('newAppServicePlanName')]",
|
||||
"resourcesLocation": "[parameters('newAppServicePlanLocation')]",
|
||||
"webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]",
|
||||
"siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]",
|
||||
"botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]",
|
||||
"publishingUsername": "[concat('$', parameters('newWebAppName'))]",
|
||||
"resourceGroupId": "[concat(subscription().id, '/resourceGroups/', parameters('groupName'))]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"name": "[parameters('groupName')]",
|
||||
"type": "Microsoft.Resources/resourceGroups",
|
||||
"apiVersion": "2018-05-01",
|
||||
"location": "[parameters('groupLocation')]",
|
||||
"properties": {}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Resources/deployments",
|
||||
"apiVersion": "2018-05-01",
|
||||
"name": "storageDeployment",
|
||||
"resourceGroup": "[parameters('groupName')]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Resources/resourceGroups/', parameters('groupName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"mode": "Incremental",
|
||||
"template": {
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {},
|
||||
"variables": {},
|
||||
"resources": [
|
||||
{
|
||||
"comments": "Create a new Linux App Service Plan if no existing App Service Plan name was passed in.",
|
||||
"type": "Microsoft.Web/serverfarms",
|
||||
"name": "[variables('appServicePlanName')]",
|
||||
"apiVersion": "2018-02-01",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"sku": "[parameters('newAppServicePlanSku')]",
|
||||
"kind": "linux",
|
||||
"properties": {
|
||||
"name": "[variables('appServicePlanName')]",
|
||||
"perSiteScaling": false,
|
||||
"reserved": true,
|
||||
"targetWorkerCount": 0,
|
||||
"targetWorkerSizeId": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"comments": "Create a Web App using a Linux App Service Plan",
|
||||
"type": "Microsoft.Web/sites",
|
||||
"apiVersion": "2015-08-01",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"kind": "app,linux",
|
||||
"dependsOn": [
|
||||
"[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/serverfarms/', variables('appServicePlanName'))]"
|
||||
],
|
||||
"name": "[variables('webAppName')]",
|
||||
"properties": {
|
||||
"name": "[variables('webAppName')]",
|
||||
"hostNameSslStates": [
|
||||
{
|
||||
"name": "[concat(parameters('newWebAppName'), '.azurewebsites.net')]",
|
||||
"sslState": "Disabled",
|
||||
"hostType": "Standard"
|
||||
},
|
||||
{
|
||||
"name": "[concat(parameters('newWebAppName'), '.scm.azurewebsites.net')]",
|
||||
"sslState": "Disabled",
|
||||
"hostType": "Repository"
|
||||
}
|
||||
],
|
||||
"serverFarmId": "[variables('appServicePlanName')]",
|
||||
"siteConfig": {
|
||||
"appSettings": [
|
||||
{
|
||||
"name": "SCM_DO_BUILD_DURING_DEPLOYMENT",
|
||||
"value": "true"
|
||||
},
|
||||
{
|
||||
"name": "MicrosoftAppId",
|
||||
"value": "[parameters('appId')]"
|
||||
},
|
||||
{
|
||||
"name": "MicrosoftAppPassword",
|
||||
"value": "[parameters('appSecret')]"
|
||||
}
|
||||
],
|
||||
"cors": {
|
||||
"allowedOrigins": [
|
||||
"https://botservice.hosting.portal.azure.net",
|
||||
"https://hosting.onecloud.azure-test.net/"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Web/sites/config",
|
||||
"apiVersion": "2016-08-01",
|
||||
"name": "[concat(variables('webAppName'), '/web')]",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"dependsOn": [
|
||||
"[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/sites/', variables('webAppName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"numberOfWorkers": 1,
|
||||
"defaultDocuments": [
|
||||
"Default.htm",
|
||||
"Default.html",
|
||||
"Default.asp",
|
||||
"index.htm",
|
||||
"index.html",
|
||||
"iisstart.htm",
|
||||
"default.aspx",
|
||||
"index.php",
|
||||
"hostingstart.html"
|
||||
],
|
||||
"netFrameworkVersion": "v4.0",
|
||||
"phpVersion": "",
|
||||
"pythonVersion": "",
|
||||
"nodeVersion": "",
|
||||
"linuxFxVersion": "PYTHON|3.7",
|
||||
"requestTracingEnabled": false,
|
||||
"remoteDebuggingEnabled": false,
|
||||
"remoteDebuggingVersion": "VS2017",
|
||||
"httpLoggingEnabled": true,
|
||||
"logsDirectorySizeLimit": 35,
|
||||
"detailedErrorLoggingEnabled": false,
|
||||
"publishingUsername": "[variables('publishingUsername')]",
|
||||
"scmType": "None",
|
||||
"use32BitWorkerProcess": true,
|
||||
"webSocketsEnabled": false,
|
||||
"alwaysOn": false,
|
||||
"appCommandLine": "gunicorn --bind 0.0.0.0 --worker-class aiohttp.worker.GunicornWebWorker --timeout 600 app:APP",
|
||||
"managedPipelineMode": "Integrated",
|
||||
"virtualApplications": [
|
||||
{
|
||||
"virtualPath": "/",
|
||||
"physicalPath": "site\\wwwroot",
|
||||
"preloadEnabled": false,
|
||||
"virtualDirectories": null
|
||||
}
|
||||
],
|
||||
"winAuthAdminState": 0,
|
||||
"winAuthTenantState": 0,
|
||||
"customAppPoolIdentityAdminState": false,
|
||||
"customAppPoolIdentityTenantState": false,
|
||||
"loadBalancing": "LeastRequests",
|
||||
"routingRules": [],
|
||||
"experiments": {
|
||||
"rampUpRules": []
|
||||
},
|
||||
"autoHealEnabled": false,
|
||||
"vnetName": "",
|
||||
"minTlsVersion": "1.2",
|
||||
"ftpsState": "AllAllowed",
|
||||
"reservedInstanceCount": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2021-03-01",
|
||||
"type": "Microsoft.BotService/botServices",
|
||||
"name": "[parameters('botId')]",
|
||||
"location": "global",
|
||||
"kind": "azurebot",
|
||||
"sku": {
|
||||
"name": "[parameters('botSku')]"
|
||||
},
|
||||
"properties": {
|
||||
"name": "[parameters('botId')]",
|
||||
"displayName": "[parameters('botId')]",
|
||||
"iconUrl": "https://docs.botframework.com/static/devportal/client/images/bot-framework-default.png",
|
||||
"endpoint": "[variables('botEndpoint')]",
|
||||
"msaAppId": "[parameters('appId')]",
|
||||
"luisAppIds": [],
|
||||
"schemaTransformationVersion": "1.3",
|
||||
"isCmekEnabled": false,
|
||||
"isIsolated": false
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/sites/', variables('webAppName'))]"
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputs": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,243 +0,0 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"appId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"appSecret": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"botId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable."
|
||||
}
|
||||
},
|
||||
"botSku": {
|
||||
"defaultValue": "F0",
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanName": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "The name of the new App Service Plan."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanSku": {
|
||||
"type": "object",
|
||||
"defaultValue": {
|
||||
"name": "S1",
|
||||
"tier": "Standard",
|
||||
"size": "S1",
|
||||
"family": "S",
|
||||
"capacity": 1
|
||||
},
|
||||
"metadata": {
|
||||
"description": "The SKU of the App Service Plan. Defaults to Standard values."
|
||||
}
|
||||
},
|
||||
"appServicePlanLocation": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The location of the App Service Plan."
|
||||
}
|
||||
},
|
||||
"existingAppServicePlan": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "Name of the existing App Service Plan used to create the Web App for the bot."
|
||||
}
|
||||
},
|
||||
"newWebAppName": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"."
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"defaultAppServicePlanName": "[if(empty(parameters('existingAppServicePlan')), 'createNewAppServicePlan', parameters('existingAppServicePlan'))]",
|
||||
"useExistingAppServicePlan": "[not(equals(variables('defaultAppServicePlanName'), 'createNewAppServicePlan'))]",
|
||||
"servicePlanName": "[if(variables('useExistingAppServicePlan'), parameters('existingAppServicePlan'), parameters('newAppServicePlanName'))]",
|
||||
"publishingUsername": "[concat('$', parameters('newWebAppName'))]",
|
||||
"resourcesLocation": "[parameters('appServicePlanLocation')]",
|
||||
"webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]",
|
||||
"siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]",
|
||||
"botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"comments": "Create a new Linux App Service Plan if no existing App Service Plan name was passed in.",
|
||||
"type": "Microsoft.Web/serverfarms",
|
||||
"apiVersion": "2016-09-01",
|
||||
"name": "[variables('servicePlanName')]",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"sku": "[parameters('newAppServicePlanSku')]",
|
||||
"kind": "linux",
|
||||
"properties": {
|
||||
"name": "[variables('servicePlanName')]",
|
||||
"perSiteScaling": false,
|
||||
"reserved": true,
|
||||
"targetWorkerCount": 0,
|
||||
"targetWorkerSizeId": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"comments": "Create a Web App using a Linux App Service Plan",
|
||||
"type": "Microsoft.Web/sites",
|
||||
"apiVersion": "2016-08-01",
|
||||
"name": "[variables('webAppName')]",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]"
|
||||
],
|
||||
"kind": "app,linux",
|
||||
"properties": {
|
||||
"enabled": true,
|
||||
"hostNameSslStates": [
|
||||
{
|
||||
"name": "[concat(parameters('newWebAppName'), '.azurewebsites.net')]",
|
||||
"sslState": "Disabled",
|
||||
"hostType": "Standard"
|
||||
},
|
||||
{
|
||||
"name": "[concat(parameters('newWebAppName'), '.scm.azurewebsites.net')]",
|
||||
"sslState": "Disabled",
|
||||
"hostType": "Repository"
|
||||
}
|
||||
],
|
||||
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]",
|
||||
"reserved": true,
|
||||
"scmSiteAlsoStopped": false,
|
||||
"clientAffinityEnabled": false,
|
||||
"clientCertEnabled": false,
|
||||
"hostNamesDisabled": false,
|
||||
"containerSize": 0,
|
||||
"dailyMemoryTimeQuota": 0,
|
||||
"httpsOnly": false,
|
||||
"siteConfig": {
|
||||
"appSettings": [
|
||||
{
|
||||
"name": "MicrosoftAppId",
|
||||
"value": "[parameters('appId')]"
|
||||
},
|
||||
{
|
||||
"name": "MicrosoftAppPassword",
|
||||
"value": "[parameters('appSecret')]"
|
||||
},
|
||||
{
|
||||
"name": "SCM_DO_BUILD_DURING_DEPLOYMENT",
|
||||
"value": "true"
|
||||
}
|
||||
],
|
||||
"cors": {
|
||||
"allowedOrigins": [
|
||||
"https://botservice.hosting.portal.azure.net",
|
||||
"https://hosting.onecloud.azure-test.net/"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Web/sites/config",
|
||||
"apiVersion": "2016-08-01",
|
||||
"name": "[concat(variables('webAppName'), '/web')]",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Web/sites', variables('webAppName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"numberOfWorkers": 1,
|
||||
"defaultDocuments": [
|
||||
"Default.htm",
|
||||
"Default.html",
|
||||
"Default.asp",
|
||||
"index.htm",
|
||||
"index.html",
|
||||
"iisstart.htm",
|
||||
"default.aspx",
|
||||
"index.php",
|
||||
"hostingstart.html"
|
||||
],
|
||||
"netFrameworkVersion": "v4.0",
|
||||
"phpVersion": "",
|
||||
"pythonVersion": "",
|
||||
"nodeVersion": "",
|
||||
"linuxFxVersion": "PYTHON|3.7",
|
||||
"requestTracingEnabled": false,
|
||||
"remoteDebuggingEnabled": false,
|
||||
"remoteDebuggingVersion": "VS2017",
|
||||
"httpLoggingEnabled": true,
|
||||
"logsDirectorySizeLimit": 35,
|
||||
"detailedErrorLoggingEnabled": false,
|
||||
"publishingUsername": "[variables('publishingUsername')]",
|
||||
"scmType": "None",
|
||||
"use32BitWorkerProcess": true,
|
||||
"webSocketsEnabled": false,
|
||||
"alwaysOn": false,
|
||||
"appCommandLine": "gunicorn --bind 0.0.0.0 --worker-class aiohttp.worker.GunicornWebWorker --timeout 600 app:APP",
|
||||
"managedPipelineMode": "Integrated",
|
||||
"virtualApplications": [
|
||||
{
|
||||
"virtualPath": "/",
|
||||
"physicalPath": "site\\wwwroot",
|
||||
"preloadEnabled": false,
|
||||
"virtualDirectories": null
|
||||
}
|
||||
],
|
||||
"winAuthAdminState": 0,
|
||||
"winAuthTenantState": 0,
|
||||
"customAppPoolIdentityAdminState": false,
|
||||
"customAppPoolIdentityTenantState": false,
|
||||
"loadBalancing": "LeastRequests",
|
||||
"routingRules": [],
|
||||
"experiments": {
|
||||
"rampUpRules": []
|
||||
},
|
||||
"autoHealEnabled": false,
|
||||
"vnetName": "",
|
||||
"minTlsVersion": "1.2",
|
||||
"ftpsState": "AllAllowed",
|
||||
"reservedInstanceCount": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2021-03-01",
|
||||
"type": "Microsoft.BotService/botServices",
|
||||
"name": "[parameters('botId')]",
|
||||
"location": "global",
|
||||
"kind": "azurebot",
|
||||
"sku": {
|
||||
"name": "[parameters('botSku')]"
|
||||
},
|
||||
"properties": {
|
||||
"name": "[parameters('botId')]",
|
||||
"displayName": "[parameters('botId')]",
|
||||
"iconUrl": "https://docs.botframework.com/static/devportal/client/images/bot-framework-default.png",
|
||||
"endpoint": "[variables('botEndpoint')]",
|
||||
"msaAppId": "[parameters('appId')]",
|
||||
"luisAppIds": [],
|
||||
"schemaTransformationVersion": "1.3",
|
||||
"isCmekEnabled": false,
|
||||
"isIsolated": false
|
||||
},
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Web/sites/', variables('webAppName'))]"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
botbuilder-core>=4.4.0b1
|
||||
flask>=1.0.3
|
|
@ -1,216 +0,0 @@
|
|||
# Setting up LUIS via CLI:
|
||||
|
||||
This README contains information on how to create and deploy a LUIS application. When the bot is ready to be deployed to production, we recommend creating a LUIS Endpoint Resource for usage with your LUIS App.
|
||||
|
||||
> _For instructions on how to create a LUIS Application via the LUIS portal, see these Quickstart steps:_
|
||||
> 1. _[Quickstart: Create a new app in the LUIS portal][Quickstart-create]_
|
||||
> 2. _[Quickstart: Deploy an app in the LUIS portal][Quickstart-deploy]_
|
||||
|
||||
[Quickstart-create]: https://docs.microsoft.com/azure/cognitive-services/luis/get-started-portal-build-app
|
||||
[Quickstart-deploy]:https://docs.microsoft.com/azure/cognitive-services/luis/get-started-portal-deploy-app
|
||||
|
||||
## Table of Contents:
|
||||
|
||||
- [Prerequisites](#Prerequisites)
|
||||
- [Import a new LUIS Application using a local LUIS application](#Import-a-new-LUIS-Application-using-a-local-LUIS-application)
|
||||
- [How to create a LUIS Endpoint resource in Azure and pair it with a LUIS Application](#How-to-create-a-LUIS-Endpoint-resource-in-Azure-and-pair-it-with-a-LUIS-Application)
|
||||
|
||||
___
|
||||
|
||||
## [Prerequisites](#Table-of-Contents):
|
||||
|
||||
#### Install Azure CLI >=2.0.61:
|
||||
|
||||
Visit the following page to find the correct installer for your OS:
|
||||
- https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest
|
||||
|
||||
#### Install LUIS CLI >=2.4.0:
|
||||
|
||||
Open a CLI of your choice and type the following:
|
||||
|
||||
```bash
|
||||
npm i -g luis-apis@^2.4.0
|
||||
```
|
||||
|
||||
#### LUIS portal account:
|
||||
|
||||
You should already have a LUIS account with either https://luis.ai, https://eu.luis.ai, or https://au.luis.ai. To determine where to create a LUIS account, consider where you will deploy your LUIS applications, and then place them in [the corresponding region][LUIS-Authoring-Regions].
|
||||
|
||||
After you've created your account, you need your [Authoring Key][LUIS-AKey] and a LUIS application ID.
|
||||
|
||||
[LUIS-Authoring-Regions]: https://docs.microsoft.com/azure/cognitive-services/luis/luis-reference-regions#luis-authoring-regions]
|
||||
[LUIS-AKey]: https://docs.microsoft.com/azure/cognitive-services/luis/luis-concept-keys#authoring-key
|
||||
|
||||
___
|
||||
|
||||
## [Import a new LUIS Application using a local LUIS application](#Table-of-Contents)
|
||||
|
||||
### 1. Import the local LUIS application to luis.ai
|
||||
|
||||
```bash
|
||||
luis import application --region "LuisAppAuthoringRegion" --authoringKey "LuisAuthoringKey" --appName "FlightBooking" --in "./cognitiveModels/FlightBooking.json"
|
||||
```
|
||||
|
||||
Outputs the following JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "########-####-####-####-############",
|
||||
"name": "FlightBooking",
|
||||
"description": "A LUIS model that uses intent and entities.",
|
||||
"culture": "en-us",
|
||||
"usageScenario": "",
|
||||
"domain": "",
|
||||
"versionsCount": 1,
|
||||
"createdDateTime": "2019-03-29T18:32:02Z",
|
||||
"endpoints": {},
|
||||
"endpointHitsCount": 0,
|
||||
"activeVersion": "0.1",
|
||||
"ownerEmail": "bot@contoso.com",
|
||||
"tokenizerVersion": "1.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
For the next step, you'll need the `"id"` value for `--appId` and the `"activeVersion"` value for `--versionId`.
|
||||
|
||||
### 2. Train the LUIS Application
|
||||
|
||||
```bash
|
||||
luis train version --region "LuisAppAuthoringRegion" --authoringKey "LuisAuthoringKey" --appId "LuisAppId" --versionId "LuisAppversion" --wait
|
||||
```
|
||||
|
||||
### 3. Publish the LUIS Application
|
||||
|
||||
```bash
|
||||
luis publish version --region "LuisAppAuthoringRegion" --authoringKey "LuisAuthoringKey" --appId "LuisAppId" --versionId "LuisAppversion" --publishRegion "LuisAppPublishRegion"
|
||||
```
|
||||
|
||||
> `--region` corresponds to the region you _author_ your application in. The regions available for this are "westus", "westeurope" and "australiaeast". <br/>
|
||||
> These regions correspond to the three available portals, https://luis.ai, https://eu.luis.ai, or https://au.luis.ai. <br/>
|
||||
> `--publishRegion` corresponds to the region of the endpoint you're publishing to, (e.g. "westus", "southeastasia", "westeurope", "brazilsouth"). <br/>
|
||||
> See the [reference docs][Endpoint-API] for a list of available publish/endpoint regions.
|
||||
|
||||
[Endpoint-API]: https://westus.dev.cognitive.microsoft.com/docs/services/5819c76f40a6350ce09de1ac/operations/5819c77140a63516d81aee78
|
||||
|
||||
Outputs the following:
|
||||
|
||||
```json
|
||||
{
|
||||
"versionId": "0.1",
|
||||
"isStaging": false,
|
||||
"endpointUrl": "https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/########-####-####-####-############",
|
||||
"region": "westus",
|
||||
"assignedEndpointKey": null,
|
||||
"endpointRegion": "westus",
|
||||
"failedRegions": "",
|
||||
"publishedDateTime": "2019-03-29T18:40:32Z",
|
||||
"directVersionPublish": false
|
||||
}
|
||||
```
|
||||
|
||||
To see how to create an LUIS Cognitive Service Resource in Azure, please see [the next README][README-LUIS]. This Resource should be used when you want to move your bot to production. The instructions will show you how to create and pair the resource with a LUIS Application.
|
||||
|
||||
[README-LUIS]: ./README-LUIS.md
|
||||
|
||||
___
|
||||
|
||||
## [How to create a LUIS Endpoint resource in Azure and pair it with a LUIS Application](#Table-of-Contents)
|
||||
|
||||
### 1. Create a new LUIS Cognitive Services resource on Azure via Azure CLI
|
||||
|
||||
> _Note:_ <br/>
|
||||
> _If you don't have a Resource Group in your Azure subscription, you can create one through the Azure portal or through using:_
|
||||
> ```bash
|
||||
> az group create --subscription "AzureSubscriptionGuid" --location "westus" --name "ResourceGroupName"
|
||||
> ```
|
||||
> _To see a list of valid locations, use `az account list-locations`_
|
||||
|
||||
|
||||
```bash
|
||||
# Use Azure CLI to create the LUIS Key resource on Azure
|
||||
az cognitiveservices account create --kind "luis" --name "NewLuisResourceName" --sku "S0" --location "westus" --subscription "AzureSubscriptionGuid" -g "ResourceGroupName"
|
||||
```
|
||||
|
||||
The command will output a response similar to the JSON below:
|
||||
|
||||
```json
|
||||
{
|
||||
"endpoint": "https://westus.api.cognitive.microsoft.com/luis/v2.0",
|
||||
"etag": "\"########-####-####-####-############\"",
|
||||
"id": "/subscriptions/########-####-####-####-############/resourceGroups/ResourceGroupName/providers/Microsoft.CognitiveServices/accounts/NewLuisResourceName",
|
||||
"internalId": "################################",
|
||||
"kind": "luis",
|
||||
"location": "westus",
|
||||
"name": "NewLuisResourceName",
|
||||
"provisioningState": "Succeeded",
|
||||
"resourceGroup": "ResourceGroupName",
|
||||
"sku": {
|
||||
"name": "S0",
|
||||
"tier": null
|
||||
},
|
||||
"tags": null,
|
||||
"type": "Microsoft.CognitiveServices/accounts"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
Take the output from the previous command and create a JSON file in the following format:
|
||||
|
||||
```json
|
||||
{
|
||||
"azureSubscriptionId": "00000000-0000-0000-0000-000000000000",
|
||||
"resourceGroup": "ResourceGroupName",
|
||||
"accountName": "NewLuisResourceName"
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Retrieve ARM access token via Azure CLI
|
||||
|
||||
```bash
|
||||
az account get-access-token --subscription "AzureSubscriptionGuid"
|
||||
```
|
||||
|
||||
This will return an object that looks like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"accessToken": "eyJ0eXAiOiJKVtokentokentokentokentokeng1dCI6Ik4tbEMwbi05REFMcXdodUhZbkhRNjNHZUNYYyIsItokenI6Ik4tbEMwbi05REFMcXdodUhZbkhRNjNHZUNYYyJ9.eyJhdWQiOiJodHRwczovL21hbmFnZW1lbnQuY29yZS53aW5kb3dzLm5ldC8iLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC83MmY5ODhiZi04NmYxLTQxYWYtOTFhYi0yZDdjZDAxMWRiNDcvIiwiaWF0IjoxNTUzODc3MTUwLCJuYmYiOjE1NTM4NzcxNTAsImV4cCI6MTU1Mzg4MTA1MCwiX2NsYWltX25hbWVzIjp7Imdyb3VwcyI6InNyYzEifSwiX2NsYWltX3NvdXJjZXMiOnsic3JjMSI6eyJlbmRwb2ludCI6Imh0dHBzOi8vZ3JhcGgud2luZG93cy5uZXQvNzJmOTg4YmYtODZmMS00MWFmLTkxYWItMmQ3Y2QwMTFkYjQ3L3VzZXJzL2ZmZTQyM2RkLWJhM2YtNDg0Ny04NjgyLWExNTI5MDA4MjM4Ny9nZXRNZW1iZXJPYmplY3RzIn19LCJhY3IiOiIxIiwiYWlvIjoiQVZRQXEvOEtBQUFBeGVUc201NDlhVHg4RE1mMFlRVnhGZmxxOE9RSC9PODR3QktuSmRqV1FqTkkwbmxLYzB0bHJEZzMyMFZ5bWZGaVVBSFBvNUFFUTNHL0FZNDRjdk01T3M0SEt0OVJkcE5JZW9WU0dzd0kvSkk9IiwiYW1yIjpbIndpYSIsIm1mYSJdLCJhcHBpZCI6IjA0YjA3Nzk1LThkZGItNDYxYS1iYmVlLTAyZjllMWJmN2I0NiIsImFwcGlkYWNyIjoiMCIsImRldmljZWlkIjoiNDhmNDVjNjEtMTg3Zi00MjUxLTlmZWItMTllZGFkZmMwMmE3IiwiZmFtaWx5X25hbWUiOiJHdW0iLCJnaXZlbl9uYW1lIjoiU3RldmVuIiwiaXBhZGRyIjoiMTY3LjIyMC4yLjU1IiwibmFtZSI6IlN0ZXZlbiBHdW0iLCJvaWQiOiJmZmU0MjNkZC1iYTNmLTQ4NDctODY4Mi1hMTUyOTAwODIzODciLCJvbnByZW1fc2lkIjoiUy0xLTUtMjEtMjEyNzUyMTE4NC0xNjA0MDEyOTIwLTE4ODc5Mjc1MjctMjYwOTgyODUiLCJwdWlkIjoiMTAwMzdGRkVBMDQ4NjlBNyIsInJoIjoiSSIsInNjcCI6InVzZXJfaW1wZXJzb25hdGlvbiIsInN1YiI6Ik1rMGRNMWszN0U5ckJyMjhieUhZYjZLSU85LXVFQVVkZFVhNWpkSUd1Nk0iLCJ0aWQiOiI3MmY5ODhiZi04NmYxLTQxYWYtOTFhYi0yZDdjZDAxMWRiNDciLCJ1bmlxdWVfbmFtZSI6InN0Z3VtQG1pY3Jvc29mdC5jb20iLCJ1cG4iOiJzdGd1bUBtaWNyb3NvZnQuY29tIiwidXRpIjoiT2w2NGN0TXY4RVNEQzZZQWRqRUFtokenInZlciI6IjEuMCJ9.kFAsEilE0mlS1pcpqxf4rEnRKeYsehyk-gz-zJHUrE__oad3QjgDSBDPrR_ikLdweynxbj86pgG4QFaHURNCeE6SzrbaIrNKw-n9jrEtokenlosOxg_0l2g1LeEUOi5Q4gQREAU_zvSbl-RY6sAadpOgNHtGvz3Rc6FZRITfkckSLmsKAOFoh-aWC6tFKG8P52rtB0qVVRz9tovBeNqkMYL49s9ypduygbXNVwSQhm5JszeWDgrFuVFHBUP_iENCQYGQpEZf_KvjmX1Ur1F9Eh9nb4yI2gFlKncKNsQl-tokenK7-tokentokentokentokentokentokenatoken",
|
||||
"expiresOn": "2200-12-31 23:59:59.999999",
|
||||
"subscription": "AzureSubscriptionGuid",
|
||||
"tenant": "tenant-guid",
|
||||
"tokenType": "Bearer"
|
||||
}
|
||||
```
|
||||
|
||||
The value needed for the next step is the `"accessToken"`.
|
||||
|
||||
### 3. Use `luis add appazureaccount` to pair your LUIS resource with a LUIS Application
|
||||
|
||||
```bash
|
||||
luis add appazureaccount --in "path/to/created/requestBody.json" --appId "LuisAppId" --authoringKey "LuisAuthoringKey" --armToken "accessToken"
|
||||
```
|
||||
|
||||
If successful, it should yield a response like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "Success",
|
||||
"message": "Operation Successful"
|
||||
}
|
||||
```
|
||||
|
||||
### 4. See the LUIS Cognitive Services' keys
|
||||
|
||||
```bash
|
||||
az cognitiveservices account keys list --name "NewLuisResourceName" --subscription "AzureSubscriptionGuid" -g "ResourceGroupName"
|
||||
```
|
||||
|
||||
This will return an object that looks like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"key1": "9a69####dc8f####8eb4####399f####",
|
||||
"key2": "####f99e####4b1a####fb3b####6b9f"
|
||||
}
|
||||
```
|
|
@ -1,64 +0,0 @@
|
|||
# CoreBot
|
||||
|
||||
Bot Framework v4 core bot sample.
|
||||
|
||||
This bot has been created using [Bot Framework](https://dev.botframework.com), it shows how to:
|
||||
|
||||
- Use [LUIS](https://www.luis.ai) to implement core AI capabilities
|
||||
- Implement a multi-turn conversation using Dialogs
|
||||
- Handle user interruptions for such things as `Help` or `Cancel`
|
||||
- Prompt for and validate requests for information from the user
|
||||
|
||||
## Prerequisites
|
||||
|
||||
This sample **requires** prerequisites in order to run.
|
||||
|
||||
### Overview
|
||||
|
||||
This bot uses [LUIS](https://www.luis.ai), an AI based cognitive service, to implement language understanding.
|
||||
|
||||
### Install Python 3.7
|
||||
|
||||
|
||||
### Create a LUIS Application to enable language understanding
|
||||
|
||||
LUIS language model setup, training, and application configuration steps can be found [here](https://docs.microsoft.com/azure/bot-service/bot-builder-howto-v4-luis?view=azure-bot-service-4.0&tabs=cs).
|
||||
|
||||
If you wish to create a LUIS application via the CLI, these steps can be found in the [README-LUIS.md](README-LUIS.md).
|
||||
|
||||
## Running the sample
|
||||
- Bring up a terminal, navigate to `botbuilder-samples\samples\python\13.core-bot` folder
|
||||
- Activate your desired virtual environment
|
||||
- Run `pip install -r requirements.txt` to install all dependencies
|
||||
- Update LuisAppId, LuisAPIKey and LuisAPIHostName in `config.py` with the information retrieved from the [LUIS portal](https://www.luis.ai)
|
||||
- Run your bot with `python app.py`
|
||||
|
||||
|
||||
## Testing the bot using Bot Framework Emulator
|
||||
|
||||
[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.3.0 or greater from [here](https://github.com/Microsoft/BotFramework-Emulator/releases)
|
||||
|
||||
### Connect to the bot using Bot Framework Emulator
|
||||
|
||||
- Launch Bot Framework Emulator
|
||||
- Enter a Bot URL of `http://localhost:3978/api/messages`
|
||||
|
||||
## Deploy the bot to Azure
|
||||
|
||||
To learn more about deploying a bot to Azure, see [Deploy your bot to Azure](https://aka.ms/azuredeployment) for a complete list of deployment instructions.
|
||||
|
||||
# Further reading
|
||||
|
||||
- [Bot Framework Documentation](https://docs.botframework.com)
|
||||
- [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0)
|
||||
- [Dialogs](https://docs.microsoft.com/azure/bot-service/bot-builder-concept-dialog?view=azure-bot-service-4.0)
|
||||
- [Gathering Input Using Prompts](https://docs.microsoft.com/azure/bot-service/bot-builder-prompts?view=azure-bot-service-4.0&tabs=csharp)
|
||||
- [Activity processing](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0)
|
||||
- [Azure Bot Service Introduction](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0)
|
||||
- [Azure Bot Service Documentation](https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0)
|
||||
- [Azure CLI](https://docs.microsoft.com/cli/azure/?view=azure-cli-latest)
|
||||
- [Azure Portal](https://portal.azure.com)
|
||||
- [Language Understanding using LUIS](https://docs.microsoft.com/azure/cognitive-services/luis/)
|
||||
- [Channels and Bot Connector Service](https://docs.microsoft.com/azure/bot-service/bot-concepts?view=azure-bot-service-4.0)
|
|
@ -1,56 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
import sys
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
|
||||
from botbuilder.core import (
|
||||
BotFrameworkAdapter,
|
||||
BotFrameworkAdapterSettings,
|
||||
ConversationState,
|
||||
TurnContext,
|
||||
)
|
||||
from botbuilder.schema import ActivityTypes, Activity
|
||||
|
||||
|
||||
class AdapterWithErrorHandler(BotFrameworkAdapter):
|
||||
def __init__(
|
||||
self,
|
||||
settings: BotFrameworkAdapterSettings,
|
||||
conversation_state: ConversationState,
|
||||
):
|
||||
super().__init__(settings)
|
||||
self._conversation_state = conversation_state
|
||||
|
||||
# Catch-all for errors.
|
||||
async def on_error(context: TurnContext, error: Exception):
|
||||
# This check writes out errors to console log
|
||||
# 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)
|
||||
traceback.print_exc()
|
||||
|
||||
# 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
|
||||
nonlocal self
|
||||
await self._conversation_state.delete(context)
|
||||
|
||||
self.on_turn_error = on_error
|
|
@ -1,107 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
"""
|
||||
This sample shows how to create a bot that demonstrates the following:
|
||||
- Use [LUIS](https://www.luis.ai) to implement core AI capabilities.
|
||||
- Implement a multi-turn conversation using Dialogs.
|
||||
- Handle user interruptions for such things as `Help` or `Cancel`.
|
||||
- Prompt for and validate requests for information from the user.
|
||||
"""
|
||||
import os
|
||||
|
||||
import tornado.ioloop
|
||||
import tornado.web
|
||||
import tornado.escape
|
||||
from tornado.options import define, parse_command_line, options
|
||||
|
||||
from botbuilder.core import (
|
||||
BotFrameworkAdapterSettings,
|
||||
ConversationState,
|
||||
MemoryStorage,
|
||||
UserState,
|
||||
)
|
||||
from botbuilder.schema import Activity
|
||||
|
||||
from dialogs import MainDialog, BookingDialog
|
||||
from bots import DialogAndWelcomeBot
|
||||
|
||||
from adapter_with_error_handler import AdapterWithErrorHandler
|
||||
from flight_booking_recognizer import FlightBookingRecognizer
|
||||
|
||||
define("port", default=3978, help="Application port")
|
||||
define("app_id", default=os.environ.get("MicrosoftAppId", ""), help="Application id from Azure")
|
||||
define(
|
||||
"app_password",
|
||||
default=os.environ.get("MicrosoftAppPassword", ""),
|
||||
help="Application password from Azure"
|
||||
)
|
||||
define("luis_app_id", default=os.environ.get("LuisAppId", ""), help="Application id for Luis")
|
||||
define("luis_api_key", default=os.environ.get("LuisApiKey", ""), help="Api key for Luis")
|
||||
define(
|
||||
"luis_api_host_name",
|
||||
default=os.environ.get("LuisAppId", ""),
|
||||
help="LUIS endpoint host name, ie 'westus.api.cognitive.microsoft.com')"
|
||||
)
|
||||
define("debug", default=True, help="run in debug mode")
|
||||
|
||||
class MessageHandler(tornado.web.RequestHandler):
|
||||
def initialize(self, adapter: AdapterWithErrorHandler, bot: DialogAndWelcomeBot):
|
||||
self.adapter = adapter
|
||||
self.bot = bot
|
||||
|
||||
async def post(self):
|
||||
# Main bot message handler.
|
||||
if "application/json" in self.request.headers["Content-Type"]:
|
||||
body = tornado.escape.json_decode(self.request.body)
|
||||
else:
|
||||
self.set_status(415)
|
||||
return
|
||||
|
||||
activity = Activity().from_dict(body)
|
||||
auth_header = (
|
||||
self.request.headers["Authorization"] if "Authorization" in self.request.headers else ""
|
||||
)
|
||||
|
||||
try:
|
||||
await self.adapter.process_activity(activity, auth_header, self.bot.on_turn)
|
||||
self.set_status(201)
|
||||
return
|
||||
except Exception as exception:
|
||||
raise exception
|
||||
|
||||
|
||||
def main():
|
||||
parse_command_line()
|
||||
|
||||
# Create adapter.
|
||||
# See https://aka.ms/about-bot-adapter to learn more about how bots work.
|
||||
settings = BotFrameworkAdapterSettings(options.app_id, options.app_password)
|
||||
|
||||
# Create MemoryStorage, UserState and ConversationState
|
||||
memory = MemoryStorage()
|
||||
user_state = UserState(memory)
|
||||
conversation_state = ConversationState(memory)
|
||||
|
||||
# Create adapter.
|
||||
# See https://aka.ms/about-bot-adapter to learn more about how bots work.
|
||||
adapter = AdapterWithErrorHandler(settings, conversation_state)
|
||||
|
||||
# Create dialogs and Bot
|
||||
recognizer = FlightBookingRecognizer(options)
|
||||
booking_dialog = BookingDialog()
|
||||
dialog = MainDialog(recognizer, booking_dialog)
|
||||
bot = DialogAndWelcomeBot(conversation_state, user_state, dialog)
|
||||
|
||||
|
||||
app = tornado.web.Application(
|
||||
[
|
||||
(r"/api/messages", MessageHandler, dict(adapter=adapter, bot=bot)),
|
||||
],
|
||||
debug=options.debug,
|
||||
)
|
||||
app.listen(options.port)
|
||||
tornado.ioloop.IOLoop.current().start()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,18 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
|
||||
class BookingDetails:
|
||||
def __init__(
|
||||
self,
|
||||
destination: str = None,
|
||||
origin: str = None,
|
||||
travel_date: str = None,
|
||||
unsupported_airports=None,
|
||||
):
|
||||
if unsupported_airports is None:
|
||||
unsupported_airports = []
|
||||
self.destination = destination
|
||||
self.origin = origin
|
||||
self.travel_date = travel_date
|
||||
self.unsupported_airports = unsupported_airports
|
|
@ -1,7 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from .dialog_bot import DialogBot
|
||||
from .dialog_and_welcome_bot import DialogAndWelcomeBot
|
||||
|
||||
__all__ = ["DialogBot", "DialogAndWelcomeBot"]
|
|
@ -1,57 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import json
|
||||
import os.path
|
||||
|
||||
from typing import List
|
||||
from botbuilder.core import (
|
||||
ConversationState,
|
||||
MessageFactory,
|
||||
UserState,
|
||||
TurnContext,
|
||||
)
|
||||
from botbuilder.dialogs import Dialog
|
||||
from botbuilder.schema import Attachment, ChannelAccount
|
||||
from helpers.dialog_helper import DialogHelper
|
||||
|
||||
from .dialog_bot import DialogBot
|
||||
|
||||
|
||||
class DialogAndWelcomeBot(DialogBot):
|
||||
def __init__(
|
||||
self,
|
||||
conversation_state: ConversationState,
|
||||
user_state: UserState,
|
||||
dialog: Dialog,
|
||||
):
|
||||
super(DialogAndWelcomeBot, self).__init__(
|
||||
conversation_state, user_state, dialog
|
||||
)
|
||||
|
||||
async def on_members_added_activity(
|
||||
self, members_added: List[ChannelAccount], turn_context: TurnContext
|
||||
):
|
||||
for member in members_added:
|
||||
# Greet anyone that was not the target (recipient) of this message.
|
||||
# To learn more about Adaptive Cards, see https://aka.ms/msbot-adaptivecards for more details.
|
||||
if member.id != turn_context.activity.recipient.id:
|
||||
welcome_card = self.create_adaptive_card_attachment()
|
||||
response = MessageFactory.attachment(welcome_card)
|
||||
await turn_context.send_activity(response)
|
||||
await DialogHelper.run_dialog(
|
||||
self.dialog,
|
||||
turn_context,
|
||||
self.conversation_state.create_property("DialogState"),
|
||||
)
|
||||
|
||||
# Load attachment from file.
|
||||
def create_adaptive_card_attachment(self):
|
||||
relative_path = os.path.abspath(os.path.dirname(__file__))
|
||||
path = os.path.join(relative_path, "../cards/welcomeCard.json")
|
||||
with open(path) as in_file:
|
||||
card = json.load(in_file)
|
||||
|
||||
return Attachment(
|
||||
content_type="application/vnd.microsoft.card.adaptive", content=card
|
||||
)
|
|
@ -1,41 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from botbuilder.core import ActivityHandler, ConversationState, UserState, TurnContext
|
||||
from botbuilder.dialogs import Dialog
|
||||
from helpers.dialog_helper import DialogHelper
|
||||
|
||||
|
||||
class DialogBot(ActivityHandler):
|
||||
def __init__(
|
||||
self,
|
||||
conversation_state: ConversationState,
|
||||
user_state: UserState,
|
||||
dialog: Dialog,
|
||||
):
|
||||
if conversation_state is None:
|
||||
raise Exception(
|
||||
"[DialogBot]: Missing parameter. conversation_state is required"
|
||||
)
|
||||
if user_state is None:
|
||||
raise Exception("[DialogBot]: Missing parameter. user_state is required")
|
||||
if dialog is None:
|
||||
raise Exception("[DialogBot]: Missing parameter. dialog is required")
|
||||
|
||||
self.conversation_state = conversation_state
|
||||
self.user_state = user_state
|
||||
self.dialog = dialog
|
||||
|
||||
async def on_turn(self, turn_context: TurnContext):
|
||||
await super().on_turn(turn_context)
|
||||
|
||||
# Save any state changes that might have occurred during the turn.
|
||||
await self.conversation_state.save_changes(turn_context, False)
|
||||
await self.user_state.save_changes(turn_context, False)
|
||||
|
||||
async def on_message_activity(self, turn_context: TurnContext):
|
||||
await DialogHelper.run_dialog(
|
||||
self.dialog,
|
||||
turn_context,
|
||||
self.conversation_state.create_property("DialogState"),
|
||||
)
|
|
@ -1,46 +0,0 @@
|
|||
{
|
||||
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
|
||||
"type": "AdaptiveCard",
|
||||
"version": "1.0",
|
||||
"body": [
|
||||
{
|
||||
"type": "Image",
|
||||
"url": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQtB3AwMUeNoq4gUBGe6Ocj8kyh3bXa9ZbV7u1fVKQoyKFHdkqU",
|
||||
"size": "stretch"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"spacing": "medium",
|
||||
"size": "default",
|
||||
"weight": "bolder",
|
||||
"text": "Welcome to Bot Framework!",
|
||||
"wrap": true,
|
||||
"maxLines": 0
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"size": "default",
|
||||
"isSubtle": "true",
|
||||
"text": "Now that you have successfully run your bot, follow the links in this Adaptive Card to expand your knowledge of Bot Framework.",
|
||||
"wrap": true,
|
||||
"maxLines": 0
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": "Action.OpenUrl",
|
||||
"title": "Get an overview",
|
||||
"url": "https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0"
|
||||
},
|
||||
{
|
||||
"type": "Action.OpenUrl",
|
||||
"title": "Ask a question",
|
||||
"url": "https://stackoverflow.com/questions/tagged/botframework"
|
||||
},
|
||||
{
|
||||
"type": "Action.OpenUrl",
|
||||
"title": "Learn how to deploy",
|
||||
"url": "https://docs.microsoft.com/azure/bot-service/bot-builder-howto-deploy-azure?view=azure-bot-service-4.0"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,339 +0,0 @@
|
|||
{
|
||||
"luis_schema_version": "3.2.0",
|
||||
"versionId": "0.1",
|
||||
"name": "FlightBooking",
|
||||
"desc": "Luis Model for CoreBot",
|
||||
"culture": "en-us",
|
||||
"tokenizerVersion": "1.0.0",
|
||||
"intents": [
|
||||
{
|
||||
"name": "BookFlight"
|
||||
},
|
||||
{
|
||||
"name": "Cancel"
|
||||
},
|
||||
{
|
||||
"name": "GetWeather"
|
||||
},
|
||||
{
|
||||
"name": "None"
|
||||
}
|
||||
],
|
||||
"entities": [],
|
||||
"composites": [
|
||||
{
|
||||
"name": "From",
|
||||
"children": [
|
||||
"Airport"
|
||||
],
|
||||
"roles": []
|
||||
},
|
||||
{
|
||||
"name": "To",
|
||||
"children": [
|
||||
"Airport"
|
||||
],
|
||||
"roles": []
|
||||
}
|
||||
],
|
||||
"closedLists": [
|
||||
{
|
||||
"name": "Airport",
|
||||
"subLists": [
|
||||
{
|
||||
"canonicalForm": "Paris",
|
||||
"list": [
|
||||
"paris",
|
||||
"cdg"
|
||||
]
|
||||
},
|
||||
{
|
||||
"canonicalForm": "London",
|
||||
"list": [
|
||||
"london",
|
||||
"lhr"
|
||||
]
|
||||
},
|
||||
{
|
||||
"canonicalForm": "Berlin",
|
||||
"list": [
|
||||
"berlin",
|
||||
"txl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"canonicalForm": "New York",
|
||||
"list": [
|
||||
"new york",
|
||||
"jfk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"canonicalForm": "Seattle",
|
||||
"list": [
|
||||
"seattle",
|
||||
"sea"
|
||||
]
|
||||
}
|
||||
],
|
||||
"roles": []
|
||||
}
|
||||
],
|
||||
"patternAnyEntities": [],
|
||||
"regex_entities": [],
|
||||
"prebuiltEntities": [
|
||||
{
|
||||
"name": "datetimeV2",
|
||||
"roles": []
|
||||
}
|
||||
],
|
||||
"model_features": [],
|
||||
"regex_features": [],
|
||||
"patterns": [],
|
||||
"utterances": [
|
||||
{
|
||||
"text": "book a flight",
|
||||
"intent": "BookFlight",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "book a flight from new york",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "From",
|
||||
"startPos": 19,
|
||||
"endPos": 26
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "book a flight from seattle",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "From",
|
||||
"startPos": 19,
|
||||
"endPos": 25
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "book a hotel in new york",
|
||||
"intent": "None",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "book a restaurant",
|
||||
"intent": "None",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "book flight from london to paris on feb 14th",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "From",
|
||||
"startPos": 17,
|
||||
"endPos": 22
|
||||
},
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 27,
|
||||
"endPos": 31
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "book flight to berlin on feb 14th",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 15,
|
||||
"endPos": 20
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "book me a flight from london to paris",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "From",
|
||||
"startPos": 22,
|
||||
"endPos": 27
|
||||
},
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 32,
|
||||
"endPos": 36
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "bye",
|
||||
"intent": "Cancel",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "cancel booking",
|
||||
"intent": "Cancel",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "exit",
|
||||
"intent": "Cancel",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "find an airport near me",
|
||||
"intent": "None",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "flight to paris",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 10,
|
||||
"endPos": 14
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "flight to paris from london on feb 14th",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 10,
|
||||
"endPos": 14
|
||||
},
|
||||
{
|
||||
"entity": "From",
|
||||
"startPos": 21,
|
||||
"endPos": 26
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "fly from berlin to paris on may 5th",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "From",
|
||||
"startPos": 9,
|
||||
"endPos": 14
|
||||
},
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 19,
|
||||
"endPos": 23
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "go to paris",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 6,
|
||||
"endPos": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "going from paris to berlin",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "From",
|
||||
"startPos": 11,
|
||||
"endPos": 15
|
||||
},
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 20,
|
||||
"endPos": 25
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "i'd like to rent a car",
|
||||
"intent": "None",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "ignore",
|
||||
"intent": "Cancel",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "travel from new york to paris",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "From",
|
||||
"startPos": 12,
|
||||
"endPos": 19
|
||||
},
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 24,
|
||||
"endPos": 28
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "travel to new york",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 10,
|
||||
"endPos": 17
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "travel to paris",
|
||||
"intent": "BookFlight",
|
||||
"entities": [
|
||||
{
|
||||
"entity": "To",
|
||||
"startPos": 10,
|
||||
"endPos": 14
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"text": "what's the forecast for this friday?",
|
||||
"intent": "GetWeather",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "what's the weather like for tomorrow",
|
||||
"intent": "GetWeather",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "what's the weather like in new york",
|
||||
"intent": "GetWeather",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "what's the weather like?",
|
||||
"intent": "GetWeather",
|
||||
"entities": []
|
||||
},
|
||||
{
|
||||
"text": "winter is coming",
|
||||
"intent": "None",
|
||||
"entities": []
|
||||
}
|
||||
],
|
||||
"settings": []
|
||||
}
|
|
@ -1,265 +0,0 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"groupLocation": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Specifies the location of the Resource Group."
|
||||
}
|
||||
},
|
||||
"groupName": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Specifies the name of the Resource Group."
|
||||
}
|
||||
},
|
||||
"appId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"appSecret": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"botId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable."
|
||||
}
|
||||
},
|
||||
"botSku": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanName": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The name of the App Service Plan."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanSku": {
|
||||
"type": "object",
|
||||
"defaultValue": {
|
||||
"name": "S1",
|
||||
"tier": "Standard",
|
||||
"size": "S1",
|
||||
"family": "S",
|
||||
"capacity": 1
|
||||
},
|
||||
"metadata": {
|
||||
"description": "The SKU of the App Service Plan. Defaults to Standard values."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanLocation": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The location of the App Service Plan. Defaults to \"westus\"."
|
||||
}
|
||||
},
|
||||
"newWebAppName": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"."
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"appServicePlanName": "[parameters('newAppServicePlanName')]",
|
||||
"resourcesLocation": "[parameters('newAppServicePlanLocation')]",
|
||||
"webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]",
|
||||
"siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]",
|
||||
"botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]",
|
||||
"publishingUsername": "[concat('$', parameters('newWebAppName'))]",
|
||||
"resourceGroupId": "[concat(subscription().id, '/resourceGroups/', parameters('groupName'))]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"name": "[parameters('groupName')]",
|
||||
"type": "Microsoft.Resources/resourceGroups",
|
||||
"apiVersion": "2018-05-01",
|
||||
"location": "[parameters('groupLocation')]",
|
||||
"properties": {}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Resources/deployments",
|
||||
"apiVersion": "2018-05-01",
|
||||
"name": "storageDeployment",
|
||||
"resourceGroup": "[parameters('groupName')]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Resources/resourceGroups/', parameters('groupName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"mode": "Incremental",
|
||||
"template": {
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {},
|
||||
"variables": {},
|
||||
"resources": [
|
||||
{
|
||||
"comments": "Create a new Linux App Service Plan if no existing App Service Plan name was passed in.",
|
||||
"type": "Microsoft.Web/serverfarms",
|
||||
"name": "[variables('appServicePlanName')]",
|
||||
"apiVersion": "2018-02-01",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"sku": "[parameters('newAppServicePlanSku')]",
|
||||
"kind": "linux",
|
||||
"properties": {
|
||||
"name": "[variables('appServicePlanName')]",
|
||||
"perSiteScaling": false,
|
||||
"reserved": true,
|
||||
"targetWorkerCount": 0,
|
||||
"targetWorkerSizeId": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"comments": "Create a Web App using a Linux App Service Plan",
|
||||
"type": "Microsoft.Web/sites",
|
||||
"apiVersion": "2015-08-01",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"kind": "app,linux",
|
||||
"dependsOn": [
|
||||
"[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/serverfarms/', variables('appServicePlanName'))]"
|
||||
],
|
||||
"name": "[variables('webAppName')]",
|
||||
"properties": {
|
||||
"name": "[variables('webAppName')]",
|
||||
"hostNameSslStates": [
|
||||
{
|
||||
"name": "[concat(parameters('newWebAppName'), '.azurewebsites.net')]",
|
||||
"sslState": "Disabled",
|
||||
"hostType": "Standard"
|
||||
},
|
||||
{
|
||||
"name": "[concat(parameters('newWebAppName'), '.scm.azurewebsites.net')]",
|
||||
"sslState": "Disabled",
|
||||
"hostType": "Repository"
|
||||
}
|
||||
],
|
||||
"serverFarmId": "[variables('appServicePlanName')]",
|
||||
"siteConfig": {
|
||||
"appSettings": [
|
||||
{
|
||||
"name": "SCM_DO_BUILD_DURING_DEPLOYMENT",
|
||||
"value": "true"
|
||||
},
|
||||
{
|
||||
"name": "MicrosoftAppId",
|
||||
"value": "[parameters('appId')]"
|
||||
},
|
||||
{
|
||||
"name": "MicrosoftAppPassword",
|
||||
"value": "[parameters('appSecret')]"
|
||||
}
|
||||
],
|
||||
"cors": {
|
||||
"allowedOrigins": [
|
||||
"https://botservice.hosting.portal.azure.net",
|
||||
"https://hosting.onecloud.azure-test.net/"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Web/sites/config",
|
||||
"apiVersion": "2016-08-01",
|
||||
"name": "[concat(variables('webAppName'), '/web')]",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"dependsOn": [
|
||||
"[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/sites/', variables('webAppName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"numberOfWorkers": 1,
|
||||
"defaultDocuments": [
|
||||
"Default.htm",
|
||||
"Default.html",
|
||||
"Default.asp",
|
||||
"index.htm",
|
||||
"index.html",
|
||||
"iisstart.htm",
|
||||
"default.aspx",
|
||||
"index.php",
|
||||
"hostingstart.html"
|
||||
],
|
||||
"netFrameworkVersion": "v4.0",
|
||||
"phpVersion": "",
|
||||
"pythonVersion": "",
|
||||
"nodeVersion": "",
|
||||
"linuxFxVersion": "PYTHON|3.7",
|
||||
"requestTracingEnabled": false,
|
||||
"remoteDebuggingEnabled": false,
|
||||
"remoteDebuggingVersion": "VS2017",
|
||||
"httpLoggingEnabled": true,
|
||||
"logsDirectorySizeLimit": 35,
|
||||
"detailedErrorLoggingEnabled": false,
|
||||
"publishingUsername": "[variables('publishingUsername')]",
|
||||
"scmType": "None",
|
||||
"use32BitWorkerProcess": true,
|
||||
"webSocketsEnabled": false,
|
||||
"alwaysOn": false,
|
||||
"appCommandLine": "gunicorn --bind 0.0.0.0 --worker-class aiohttp.worker.GunicornWebWorker --timeout 600 app:APP",
|
||||
"managedPipelineMode": "Integrated",
|
||||
"virtualApplications": [
|
||||
{
|
||||
"virtualPath": "/",
|
||||
"physicalPath": "site\\wwwroot",
|
||||
"preloadEnabled": false,
|
||||
"virtualDirectories": null
|
||||
}
|
||||
],
|
||||
"winAuthAdminState": 0,
|
||||
"winAuthTenantState": 0,
|
||||
"customAppPoolIdentityAdminState": false,
|
||||
"customAppPoolIdentityTenantState": false,
|
||||
"loadBalancing": "LeastRequests",
|
||||
"routingRules": [],
|
||||
"experiments": {
|
||||
"rampUpRules": []
|
||||
},
|
||||
"autoHealEnabled": false,
|
||||
"vnetName": "",
|
||||
"minTlsVersion": "1.2",
|
||||
"ftpsState": "AllAllowed",
|
||||
"reservedInstanceCount": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2021-03-01",
|
||||
"type": "Microsoft.BotService/botServices",
|
||||
"name": "[parameters('botId')]",
|
||||
"location": "global",
|
||||
"kind": "azurebot",
|
||||
"sku": {
|
||||
"name": "[parameters('botSku')]"
|
||||
},
|
||||
"properties": {
|
||||
"name": "[parameters('botId')]",
|
||||
"displayName": "[parameters('botId')]",
|
||||
"iconUrl": "https://docs.botframework.com/static/devportal/client/images/bot-framework-default.png",
|
||||
"endpoint": "[variables('botEndpoint')]",
|
||||
"msaAppId": "[parameters('appId')]",
|
||||
"luisAppIds": [],
|
||||
"schemaTransformationVersion": "1.3",
|
||||
"isCmekEnabled": false,
|
||||
"isIsolated": false
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/sites/', variables('webAppName'))]"
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputs": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,243 +0,0 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"appId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"appSecret": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"botId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable."
|
||||
}
|
||||
},
|
||||
"botSku": {
|
||||
"defaultValue": "F0",
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanName": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "The name of the new App Service Plan."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanSku": {
|
||||
"type": "object",
|
||||
"defaultValue": {
|
||||
"name": "S1",
|
||||
"tier": "Standard",
|
||||
"size": "S1",
|
||||
"family": "S",
|
||||
"capacity": 1
|
||||
},
|
||||
"metadata": {
|
||||
"description": "The SKU of the App Service Plan. Defaults to Standard values."
|
||||
}
|
||||
},
|
||||
"appServicePlanLocation": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The location of the App Service Plan."
|
||||
}
|
||||
},
|
||||
"existingAppServicePlan": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "Name of the existing App Service Plan used to create the Web App for the bot."
|
||||
}
|
||||
},
|
||||
"newWebAppName": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"."
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"defaultAppServicePlanName": "[if(empty(parameters('existingAppServicePlan')), 'createNewAppServicePlan', parameters('existingAppServicePlan'))]",
|
||||
"useExistingAppServicePlan": "[not(equals(variables('defaultAppServicePlanName'), 'createNewAppServicePlan'))]",
|
||||
"servicePlanName": "[if(variables('useExistingAppServicePlan'), parameters('existingAppServicePlan'), parameters('newAppServicePlanName'))]",
|
||||
"publishingUsername": "[concat('$', parameters('newWebAppName'))]",
|
||||
"resourcesLocation": "[parameters('appServicePlanLocation')]",
|
||||
"webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]",
|
||||
"siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]",
|
||||
"botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"comments": "Create a new Linux App Service Plan if no existing App Service Plan name was passed in.",
|
||||
"type": "Microsoft.Web/serverfarms",
|
||||
"apiVersion": "2016-09-01",
|
||||
"name": "[variables('servicePlanName')]",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"sku": "[parameters('newAppServicePlanSku')]",
|
||||
"kind": "linux",
|
||||
"properties": {
|
||||
"name": "[variables('servicePlanName')]",
|
||||
"perSiteScaling": false,
|
||||
"reserved": true,
|
||||
"targetWorkerCount": 0,
|
||||
"targetWorkerSizeId": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"comments": "Create a Web App using a Linux App Service Plan",
|
||||
"type": "Microsoft.Web/sites",
|
||||
"apiVersion": "2016-08-01",
|
||||
"name": "[variables('webAppName')]",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]"
|
||||
],
|
||||
"kind": "app,linux",
|
||||
"properties": {
|
||||
"enabled": true,
|
||||
"hostNameSslStates": [
|
||||
{
|
||||
"name": "[concat(parameters('newWebAppName'), '.azurewebsites.net')]",
|
||||
"sslState": "Disabled",
|
||||
"hostType": "Standard"
|
||||
},
|
||||
{
|
||||
"name": "[concat(parameters('newWebAppName'), '.scm.azurewebsites.net')]",
|
||||
"sslState": "Disabled",
|
||||
"hostType": "Repository"
|
||||
}
|
||||
],
|
||||
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]",
|
||||
"reserved": true,
|
||||
"scmSiteAlsoStopped": false,
|
||||
"clientAffinityEnabled": false,
|
||||
"clientCertEnabled": false,
|
||||
"hostNamesDisabled": false,
|
||||
"containerSize": 0,
|
||||
"dailyMemoryTimeQuota": 0,
|
||||
"httpsOnly": false,
|
||||
"siteConfig": {
|
||||
"appSettings": [
|
||||
{
|
||||
"name": "MicrosoftAppId",
|
||||
"value": "[parameters('appId')]"
|
||||
},
|
||||
{
|
||||
"name": "MicrosoftAppPassword",
|
||||
"value": "[parameters('appSecret')]"
|
||||
},
|
||||
{
|
||||
"name": "SCM_DO_BUILD_DURING_DEPLOYMENT",
|
||||
"value": "true"
|
||||
}
|
||||
],
|
||||
"cors": {
|
||||
"allowedOrigins": [
|
||||
"https://botservice.hosting.portal.azure.net",
|
||||
"https://hosting.onecloud.azure-test.net/"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Web/sites/config",
|
||||
"apiVersion": "2016-08-01",
|
||||
"name": "[concat(variables('webAppName'), '/web')]",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Web/sites', variables('webAppName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"numberOfWorkers": 1,
|
||||
"defaultDocuments": [
|
||||
"Default.htm",
|
||||
"Default.html",
|
||||
"Default.asp",
|
||||
"index.htm",
|
||||
"index.html",
|
||||
"iisstart.htm",
|
||||
"default.aspx",
|
||||
"index.php",
|
||||
"hostingstart.html"
|
||||
],
|
||||
"netFrameworkVersion": "v4.0",
|
||||
"phpVersion": "",
|
||||
"pythonVersion": "",
|
||||
"nodeVersion": "",
|
||||
"linuxFxVersion": "PYTHON|3.7",
|
||||
"requestTracingEnabled": false,
|
||||
"remoteDebuggingEnabled": false,
|
||||
"remoteDebuggingVersion": "VS2017",
|
||||
"httpLoggingEnabled": true,
|
||||
"logsDirectorySizeLimit": 35,
|
||||
"detailedErrorLoggingEnabled": false,
|
||||
"publishingUsername": "[variables('publishingUsername')]",
|
||||
"scmType": "None",
|
||||
"use32BitWorkerProcess": true,
|
||||
"webSocketsEnabled": false,
|
||||
"alwaysOn": false,
|
||||
"appCommandLine": "gunicorn --bind 0.0.0.0 --worker-class aiohttp.worker.GunicornWebWorker --timeout 600 app:APP",
|
||||
"managedPipelineMode": "Integrated",
|
||||
"virtualApplications": [
|
||||
{
|
||||
"virtualPath": "/",
|
||||
"physicalPath": "site\\wwwroot",
|
||||
"preloadEnabled": false,
|
||||
"virtualDirectories": null
|
||||
}
|
||||
],
|
||||
"winAuthAdminState": 0,
|
||||
"winAuthTenantState": 0,
|
||||
"customAppPoolIdentityAdminState": false,
|
||||
"customAppPoolIdentityTenantState": false,
|
||||
"loadBalancing": "LeastRequests",
|
||||
"routingRules": [],
|
||||
"experiments": {
|
||||
"rampUpRules": []
|
||||
},
|
||||
"autoHealEnabled": false,
|
||||
"vnetName": "",
|
||||
"minTlsVersion": "1.2",
|
||||
"ftpsState": "AllAllowed",
|
||||
"reservedInstanceCount": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2021-03-01",
|
||||
"type": "Microsoft.BotService/botServices",
|
||||
"name": "[parameters('botId')]",
|
||||
"location": "global",
|
||||
"kind": "azurebot",
|
||||
"sku": {
|
||||
"name": "[parameters('botSku')]"
|
||||
},
|
||||
"properties": {
|
||||
"name": "[parameters('botId')]",
|
||||
"displayName": "[parameters('botId')]",
|
||||
"iconUrl": "https://docs.botframework.com/static/devportal/client/images/bot-framework-default.png",
|
||||
"endpoint": "[variables('botEndpoint')]",
|
||||
"msaAppId": "[parameters('appId')]",
|
||||
"luisAppIds": [],
|
||||
"schemaTransformationVersion": "1.3",
|
||||
"isCmekEnabled": false,
|
||||
"isIsolated": false
|
||||
},
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Web/sites/', variables('webAppName'))]"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from .booking_dialog import BookingDialog
|
||||
from .cancel_and_help_dialog import CancelAndHelpDialog
|
||||
from .date_resolver_dialog import DateResolverDialog
|
||||
from .main_dialog import MainDialog
|
||||
|
||||
__all__ = ["BookingDialog", "CancelAndHelpDialog", "DateResolverDialog", "MainDialog"]
|
|
@ -1,136 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from datatypes_date_time.timex import Timex
|
||||
|
||||
from botbuilder.dialogs import WaterfallDialog, WaterfallStepContext, DialogTurnResult
|
||||
from botbuilder.dialogs.prompts import ConfirmPrompt, TextPrompt, PromptOptions
|
||||
from botbuilder.core import MessageFactory
|
||||
from botbuilder.schema import InputHints
|
||||
from .cancel_and_help_dialog import CancelAndHelpDialog
|
||||
from .date_resolver_dialog import DateResolverDialog
|
||||
|
||||
|
||||
class BookingDialog(CancelAndHelpDialog):
|
||||
def __init__(self, dialog_id: str = None):
|
||||
super(BookingDialog, self).__init__(dialog_id or BookingDialog.__name__)
|
||||
|
||||
self.add_dialog(TextPrompt(TextPrompt.__name__))
|
||||
self.add_dialog(ConfirmPrompt(ConfirmPrompt.__name__))
|
||||
self.add_dialog(DateResolverDialog(DateResolverDialog.__name__))
|
||||
self.add_dialog(
|
||||
WaterfallDialog(
|
||||
WaterfallDialog.__name__,
|
||||
[
|
||||
self.destination_step,
|
||||
self.origin_step,
|
||||
self.travel_date_step,
|
||||
self.confirm_step,
|
||||
self.final_step,
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
self.initial_dialog_id = WaterfallDialog.__name__
|
||||
|
||||
async def destination_step(
|
||||
self, step_context: WaterfallStepContext
|
||||
) -> DialogTurnResult:
|
||||
"""
|
||||
If a destination city has not been provided, prompt for one.
|
||||
:param step_context:
|
||||
:return DialogTurnResult:
|
||||
"""
|
||||
booking_details = step_context.options
|
||||
|
||||
if booking_details.destination is None:
|
||||
message_text = "Where would you like to travel to?"
|
||||
prompt_message = MessageFactory.text(
|
||||
message_text, message_text, InputHints.expecting_input
|
||||
)
|
||||
return await step_context.prompt(
|
||||
TextPrompt.__name__, PromptOptions(prompt=prompt_message)
|
||||
)
|
||||
return await step_context.next(booking_details.destination)
|
||||
|
||||
async def origin_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
|
||||
"""
|
||||
If an origin city has not been provided, prompt for one.
|
||||
:param step_context:
|
||||
:return DialogTurnResult:
|
||||
"""
|
||||
booking_details = step_context.options
|
||||
|
||||
# Capture the response to the previous step's prompt
|
||||
booking_details.destination = step_context.result
|
||||
if booking_details.origin is None:
|
||||
message_text = "From what city will you be travelling?"
|
||||
prompt_message = MessageFactory.text(
|
||||
message_text, message_text, InputHints.expecting_input
|
||||
)
|
||||
return await step_context.prompt(
|
||||
TextPrompt.__name__, PromptOptions(prompt=prompt_message)
|
||||
)
|
||||
return await step_context.next(booking_details.origin)
|
||||
|
||||
async def travel_date_step(
|
||||
self, step_context: WaterfallStepContext
|
||||
) -> DialogTurnResult:
|
||||
"""
|
||||
If a travel date has not been provided, prompt for one.
|
||||
This will use the DATE_RESOLVER_DIALOG.
|
||||
:param step_context:
|
||||
:return DialogTurnResult:
|
||||
"""
|
||||
booking_details = step_context.options
|
||||
|
||||
# Capture the results of the previous step
|
||||
booking_details.origin = step_context.result
|
||||
if not booking_details.travel_date or self.is_ambiguous(
|
||||
booking_details.travel_date
|
||||
):
|
||||
return await step_context.begin_dialog(
|
||||
DateResolverDialog.__name__, booking_details.travel_date
|
||||
)
|
||||
return await step_context.next(booking_details.travel_date)
|
||||
|
||||
async def confirm_step(
|
||||
self, step_context: WaterfallStepContext
|
||||
) -> DialogTurnResult:
|
||||
"""
|
||||
Confirm the information the user has provided.
|
||||
:param step_context:
|
||||
:return DialogTurnResult:
|
||||
"""
|
||||
booking_details = step_context.options
|
||||
|
||||
# Capture the results of the previous step
|
||||
booking_details.travel_date = step_context.result
|
||||
message_text = (
|
||||
f"Please confirm, I have you traveling to: { booking_details.destination } from: "
|
||||
f"{ booking_details.origin } on: { booking_details.travel_date}."
|
||||
)
|
||||
prompt_message = MessageFactory.text(
|
||||
message_text, message_text, InputHints.expecting_input
|
||||
)
|
||||
|
||||
# Offer a YES/NO prompt.
|
||||
return await step_context.prompt(
|
||||
ConfirmPrompt.__name__, PromptOptions(prompt=prompt_message)
|
||||
)
|
||||
|
||||
async def final_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
|
||||
"""
|
||||
Complete the interaction and end the dialog.
|
||||
:param step_context:
|
||||
:return DialogTurnResult:
|
||||
"""
|
||||
if step_context.result:
|
||||
booking_details = step_context.options
|
||||
|
||||
return await step_context.end_dialog(booking_details)
|
||||
return await step_context.end_dialog()
|
||||
|
||||
def is_ambiguous(self, timex: str) -> bool:
|
||||
timex_property = Timex(timex)
|
||||
return "definite" not in timex_property.types
|
|
@ -1,47 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from botbuilder.dialogs import (
|
||||
ComponentDialog,
|
||||
DialogContext,
|
||||
DialogTurnResult,
|
||||
DialogTurnStatus,
|
||||
)
|
||||
from botbuilder.schema import ActivityTypes, InputHints
|
||||
from botbuilder.core import MessageFactory
|
||||
|
||||
|
||||
class CancelAndHelpDialog(ComponentDialog):
|
||||
def __init__(self, dialog_id: str):
|
||||
super(CancelAndHelpDialog, self).__init__(dialog_id)
|
||||
|
||||
async def on_continue_dialog(self, inner_dc: DialogContext) -> DialogTurnResult:
|
||||
result = await self.interrupt(inner_dc)
|
||||
if result is not None:
|
||||
return result
|
||||
|
||||
return await super(CancelAndHelpDialog, self).on_continue_dialog(inner_dc)
|
||||
|
||||
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_text = "Show Help..."
|
||||
help_message = MessageFactory.text(
|
||||
help_message_text, help_message_text, InputHints.expecting_input
|
||||
)
|
||||
|
||||
if text in ("help", "?"):
|
||||
await inner_dc.context.send_activity(help_message)
|
||||
return DialogTurnResult(DialogTurnStatus.Waiting)
|
||||
|
||||
cancel_message_text = "Cancelling"
|
||||
cancel_message = MessageFactory.text(
|
||||
cancel_message_text, cancel_message_text, InputHints.ignoring_input
|
||||
)
|
||||
|
||||
if text in ("cancel", "quit"):
|
||||
await inner_dc.context.send_activity(cancel_message)
|
||||
return await inner_dc.cancel_all_dialogs()
|
||||
|
||||
return None
|
|
@ -1,80 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from datatypes_date_time.timex import Timex
|
||||
|
||||
from botbuilder.core import MessageFactory
|
||||
from botbuilder.dialogs import WaterfallDialog, DialogTurnResult, WaterfallStepContext
|
||||
from botbuilder.dialogs.prompts import (
|
||||
DateTimePrompt,
|
||||
PromptValidatorContext,
|
||||
PromptOptions,
|
||||
DateTimeResolution,
|
||||
)
|
||||
from botbuilder.schema import InputHints
|
||||
from .cancel_and_help_dialog import CancelAndHelpDialog
|
||||
|
||||
|
||||
class DateResolverDialog(CancelAndHelpDialog):
|
||||
def __init__(self, dialog_id: str = None):
|
||||
super(DateResolverDialog, self).__init__(
|
||||
dialog_id or DateResolverDialog.__name__
|
||||
)
|
||||
|
||||
self.add_dialog(
|
||||
DateTimePrompt(
|
||||
DateTimePrompt.__name__, DateResolverDialog.datetime_prompt_validator
|
||||
)
|
||||
)
|
||||
self.add_dialog(
|
||||
WaterfallDialog(
|
||||
WaterfallDialog.__name__ + "2", [self.initial_step, self.final_step]
|
||||
)
|
||||
)
|
||||
|
||||
self.initial_dialog_id = WaterfallDialog.__name__ + "2"
|
||||
|
||||
async def initial_step(
|
||||
self, step_context: WaterfallStepContext
|
||||
) -> DialogTurnResult:
|
||||
timex = step_context.options
|
||||
|
||||
prompt_msg_text = "On what date would you like to travel?"
|
||||
prompt_msg = MessageFactory.text(
|
||||
prompt_msg_text, prompt_msg_text, InputHints.expecting_input
|
||||
)
|
||||
|
||||
reprompt_msg_text = "I'm sorry, for best results, please enter your travel date including the month, " \
|
||||
"day and year. "
|
||||
reprompt_msg = MessageFactory.text(
|
||||
reprompt_msg_text, reprompt_msg_text, InputHints.expecting_input
|
||||
)
|
||||
|
||||
if timex is None:
|
||||
# We were not given any date at all so prompt the user.
|
||||
return await step_context.prompt(
|
||||
DateTimePrompt.__name__,
|
||||
PromptOptions(prompt=prompt_msg, retry_prompt=reprompt_msg),
|
||||
)
|
||||
# We have a Date we just need to check it is unambiguous.
|
||||
if "definite" not in Timex(timex).types:
|
||||
# This is essentially a "reprompt" of the data we were given up front.
|
||||
return await step_context.prompt(
|
||||
DateTimePrompt.__name__, PromptOptions(prompt=reprompt_msg)
|
||||
)
|
||||
|
||||
return await step_context.next(DateTimeResolution(timex=timex))
|
||||
|
||||
async def final_step(self, step_context: WaterfallStepContext):
|
||||
timex = step_context.result[0].timex
|
||||
return await step_context.end_dialog(timex)
|
||||
|
||||
@staticmethod
|
||||
async def datetime_prompt_validator(prompt_context: PromptValidatorContext) -> bool:
|
||||
if prompt_context.recognized.succeeded:
|
||||
timex = prompt_context.recognized.value[0].timex.split("T")[0]
|
||||
|
||||
# TODO: Needs TimexProperty
|
||||
return "definite" in Timex(timex).types
|
||||
|
||||
return False
|
|
@ -1,132 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from botbuilder.dialogs import (
|
||||
ComponentDialog,
|
||||
WaterfallDialog,
|
||||
WaterfallStepContext,
|
||||
DialogTurnResult,
|
||||
)
|
||||
from botbuilder.dialogs.prompts import TextPrompt, PromptOptions
|
||||
from botbuilder.core import MessageFactory, TurnContext
|
||||
from botbuilder.schema import InputHints
|
||||
|
||||
from booking_details import BookingDetails
|
||||
from flight_booking_recognizer import FlightBookingRecognizer
|
||||
from helpers.luis_helper import LuisHelper, Intent
|
||||
from .booking_dialog import BookingDialog
|
||||
|
||||
|
||||
class MainDialog(ComponentDialog):
|
||||
def __init__(
|
||||
self, luis_recognizer: FlightBookingRecognizer, booking_dialog: BookingDialog
|
||||
):
|
||||
super(MainDialog, self).__init__(MainDialog.__name__)
|
||||
|
||||
self._luis_recognizer = luis_recognizer
|
||||
self._booking_dialog_id = booking_dialog.id
|
||||
|
||||
self.add_dialog(TextPrompt(TextPrompt.__name__))
|
||||
self.add_dialog(booking_dialog)
|
||||
self.add_dialog(
|
||||
WaterfallDialog(
|
||||
"WFDialog", [self.intro_step, self.act_step, self.final_step]
|
||||
)
|
||||
)
|
||||
|
||||
self.initial_dialog_id = "WFDialog"
|
||||
|
||||
async def intro_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
|
||||
if not self._luis_recognizer.is_configured:
|
||||
await step_context.context.send_activity(
|
||||
MessageFactory.text(
|
||||
"NOTE: LUIS is not configured. To enable all capabilities, add 'LuisAppId', 'LuisAPIKey' and "
|
||||
"'LuisAPIHostName' to the appsettings.json file.",
|
||||
input_hint=InputHints.ignoring_input,
|
||||
)
|
||||
)
|
||||
|
||||
return await step_context.next(None)
|
||||
message_text = (
|
||||
str(step_context.options)
|
||||
if step_context.options
|
||||
else "What can I help you with today?"
|
||||
)
|
||||
prompt_message = MessageFactory.text(
|
||||
message_text, message_text, InputHints.expecting_input
|
||||
)
|
||||
|
||||
return await step_context.prompt(
|
||||
TextPrompt.__name__, PromptOptions(prompt=prompt_message)
|
||||
)
|
||||
|
||||
async def act_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
|
||||
if not self._luis_recognizer.is_configured:
|
||||
# LUIS is not configured, we just run the BookingDialog path with an empty BookingDetailsInstance.
|
||||
return await step_context.begin_dialog(
|
||||
self._booking_dialog_id, BookingDetails()
|
||||
)
|
||||
|
||||
# Call LUIS and gather any potential booking details. (Note the TurnContext has the response to the prompt.)
|
||||
intent, luis_result = await LuisHelper.execute_luis_query(
|
||||
self._luis_recognizer, step_context.context
|
||||
)
|
||||
|
||||
if intent == Intent.BOOK_FLIGHT.value and luis_result:
|
||||
# Show a warning for Origin and Destination if we can't resolve them.
|
||||
await MainDialog._show_warning_for_unsupported_cities(
|
||||
step_context.context, luis_result
|
||||
)
|
||||
|
||||
# Run the BookingDialog giving it whatever details we have from the LUIS call.
|
||||
return await step_context.begin_dialog(self._booking_dialog_id, luis_result)
|
||||
|
||||
if intent == Intent.GET_WEATHER.value:
|
||||
get_weather_text = "TODO: get weather flow here"
|
||||
get_weather_message = MessageFactory.text(
|
||||
get_weather_text, get_weather_text, InputHints.ignoring_input
|
||||
)
|
||||
await step_context.context.send_activity(get_weather_message)
|
||||
|
||||
else:
|
||||
didnt_understand_text = (
|
||||
"Sorry, I didn't get that. Please try asking in a different way"
|
||||
)
|
||||
didnt_understand_message = MessageFactory.text(
|
||||
didnt_understand_text, didnt_understand_text, InputHints.ignoring_input
|
||||
)
|
||||
await step_context.context.send_activity(didnt_understand_message)
|
||||
|
||||
return await step_context.next(None)
|
||||
|
||||
async def final_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
|
||||
# If the child dialog ("BookingDialog") was cancelled or the user failed to confirm,
|
||||
# the Result here will be null.
|
||||
if step_context.result is not None:
|
||||
result = step_context.result
|
||||
|
||||
# Now we have all the booking details call the booking service.
|
||||
|
||||
# If the call to the booking service was successful tell the user.
|
||||
# time_property = Timex(result.travel_date)
|
||||
# travel_date_msg = time_property.to_natural_language(datetime.now())
|
||||
msg_txt = f"I have you booked to {result.destination} from {result.origin} on {result.travel_date}"
|
||||
message = MessageFactory.text(msg_txt, msg_txt, InputHints.ignoring_input)
|
||||
await step_context.context.send_activity(message)
|
||||
|
||||
prompt_message = "What else can I do for you?"
|
||||
return await step_context.replace_dialog(self.id, prompt_message)
|
||||
|
||||
@staticmethod
|
||||
async def _show_warning_for_unsupported_cities(
|
||||
context: TurnContext, luis_result: BookingDetails
|
||||
) -> None:
|
||||
if luis_result.unsupported_airports:
|
||||
message_text = (
|
||||
f"Sorry but the following airports are not supported:"
|
||||
f" {', '.join(luis_result.unsupported_airports)}"
|
||||
)
|
||||
message = MessageFactory.text(
|
||||
message_text, message_text, InputHints.ignoring_input
|
||||
)
|
||||
await context.send_activity(message)
|
|
@ -1,32 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from botbuilder.ai.luis import LuisApplication, LuisRecognizer
|
||||
from botbuilder.core import Recognizer, RecognizerResult, TurnContext
|
||||
|
||||
|
||||
class FlightBookingRecognizer(Recognizer):
|
||||
def __init__(self, configuration: object):
|
||||
self._recognizer = None
|
||||
|
||||
luis_is_configured = (
|
||||
configuration.luis_app_id
|
||||
and configuration.luis_api_key
|
||||
and configuration.luis_api_host_name
|
||||
)
|
||||
if luis_is_configured:
|
||||
luis_application = LuisApplication(
|
||||
configuration.luis_app_id,
|
||||
configuration.luis_api_key,
|
||||
"https://" + configuration.luis_api_host_name,
|
||||
)
|
||||
|
||||
self._recognizer = LuisRecognizer(luis_application)
|
||||
|
||||
@property
|
||||
def is_configured(self) -> bool:
|
||||
# Returns true if luis is configured in the config.py and initialized.
|
||||
return self._recognizer is not None
|
||||
|
||||
async def recognize(self, turn_context: TurnContext) -> RecognizerResult:
|
||||
return await self._recognizer.recognize(turn_context)
|
|
@ -1,6 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from . import activity_helper, luis_helper, dialog_helper
|
||||
|
||||
__all__ = ["activity_helper", "dialog_helper", "luis_helper"]
|
|
@ -1,37 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from datetime import datetime
|
||||
from botbuilder.schema import (
|
||||
Activity,
|
||||
ActivityTypes,
|
||||
ChannelAccount,
|
||||
ConversationAccount,
|
||||
)
|
||||
|
||||
|
||||
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=[],
|
||||
)
|
|
@ -1,19 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from botbuilder.core import StatePropertyAccessor, TurnContext
|
||||
from botbuilder.dialogs import Dialog, DialogSet, DialogTurnStatus
|
||||
|
||||
|
||||
class DialogHelper:
|
||||
@staticmethod
|
||||
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)
|
||||
results = await dialog_context.continue_dialog()
|
||||
if results.status == DialogTurnStatus.Empty:
|
||||
await dialog_context.begin_dialog(dialog.id)
|
|
@ -1,102 +0,0 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
from enum import Enum
|
||||
from typing import Dict
|
||||
from botbuilder.ai.luis import LuisRecognizer
|
||||
from botbuilder.core import IntentScore, TopIntent, TurnContext
|
||||
|
||||
from booking_details import BookingDetails
|
||||
|
||||
|
||||
class Intent(Enum):
|
||||
BOOK_FLIGHT = "BookFlight"
|
||||
CANCEL = "Cancel"
|
||||
GET_WEATHER = "GetWeather"
|
||||
NONE_INTENT = "NoneIntent"
|
||||
|
||||
|
||||
def top_intent(intents: Dict[Intent, dict]) -> TopIntent:
|
||||
max_intent = Intent.NONE_INTENT
|
||||
max_value = 0.0
|
||||
|
||||
for intent, value in intents:
|
||||
intent_score = IntentScore(value)
|
||||
if intent_score.score > max_value:
|
||||
max_intent, max_value = intent, intent_score.score
|
||||
|
||||
return TopIntent(max_intent, max_value)
|
||||
|
||||
|
||||
class LuisHelper:
|
||||
@staticmethod
|
||||
async def execute_luis_query(
|
||||
luis_recognizer: LuisRecognizer, turn_context: TurnContext
|
||||
) -> (Intent, object):
|
||||
"""
|
||||
Returns an object with preformatted LUIS results for the bot's dialogs to consume.
|
||||
"""
|
||||
result = None
|
||||
intent = None
|
||||
|
||||
try:
|
||||
recognizer_result = await luis_recognizer.recognize(turn_context)
|
||||
|
||||
intent = (
|
||||
sorted(
|
||||
recognizer_result.intents,
|
||||
key=recognizer_result.intents.get,
|
||||
reverse=True,
|
||||
)[:1][0]
|
||||
if recognizer_result.intents
|
||||
else None
|
||||
)
|
||||
|
||||
if intent == Intent.BOOK_FLIGHT.value:
|
||||
result = BookingDetails()
|
||||
|
||||
# We need to get the result from the LUIS JSON which at every level returns an array.
|
||||
to_entities = recognizer_result.entities.get("$instance", {}).get(
|
||||
"To", []
|
||||
)
|
||||
if len(to_entities) > 0:
|
||||
if recognizer_result.entities.get("To", [{"$instance": {}}])[0][
|
||||
"$instance"
|
||||
]:
|
||||
result.destination = to_entities[0]["text"].capitalize()
|
||||
else:
|
||||
result.unsupported_airports.append(
|
||||
to_entities[0]["text"].capitalize()
|
||||
)
|
||||
|
||||
from_entities = recognizer_result.entities.get("$instance", {}).get(
|
||||
"From", []
|
||||
)
|
||||
if len(from_entities) > 0:
|
||||
if recognizer_result.entities.get("From", [{"$instance": {}}])[0][
|
||||
"$instance"
|
||||
]:
|
||||
result.origin = from_entities[0]["text"].capitalize()
|
||||
else:
|
||||
result.unsupported_airports.append(
|
||||
from_entities[0]["text"].capitalize()
|
||||
)
|
||||
|
||||
# This value will be a TIMEX. And we are only interested in a Date so grab the first result and drop
|
||||
# the Time part. TIMEX is a format that represents DateTime expressions that include some ambiguity.
|
||||
# e.g. missing a Year.
|
||||
date_entities = recognizer_result.entities.get("datetime", [])
|
||||
if date_entities:
|
||||
timex = date_entities[0]["timex"]
|
||||
|
||||
if timex:
|
||||
datetime = timex[0].split("T")[0]
|
||||
|
||||
result.travel_date = datetime
|
||||
|
||||
else:
|
||||
result.travel_date = None
|
||||
|
||||
except Exception as exception:
|
||||
print(exception)
|
||||
|
||||
return intent, result
|
|
@ -1,4 +0,0 @@
|
|||
tornado>=6.0.3
|
||||
botbuilder-dialogs>=4.14.0
|
||||
botbuilder-ai>=4.14.0
|
||||
datatypes-date-time>=1.0.0.a2
|
Загрузка…
Ссылка в новой задаче