[Samples] Add 13-core.bot sample (#1034)
* Add main pom for core-bot sample * Add deploymentTemplates folder * Add cognitiveModels folder * Add main documentation of the sample * Add dialogs classes * Add resources folder * Add webapp folder * Add bot for sample * Add BookingDetails model * Add recognizer for LUIS model * Add Startup file * Add package-info * Add empty test for sample
This commit is contained in:
Родитель
e51a84dd1e
Коммит
3b23c785a8
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE
|
|
@ -0,0 +1,216 @@
|
|||
# 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"
|
||||
}
|
||||
```
|
|
@ -0,0 +1,67 @@
|
|||
# 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.
|
||||
|
||||
### Create a LUIS Application to enable language understanding
|
||||
|
||||
The LUIS model for this example can be found under `cognitiveModels/FlightBooking.json` and the LUIS language model setup, training, and application configuration steps can be found [here](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-v4-luis?view=azure-bot-service-4.0&tabs=cs).
|
||||
|
||||
Once you created the LUIS model, update `application.properties` with your `LuisAppId`, `LuisAPIKey` and `LuisAPIHostName`.
|
||||
|
||||
```
|
||||
LuisAppId="Your LUIS App Id"
|
||||
LuisAPIKey="Your LUIS Subscription key here"
|
||||
LuisAPIHostName="Your LUIS App region here (i.e: westus.api.cognitive.microsoft.com)"
|
||||
```
|
||||
|
||||
## To try this sample
|
||||
|
||||
- From the root of this project folder:
|
||||
- Build the sample using `mvn package`
|
||||
- Run it by using `java -jar .\target\bot-core-sample.jar`
|
||||
|
||||
## 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 latest Bot Framework Emulator 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`
|
||||
|
||||
## 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/en-us/azure/bot-service/bot-builder-concept-dialog?view=azure-bot-service-4.0)
|
||||
- [Gathering Input Using Prompts](https://docs.microsoft.com/en-us/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/en-us/azure/cognitive-services/luis/)
|
||||
- [Channels and Bot Connector Service](https://docs.microsoft.com/en-us/azure/bot-service/bot-concepts?view=azure-bot-service-4.0)
|
||||
- [Spring Boot](https://spring.io/projects/spring-boot)
|
|
@ -0,0 +1,339 @@
|
|||
{
|
||||
"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": []
|
||||
}
|
|
@ -0,0 +1,291 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"groupLocation": {
|
||||
"defaultValue": "",
|
||||
"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": {
|
||||
"defaultValue": "S1",
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanName": {
|
||||
"defaultValue": "",
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The name of the App Service Plan."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanSku": {
|
||||
"type": "object",
|
||||
"defaultValue": {
|
||||
"name": "P1v2",
|
||||
"tier": "PremiumV2",
|
||||
"size": "P1v2",
|
||||
"family": "Pv2",
|
||||
"capacity": 1
|
||||
},
|
||||
"metadata": {
|
||||
"description": "The SKU of the App Service Plan. Defaults to Standard values."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanLocation": {
|
||||
"defaultValue": "",
|
||||
"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": {
|
||||
"perSiteScaling": false,
|
||||
"maximumElasticWorkerCount": 1,
|
||||
"isSpot": false,
|
||||
"reserved": true,
|
||||
"isXenon": false,
|
||||
"hyperV": false,
|
||||
"targetWorkerCount": 0,
|
||||
"targetWorkerSizeId": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"comments": "Create a Web App using a Linux App Service Plan",
|
||||
"type": "Microsoft.Web/sites",
|
||||
"apiVersion": "2018-11-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')]",
|
||||
"reserved": true,
|
||||
"isXenon": false,
|
||||
"hyperV": false,
|
||||
"scmSiteAlsoStopped": false,
|
||||
"clientAffinityEnabled": true,
|
||||
"clientCertEnabled": false,
|
||||
"hostNamesDisabled": false,
|
||||
"containerSize": 0,
|
||||
"dailyMemoryTimeQuota": 0,
|
||||
"httpsOnly": false,
|
||||
"redundancyMode": "None",
|
||||
"siteConfig": {
|
||||
"appSettings": [
|
||||
{
|
||||
"name": "JAVA_OPTS",
|
||||
"value": "-Dserver.port=80"
|
||||
},
|
||||
{
|
||||
"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": "2018-11-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",
|
||||
"linuxFxVersion": "JAVA|8-jre8",
|
||||
"requestTracingEnabled": false,
|
||||
"remoteDebuggingEnabled": false,
|
||||
"httpLoggingEnabled": false,
|
||||
"logsDirectorySizeLimit": 35,
|
||||
"detailedErrorLoggingEnabled": false,
|
||||
"publishingUsername": "[variables('publishingUsername')]",
|
||||
"scmType": "None",
|
||||
"use32BitWorkerProcess": true,
|
||||
"webSocketsEnabled": false,
|
||||
"alwaysOn": true,
|
||||
"managedPipelineMode": "Integrated",
|
||||
"virtualApplications": [
|
||||
{
|
||||
"virtualPath": "/",
|
||||
"physicalPath": "site\\wwwroot",
|
||||
"preloadEnabled": true
|
||||
}
|
||||
],
|
||||
"loadBalancing": "LeastRequests",
|
||||
"experiments": {
|
||||
"rampUpRules": []
|
||||
},
|
||||
"autoHealEnabled": false,
|
||||
"localMySqlEnabled": false,
|
||||
"ipSecurityRestrictions": [
|
||||
{
|
||||
"ipAddress": "Any",
|
||||
"action": "Allow",
|
||||
"priority": 1,
|
||||
"name": "Allow all",
|
||||
"description": "Allow all access"
|
||||
}
|
||||
],
|
||||
"scmIpSecurityRestrictions": [
|
||||
{
|
||||
"ipAddress": "Any",
|
||||
"action": "Allow",
|
||||
"priority": 1,
|
||||
"name": "Allow all",
|
||||
"description": "Allow all access"
|
||||
}
|
||||
],
|
||||
"scmIpSecurityRestrictionsUseMain": false,
|
||||
"http20Enabled": false,
|
||||
"minTlsVersion": "1.2",
|
||||
"ftpsState": "AllAllowed",
|
||||
"reservedInstanceCount": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2017-12-01",
|
||||
"type": "Microsoft.BotService/botServices",
|
||||
"name": "[parameters('botId')]",
|
||||
"location": "global",
|
||||
"kind": "bot",
|
||||
"sku": {
|
||||
"name": "[parameters('botSku')]"
|
||||
},
|
||||
"properties": {
|
||||
"name": "[parameters('botId')]",
|
||||
"displayName": "[parameters('botId')]",
|
||||
"endpoint": "[variables('botEndpoint')]",
|
||||
"msaAppId": "[parameters('appId')]",
|
||||
"developerAppInsightsApplicationId": null,
|
||||
"developerAppInsightKey": null,
|
||||
"publishingCredentials": null,
|
||||
"storageResourceId": null
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/sites/', variables('webAppName'))]"
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputs": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,259 @@
|
|||
{
|
||||
"$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. Defaults to \"\"."
|
||||
}
|
||||
},
|
||||
"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": "S1",
|
||||
"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": "P1v2",
|
||||
"tier": "PremiumV2",
|
||||
"size": "P1v2",
|
||||
"family": "Pv2",
|
||||
"capacity": 1
|
||||
},
|
||||
"metadata": {
|
||||
"description": "The SKU of the App Service Plan. Defaults to Standard values."
|
||||
}
|
||||
},
|
||||
"appServicePlanLocation": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"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",
|
||||
"condition": "[not(variables('useExistingAppServicePlan'))]",
|
||||
"name": "[variables('servicePlanName')]",
|
||||
"apiVersion": "2018-02-01",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"sku": "[parameters('newAppServicePlanSku')]",
|
||||
"kind": "linux",
|
||||
"properties": {
|
||||
"perSiteScaling": false,
|
||||
"maximumElasticWorkerCount": 1,
|
||||
"isSpot": false,
|
||||
"reserved": true,
|
||||
"isXenon": false,
|
||||
"hyperV": false,
|
||||
"targetWorkerCount": 0,
|
||||
"targetWorkerSizeId": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"comments": "Create a Web App using a Linux App Service Plan",
|
||||
"type": "Microsoft.Web/sites",
|
||||
"apiVersion": "2018-11-01",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"kind": "app,linux",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]"
|
||||
],
|
||||
"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": "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]",
|
||||
"reserved": true,
|
||||
"isXenon": false,
|
||||
"hyperV": false,
|
||||
"scmSiteAlsoStopped": false,
|
||||
"clientAffinityEnabled": true,
|
||||
"clientCertEnabled": false,
|
||||
"hostNamesDisabled": false,
|
||||
"containerSize": 0,
|
||||
"dailyMemoryTimeQuota": 0,
|
||||
"httpsOnly": false,
|
||||
"redundancyMode": "None",
|
||||
"siteConfig": {
|
||||
"appSettings": [
|
||||
{
|
||||
"name": "JAVA_OPTS",
|
||||
"value": "-Dserver.port=80"
|
||||
},
|
||||
{
|
||||
"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": "2018-11-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",
|
||||
"linuxFxVersion": "JAVA|8-jre8",
|
||||
"requestTracingEnabled": false,
|
||||
"remoteDebuggingEnabled": false,
|
||||
"httpLoggingEnabled": false,
|
||||
"logsDirectorySizeLimit": 35,
|
||||
"detailedErrorLoggingEnabled": false,
|
||||
"publishingUsername": "[variables('publishingUsername')]",
|
||||
"scmType": "None",
|
||||
"use32BitWorkerProcess": true,
|
||||
"webSocketsEnabled": false,
|
||||
"alwaysOn": true,
|
||||
"managedPipelineMode": "Integrated",
|
||||
"virtualApplications": [
|
||||
{
|
||||
"virtualPath": "/",
|
||||
"physicalPath": "site\\wwwroot",
|
||||
"preloadEnabled": true
|
||||
}
|
||||
],
|
||||
"loadBalancing": "LeastRequests",
|
||||
"experiments": {
|
||||
"rampUpRules": []
|
||||
},
|
||||
"autoHealEnabled": false,
|
||||
"localMySqlEnabled": false,
|
||||
"ipSecurityRestrictions": [
|
||||
{
|
||||
"ipAddress": "Any",
|
||||
"action": "Allow",
|
||||
"priority": 1,
|
||||
"name": "Allow all",
|
||||
"description": "Allow all access"
|
||||
}
|
||||
],
|
||||
"scmIpSecurityRestrictions": [
|
||||
{
|
||||
"ipAddress": "Any",
|
||||
"action": "Allow",
|
||||
"priority": 1,
|
||||
"name": "Allow all",
|
||||
"description": "Allow all access"
|
||||
}
|
||||
],
|
||||
"scmIpSecurityRestrictionsUseMain": false,
|
||||
"http20Enabled": false,
|
||||
"minTlsVersion": "1.2",
|
||||
"ftpsState": "AllAllowed",
|
||||
"reservedInstanceCount": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2017-12-01",
|
||||
"type": "Microsoft.BotService/botServices",
|
||||
"name": "[parameters('botId')]",
|
||||
"location": "global",
|
||||
"kind": "bot",
|
||||
"sku": {
|
||||
"name": "[parameters('botSku')]"
|
||||
},
|
||||
"properties": {
|
||||
"name": "[parameters('botId')]",
|
||||
"displayName": "[parameters('botId')]",
|
||||
"endpoint": "[variables('botEndpoint')]",
|
||||
"msaAppId": "[parameters('appId')]",
|
||||
"developerAppInsightsApplicationId": null,
|
||||
"developerAppInsightKey": null,
|
||||
"publishingCredentials": null,
|
||||
"storageResourceId": null
|
||||
},
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Web/sites/', variables('webAppName'))]"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,253 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.microsoft.bot.sample</groupId>
|
||||
<artifactId>bot-core</artifactId>
|
||||
<version>sample</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.groupId}:${project.artifactId}</name>
|
||||
<description>This package contains a Java Core Bot sample using Spring Boot.</description>
|
||||
<url>http://maven.apache.org</url>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.4.0</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>MIT License</name>
|
||||
<url>http://www.opensource.org/licenses/mit-license.php</url>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<name>Bot Framework Development</name>
|
||||
<email></email>
|
||||
<organization>Microsoft</organization>
|
||||
<organizationUrl>https://dev.botframework.com/</organizationUrl>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<start-class>com.microsoft.bot.sample.core.Application</start-class>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<version>2.4.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.vintage</groupId>
|
||||
<artifactId>junit-vintage-engine</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>2.11.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>2.13.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.microsoft.bot</groupId>
|
||||
<artifactId>bot-integration-spring</artifactId>
|
||||
<version>4.6.0-preview9</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.bot</groupId>
|
||||
<artifactId>bot-dialogs</artifactId>
|
||||
<version>4.6.0-preview9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.bot</groupId>
|
||||
<artifactId>bot-ai-qna</artifactId>
|
||||
<version>4.6.0-preview9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.bot</groupId>
|
||||
<artifactId>bot-ai-luis-v3</artifactId>
|
||||
<version>4.6.0-preview9</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>build</id>
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>3.2.3</version>
|
||||
<configuration>
|
||||
<warSourceDirectory>src/main/webapp</warSourceDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<mainClass>com.microsoft.bot.sample.core.Application</mainClass>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.microsoft.azure</groupId>
|
||||
<artifactId>azure-webapp-maven-plugin</artifactId>
|
||||
<version>1.12.0</version>
|
||||
<configuration>
|
||||
<schemaVersion>V2</schemaVersion>
|
||||
<resourceGroup>${groupname}</resourceGroup>
|
||||
<appName>${botname}</appName>
|
||||
<appSettings>
|
||||
<property>
|
||||
<name>JAVA_OPTS</name>
|
||||
<value>-Dserver.port=80</value>
|
||||
</property>
|
||||
</appSettings>
|
||||
<runtime>
|
||||
<os>linux</os>
|
||||
<javaVersion>Java 8</javaVersion>
|
||||
<webContainer>Java SE</webContainer>
|
||||
</runtime>
|
||||
<deployment>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${project.basedir}/target</directory>
|
||||
<includes>
|
||||
<include>*.jar</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</deployment>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>publish</id>
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>3.2.3</version>
|
||||
<configuration>
|
||||
<warSourceDirectory>src/main/webapp</warSourceDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.sonatype.plugins</groupId>
|
||||
<artifactId>nexus-staging-maven-plugin</artifactId>
|
||||
<version>1.6.8</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<skipRemoteStaging>true</skipRemoteStaging>
|
||||
<serverId>ossrh</serverId>
|
||||
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
|
||||
<autoReleaseAfterClose>true</autoReleaseAfterClose>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>8</source>
|
||||
<failOnError>false</failOnError>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
|
@ -0,0 +1,114 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.microsoft.bot.sample.core;
|
||||
|
||||
import com.microsoft.bot.builder.Bot;
|
||||
import com.microsoft.bot.builder.ConversationState;
|
||||
import com.microsoft.bot.builder.Storage;
|
||||
import com.microsoft.bot.builder.UserState;
|
||||
import com.microsoft.bot.dialogs.Dialog;
|
||||
import com.microsoft.bot.integration.AdapterWithErrorHandler;
|
||||
import com.microsoft.bot.integration.BotFrameworkHttpAdapter;
|
||||
import com.microsoft.bot.integration.Configuration;
|
||||
import com.microsoft.bot.integration.spring.BotController;
|
||||
import com.microsoft.bot.integration.spring.BotDependencyConfiguration;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* This is the starting point of the Sprint Boot Bot application.
|
||||
*/
|
||||
@SpringBootApplication
|
||||
|
||||
// Use the default BotController to receive incoming Channel messages. A custom
|
||||
// controller could be used by eliminating this import and creating a new
|
||||
// org.springframework.web.bind.annotation.RestController.
|
||||
// The default controller is created by the Spring Boot container using
|
||||
// dependency injection. The default route is /api/messages.
|
||||
@Import({BotController.class})
|
||||
|
||||
/**
|
||||
* This class extends the BotDependencyConfiguration which provides the default
|
||||
* implementations for a Bot application. The Application class should
|
||||
* override methods in order to provide custom implementations.
|
||||
*/
|
||||
public class Application extends BotDependencyConfiguration {
|
||||
/**
|
||||
* The start method.
|
||||
* @param args The args.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Bot for this application.
|
||||
*
|
||||
* <p>
|
||||
* The @Component annotation could be used on the Bot class instead of this method
|
||||
* with the @Bean annotation.
|
||||
* </p>
|
||||
*
|
||||
* @return The Bot implementation for this application.
|
||||
*/
|
||||
@Bean
|
||||
public Bot getBot(
|
||||
) {
|
||||
Storage storage = this.getStorage();
|
||||
UserState userState = this.getUserState(storage);
|
||||
ConversationState conversationState = this.getConversationState(storage);
|
||||
Dialog rootDialog = this.getRootDialog();
|
||||
return new DialogAndWelcomeBot(conversationState, userState, rootDialog);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a FlightBookingRecognizer object.
|
||||
* @return The FlightBookingRecognizer.
|
||||
*/
|
||||
@Bean
|
||||
public FlightBookingRecognizer getFlightBookingRecognizer() {
|
||||
Configuration configuration = this.getConfiguration();
|
||||
return new FlightBookingRecognizer(configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a BookingDialog object.
|
||||
* @return The BookingDialog.
|
||||
*/
|
||||
@Bean
|
||||
public BookingDialog getBookingDialog() {
|
||||
return new BookingDialog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the starting Dialog for this application.
|
||||
*
|
||||
* <p>
|
||||
* The @Component annotation could be used on the Dialog class instead of this method
|
||||
* with the @Bean annotation.
|
||||
* </p>
|
||||
*
|
||||
* @return The Dialog implementation for this application.
|
||||
*/
|
||||
@Bean
|
||||
public Dialog getRootDialog() {
|
||||
FlightBookingRecognizer flightBookingRecognizer = this.getFlightBookingRecognizer();
|
||||
BookingDialog bookingDialog = this.getBookingDialog();
|
||||
return new MainDialog(flightBookingRecognizer, bookingDialog);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a custom Adapter that provides error handling.
|
||||
*
|
||||
* @param configuration The Configuration object to use.
|
||||
* @return An error handling BotFrameworkHttpAdapter.
|
||||
*/
|
||||
@Override
|
||||
public BotFrameworkHttpAdapter getBotFrameworkHttpAdaptor(Configuration configuration) {
|
||||
return new AdapterWithErrorHandler(configuration);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.microsoft.bot.sample.core;
|
||||
|
||||
/**
|
||||
* The model class to retrieve the information of the booking.
|
||||
*/
|
||||
public class BookingDetails {
|
||||
private String destination;
|
||||
private String origin;
|
||||
private String travelDate;
|
||||
|
||||
/**
|
||||
* Gets the destination of the booking.
|
||||
* @return The destination.
|
||||
*/
|
||||
public String getDestination() {
|
||||
return destination;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the destination of the booking.
|
||||
* @param withDestination The new destination.
|
||||
*/
|
||||
public void setDestination(String withDestination) {
|
||||
this.destination = withDestination;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the origin of the booking.
|
||||
* @return The origin.
|
||||
*/
|
||||
public String getOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the origin of the booking.
|
||||
* @param withOrigin The new origin.
|
||||
*/
|
||||
public void setOrigin(String withOrigin) {
|
||||
this.origin = withOrigin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the travel date of the booking.
|
||||
* @return The travel date.
|
||||
*/
|
||||
public String getTravelDate() {
|
||||
return travelDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the travel date of the booking.
|
||||
* @param withTravelDate The new travel date.
|
||||
*/
|
||||
public void setTravelDate(String withTravelDate) {
|
||||
this.travelDate = withTravelDate;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.microsoft.bot.sample.core;
|
||||
|
||||
import com.microsoft.bot.builder.MessageFactory;
|
||||
import com.microsoft.bot.dialogs.DialogTurnResult;
|
||||
import com.microsoft.bot.dialogs.WaterfallDialog;
|
||||
import com.microsoft.bot.dialogs.WaterfallStep;
|
||||
import com.microsoft.bot.dialogs.WaterfallStepContext;
|
||||
import com.microsoft.bot.dialogs.prompts.ConfirmPrompt;
|
||||
import com.microsoft.bot.dialogs.prompts.PromptOptions;
|
||||
import com.microsoft.bot.dialogs.prompts.TextPrompt;
|
||||
import com.microsoft.bot.schema.Activity;
|
||||
import com.microsoft.bot.schema.InputHints;
|
||||
import com.microsoft.recognizers.datatypes.timex.expression.Constants;
|
||||
import com.microsoft.recognizers.datatypes.timex.expression.TimexProperty;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* The class containing the booking dialogs.
|
||||
*/
|
||||
public class BookingDialog extends CancelAndHelpDialog {
|
||||
private final String destinationStepMsgText = "Where would you like to travel to?";
|
||||
private final String originStepMsgText = "Where are you traveling from?";
|
||||
|
||||
/**
|
||||
* The constructor of the Booking Dialog class.
|
||||
*/
|
||||
public BookingDialog() {
|
||||
super("BookingDialog");
|
||||
|
||||
addDialog(new TextPrompt("TextPrompt"));
|
||||
addDialog(new ConfirmPrompt("ConfirmPrompt"));
|
||||
addDialog(new DateResolverDialog(null));
|
||||
WaterfallStep[] waterfallSteps = {
|
||||
this::destinationStep,
|
||||
this::originStep,
|
||||
this::travelDateStep,
|
||||
this::confirmStep,
|
||||
this::finalStep
|
||||
};
|
||||
addDialog(new WaterfallDialog("WaterfallDialog", Arrays.asList(waterfallSteps)));
|
||||
|
||||
// The initial child Dialog to run.
|
||||
setInitialDialogId("WaterfallDialog");
|
||||
}
|
||||
|
||||
|
||||
private CompletableFuture<DialogTurnResult> destinationStep(WaterfallStepContext stepContext) {
|
||||
BookingDetails bookingDetails = (BookingDetails) stepContext.getOptions();
|
||||
|
||||
if (bookingDetails.getDestination().isEmpty()) {
|
||||
Activity promptMessage =
|
||||
MessageFactory.text(destinationStepMsgText, destinationStepMsgText, InputHints.EXPECTING_INPUT);
|
||||
PromptOptions promptOptions = new PromptOptions();
|
||||
promptOptions.setPrompt(promptMessage);
|
||||
return stepContext.prompt("TextPrompt", promptOptions);
|
||||
}
|
||||
|
||||
return stepContext.next(bookingDetails.getDestination());
|
||||
}
|
||||
|
||||
|
||||
private CompletableFuture<DialogTurnResult> originStep(WaterfallStepContext stepContext) {
|
||||
BookingDetails bookingDetails = (BookingDetails) stepContext.getOptions();
|
||||
|
||||
bookingDetails.setDestination(stepContext.getResult().toString());
|
||||
|
||||
if (bookingDetails.getOrigin().isEmpty()) {
|
||||
Activity promptMessage =
|
||||
MessageFactory.text(originStepMsgText, originStepMsgText, InputHints.EXPECTING_INPUT);
|
||||
PromptOptions promptOptions = new PromptOptions();
|
||||
promptOptions.setPrompt(promptMessage);
|
||||
return stepContext.prompt("TextPrompt", promptOptions);
|
||||
}
|
||||
|
||||
return stepContext.next(bookingDetails.getOrigin());
|
||||
}
|
||||
|
||||
|
||||
private CompletableFuture<DialogTurnResult> travelDateStep(WaterfallStepContext stepContext) {
|
||||
BookingDetails bookingDetails = (BookingDetails) stepContext.getOptions();
|
||||
|
||||
bookingDetails.setOrigin(stepContext.getResult().toString());
|
||||
|
||||
if (bookingDetails.getTravelDate() == null || isAmbiguous(bookingDetails.getTravelDate())) {
|
||||
return stepContext.beginDialog("DateResolverDialog", bookingDetails.getTravelDate());
|
||||
}
|
||||
|
||||
return stepContext.next(bookingDetails.getTravelDate());
|
||||
}
|
||||
|
||||
|
||||
private CompletableFuture<DialogTurnResult> confirmStep(WaterfallStepContext stepContext) {
|
||||
BookingDetails bookingDetails = (BookingDetails) stepContext.getOptions();
|
||||
|
||||
bookingDetails.setTravelDate(stepContext.getResult().toString());
|
||||
|
||||
String messageText =
|
||||
String.format("Please confirm, I have you traveling to: %s from: %s on: %s. Is this correct?",
|
||||
bookingDetails.getDestination(), bookingDetails.getOrigin(), bookingDetails.getTravelDate());
|
||||
Activity promptMessage = MessageFactory.text(messageText, messageText, InputHints.EXPECTING_INPUT);
|
||||
|
||||
PromptOptions promptOptions = new PromptOptions();
|
||||
promptOptions.setPrompt(promptMessage);
|
||||
|
||||
return stepContext.prompt("ConfirmPrompt", promptOptions);
|
||||
}
|
||||
|
||||
|
||||
private CompletableFuture<DialogTurnResult> finalStep(WaterfallStepContext stepContext) {
|
||||
if ((Boolean) stepContext.getResult()) {
|
||||
BookingDetails bookingDetails = (BookingDetails) stepContext.getOptions();
|
||||
|
||||
return stepContext.endDialog(bookingDetails);
|
||||
}
|
||||
|
||||
return stepContext.endDialog(null);
|
||||
}
|
||||
|
||||
private static boolean isAmbiguous(String timex) {
|
||||
TimexProperty timexProperty = new TimexProperty(timex);
|
||||
return !timexProperty.getTypes().contains(Constants.TimexTypes.DEFINITE);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.microsoft.bot.sample.core;
|
||||
|
||||
import com.microsoft.bot.builder.MessageFactory;
|
||||
import com.microsoft.bot.dialogs.ComponentDialog;
|
||||
import com.microsoft.bot.dialogs.DialogContext;
|
||||
import com.microsoft.bot.dialogs.DialogTurnResult;
|
||||
import com.microsoft.bot.dialogs.DialogTurnStatus;
|
||||
import com.microsoft.bot.schema.Activity;
|
||||
import com.microsoft.bot.schema.ActivityTypes;
|
||||
import com.microsoft.bot.schema.InputHints;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* The class in charge of the dialog interruptions.
|
||||
*/
|
||||
public class CancelAndHelpDialog extends ComponentDialog {
|
||||
private final String helpMsgText = "Show help here";
|
||||
private final String cancelMsgText = "Cancelling...";
|
||||
|
||||
/**
|
||||
* The constructor of the CancelAndHelpDialog class.
|
||||
* @param id The dialog's Id.
|
||||
*/
|
||||
public CancelAndHelpDialog(String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the dialog is _continued_, where it is the active dialog and the
|
||||
* user replies with a new activity.
|
||||
* @param innerDc innerDc The inner {@link DialogContext} for the current turn of conversation.
|
||||
* @return A {@link CompletableFuture} representing the asynchronous operation.
|
||||
* If the task is successful, the result indicates whether the dialog is
|
||||
* still active after the turn has been processed by the dialog. The
|
||||
* result may also contain a return value.
|
||||
*/
|
||||
@Override
|
||||
protected CompletableFuture<DialogTurnResult> onContinueDialog(DialogContext innerDc) {
|
||||
return interrupt(innerDc).thenCompose(result -> {
|
||||
if (result != null) {
|
||||
return CompletableFuture.completedFuture(result);
|
||||
}
|
||||
return super.onContinueDialog(innerDc);
|
||||
});
|
||||
}
|
||||
|
||||
private CompletableFuture<DialogTurnResult> interrupt(DialogContext innerDc) {
|
||||
if (innerDc.getContext().getActivity().isType(ActivityTypes.MESSAGE)) {
|
||||
String text = innerDc.getContext().getActivity().getText().toLowerCase();
|
||||
|
||||
switch (text) {
|
||||
case "help":
|
||||
case "?":
|
||||
Activity helpMessage = MessageFactory.text(helpMsgText, helpMsgText, InputHints.EXPECTING_INPUT);
|
||||
return innerDc.getContext().sendActivity(helpMessage)
|
||||
.thenCompose(sendResult ->
|
||||
CompletableFuture.completedFuture(new DialogTurnResult(DialogTurnStatus.WAITING)));
|
||||
case "cancel":
|
||||
case "quit":
|
||||
Activity cancelMessage = MessageFactory
|
||||
.text(cancelMsgText, cancelMsgText, InputHints.IGNORING_INPUT);
|
||||
return innerDc.getContext()
|
||||
.sendActivity(cancelMessage).thenCompose(sendResult -> innerDc.cancelAllDialogs());
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.microsoft.bot.sample.core;
|
||||
|
||||
import com.microsoft.bot.builder.MessageFactory;
|
||||
import com.microsoft.bot.dialogs.DialogTurnResult;
|
||||
import com.microsoft.bot.dialogs.WaterfallDialog;
|
||||
import com.microsoft.bot.dialogs.WaterfallStep;
|
||||
import com.microsoft.bot.dialogs.WaterfallStepContext;
|
||||
import com.microsoft.bot.dialogs.prompts.DateTimePrompt;
|
||||
import com.microsoft.bot.dialogs.prompts.DateTimeResolution;
|
||||
import com.microsoft.bot.dialogs.prompts.PromptOptions;
|
||||
import com.microsoft.bot.dialogs.prompts.PromptValidatorContext;
|
||||
import com.microsoft.bot.schema.Activity;
|
||||
import com.microsoft.bot.schema.InputHints;
|
||||
import com.microsoft.recognizers.datatypes.timex.expression.Constants;
|
||||
import com.microsoft.recognizers.datatypes.timex.expression.TimexProperty;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* The class containing the date resolver dialogs.
|
||||
*/
|
||||
public class DateResolverDialog extends CancelAndHelpDialog {
|
||||
private final String promptMsgText = "When would you like to travel?";
|
||||
private final String repromptMsgText =
|
||||
"I'm sorry, to make your booking please enter a full travel date including Day Month and Year.";
|
||||
|
||||
|
||||
/**
|
||||
* The constructor of the DateResolverDialog class.
|
||||
* @param id The dialog's id.
|
||||
*/
|
||||
public DateResolverDialog(@Nullable String id) {
|
||||
super(id != null ? id : "DateResolverDialog");
|
||||
|
||||
|
||||
addDialog(new DateTimePrompt("DateTimePrompt",
|
||||
DateResolverDialog::dateTimePromptValidator, null));
|
||||
WaterfallStep[] waterfallSteps = {
|
||||
this::initialStep,
|
||||
this::finalStep
|
||||
};
|
||||
addDialog(new WaterfallDialog("WaterfallDialog", Arrays.asList(waterfallSteps)));
|
||||
|
||||
// The initial child Dialog to run.
|
||||
setInitialDialogId("WaterfallDialog");
|
||||
}
|
||||
|
||||
private CompletableFuture<DialogTurnResult> initialStep(WaterfallStepContext stepContext) {
|
||||
String timex = (String) stepContext.getOptions();
|
||||
|
||||
Activity promptMessage = MessageFactory.text(promptMsgText, promptMsgText, InputHints.EXPECTING_INPUT);
|
||||
Activity repromptMessage = MessageFactory.text(repromptMsgText, repromptMsgText, InputHints.EXPECTING_INPUT);
|
||||
|
||||
if (timex == null) {
|
||||
// We were not given any date at all so prompt the user.
|
||||
PromptOptions promptOptions = new PromptOptions();
|
||||
promptOptions.setPrompt(promptMessage);
|
||||
promptOptions.setRetryPrompt(repromptMessage);
|
||||
return stepContext.prompt("DateTimePrompt", promptOptions);
|
||||
}
|
||||
|
||||
// We have a Date we just need to check it is unambiguous.
|
||||
TimexProperty timexProperty = new TimexProperty(timex);
|
||||
if (!timexProperty.getTypes().contains(Constants.TimexTypes.DEFINITE)) {
|
||||
// This is essentially a "reprompt" of the data we were given up front.
|
||||
PromptOptions promptOptions = new PromptOptions();
|
||||
promptOptions.setPrompt(repromptMessage);
|
||||
return stepContext.prompt("DateTimePrompt", promptOptions);
|
||||
}
|
||||
|
||||
DateTimeResolution dateTimeResolution = new DateTimeResolution() {
|
||||
{
|
||||
setTimex(timex);
|
||||
}
|
||||
};
|
||||
List<DateTimeResolution> dateTimeResolutions = new ArrayList<DateTimeResolution>() {
|
||||
{
|
||||
add(dateTimeResolution);
|
||||
}
|
||||
};
|
||||
return stepContext.next(dateTimeResolutions);
|
||||
}
|
||||
|
||||
private CompletableFuture<DialogTurnResult> finalStep(WaterfallStepContext stepContext) {
|
||||
String timex = ((ArrayList<DateTimeResolution>) stepContext.getResult()).get(0).getTimex();
|
||||
return stepContext.endDialog(timex);
|
||||
}
|
||||
|
||||
private static CompletableFuture<Boolean> dateTimePromptValidator(PromptValidatorContext<List<DateTimeResolution>>
|
||||
promptContext) {
|
||||
if (promptContext.getRecognized().getSucceeded()) {
|
||||
// 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.
|
||||
String timex = ((List<DateTimeResolution>) promptContext.getRecognized().getValue())
|
||||
.get(0).getTimex().split("T")[0];
|
||||
|
||||
// If this is a definite Date including year, month and day we are good otherwise reprompt.
|
||||
// A better solution might be to let the user know what part is actually missing.
|
||||
Boolean isDefinite = new TimexProperty(timex).getTypes().contains(Constants.TimexTypes.DEFINITE);
|
||||
|
||||
return CompletableFuture.completedFuture(isDefinite);
|
||||
}
|
||||
|
||||
return CompletableFuture.completedFuture(false);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.microsoft.bot.sample.core;
|
||||
|
||||
import com.codepoetics.protonpack.collectors.CompletableFutures;
|
||||
import com.microsoft.applicationinsights.core.dependencies.apachecommons.io.IOUtils;
|
||||
import com.microsoft.applicationinsights.core.dependencies.apachecommons.lang3.StringUtils;
|
||||
import com.microsoft.bot.builder.ConversationState;
|
||||
import com.microsoft.bot.builder.MessageFactory;
|
||||
import com.microsoft.bot.builder.TurnContext;
|
||||
import com.microsoft.bot.builder.UserState;
|
||||
import com.microsoft.bot.dialogs.Dialog;
|
||||
import com.microsoft.bot.schema.Activity;
|
||||
import com.microsoft.bot.schema.Attachment;
|
||||
import com.microsoft.bot.schema.ChannelAccount;
|
||||
import com.microsoft.bot.schema.Serialization;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* The class containing the welcome dialog.
|
||||
* @param <T> is a Dialog.
|
||||
*/
|
||||
public class DialogAndWelcomeBot<T extends Dialog> extends DialogBot {
|
||||
/**
|
||||
* Creates a DialogBot.
|
||||
* @param withConversationState ConversationState to use in the bot
|
||||
* @param withUserState UserState to use
|
||||
* @param withDialog Param inheriting from Dialog class
|
||||
*/
|
||||
public DialogAndWelcomeBot(ConversationState withConversationState, UserState withUserState, T withDialog) {
|
||||
super(withConversationState, withUserState, withDialog);
|
||||
}
|
||||
|
||||
/**
|
||||
* When the {@link #onConversationUpdateActivity(TurnContext)} method receives a
|
||||
* conversation update activity that indicates one or more users other than the
|
||||
* bot are joining the conversation, it calls this method.
|
||||
* @param membersAdded A list of all the members added to the conversation,
|
||||
* as described by the conversation update activity
|
||||
* @param turnContext The context object for this turn.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
@Override
|
||||
protected CompletableFuture<Void> onMembersAdded(List<ChannelAccount> membersAdded, TurnContext turnContext) {
|
||||
return turnContext.getActivity().getMembersAdded().stream()
|
||||
.filter(member -> !StringUtils
|
||||
.equals(member.getId(), turnContext.getActivity().getRecipient().getId()))
|
||||
.map(channel -> {
|
||||
// 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.
|
||||
Attachment welcomeCard = createAdaptiveCardAttachment();
|
||||
Activity response = MessageFactory.attachment(welcomeCard, null, "Welcome to Bot Framework!", null);
|
||||
|
||||
return turnContext.sendActivity(response).thenApply(sendResult -> {
|
||||
return Dialog.run(getDialog(), turnContext, getConversationState().createProperty("DialogState"));
|
||||
});
|
||||
})
|
||||
.collect(CompletableFutures.toFutureList())
|
||||
.thenApply(resourceResponse -> null);
|
||||
}
|
||||
|
||||
// Load attachment from embedded resource.
|
||||
private Attachment createAdaptiveCardAttachment() {
|
||||
try (InputStream inputStream = Thread.currentThread().
|
||||
getContextClassLoader().getResourceAsStream("cards/welcomeCard.json")) {
|
||||
String adaptiveCardJson = IOUtils.toString(inputStream, StandardCharsets.UTF_8.toString());
|
||||
|
||||
return new Attachment() {{
|
||||
setContentType("application/vnd.microsoft.card.adaptive");
|
||||
setContent(Serialization.jsonToTree(adaptiveCardJson));
|
||||
}};
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return new Attachment();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.microsoft.bot.sample.core;
|
||||
|
||||
import com.microsoft.bot.builder.ActivityHandler;
|
||||
import com.microsoft.bot.builder.BotState;
|
||||
import com.microsoft.bot.builder.ConversationState;
|
||||
import com.microsoft.bot.builder.TurnContext;
|
||||
import com.microsoft.bot.builder.UserState;
|
||||
import com.microsoft.bot.dialogs.Dialog;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* This Bot implementation can run any type of Dialog. The use of type parameterization is to allow multiple
|
||||
* different bots to be run at different endpoints within the same project. This can be achieved by defining
|
||||
* distinct Controller types each with dependency on distinct Bot types. The ConversationState is used by
|
||||
* the Dialog system. The UserState isn't, however, it might have been used in a Dialog implementation,
|
||||
* and the requirement is that all BotState objects are saved at the end of a turn.
|
||||
*
|
||||
* @param <T> parameter of a type inheriting from Dialog
|
||||
*/
|
||||
public class DialogBot<T extends Dialog> extends ActivityHandler {
|
||||
private Dialog dialog;
|
||||
private BotState conversationState;
|
||||
private BotState userState;
|
||||
|
||||
/**
|
||||
* Gets the dialog in use.
|
||||
*
|
||||
* @return instance of dialog
|
||||
*/
|
||||
protected Dialog getDialog() {
|
||||
return dialog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the conversation state.
|
||||
*
|
||||
* @return instance of conversationState
|
||||
*/
|
||||
protected BotState getConversationState() {
|
||||
return conversationState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user state.
|
||||
*
|
||||
* @return instance of userState
|
||||
*/
|
||||
protected BotState getUserState() {
|
||||
return userState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the dialog in use.
|
||||
*
|
||||
* @param withDialog the dialog (of Dialog type) to be set
|
||||
*/
|
||||
protected void setDialog(Dialog withDialog) {
|
||||
dialog = withDialog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the conversation state.
|
||||
*
|
||||
* @param withConversationState the conversationState (of BotState type) to be set
|
||||
*/
|
||||
protected void setConversationState(BotState withConversationState) {
|
||||
conversationState = withConversationState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user state.
|
||||
*
|
||||
* @param withUserState the userState (of BotState type) to be set
|
||||
*/
|
||||
protected void setUserState(BotState withUserState) {
|
||||
userState = withUserState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DialogBot.
|
||||
* @param withConversationState ConversationState to use in the bot
|
||||
* @param withUserState UserState to use
|
||||
* @param withDialog Param inheriting from Dialog class
|
||||
*/
|
||||
public DialogBot(ConversationState withConversationState, UserState withUserState, T withDialog) {
|
||||
this.conversationState = withConversationState;
|
||||
this.userState = withUserState;
|
||||
this.dialog = withDialog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the BotState objects at the end of each turn.
|
||||
* @param turnContext
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public CompletableFuture<Void> onTurn(TurnContext turnContext) {
|
||||
return super.onTurn(turnContext)
|
||||
.thenCompose(turnResult -> conversationState.saveChanges(turnContext, false))
|
||||
.thenCompose(saveResult -> userState.saveChanges(turnContext, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is executed when the turnContext receives a message activity.
|
||||
* @param turnContext
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected CompletableFuture<Void> onMessageActivity(TurnContext turnContext) {
|
||||
LoggerFactory.getLogger(DialogBot.class).info("Running dialog with Message Activity.");
|
||||
|
||||
// Run the Dialog with the new message Activity.
|
||||
return Dialog.run(dialog, turnContext, conversationState.createProperty("DialogState"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.microsoft.bot.sample.core;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.microsoft.bot.ai.luis.LuisApplication;
|
||||
import com.microsoft.bot.ai.luis.LuisRecognizer;
|
||||
import com.microsoft.bot.ai.luis.LuisRecognizerOptionsV3;
|
||||
import com.microsoft.bot.builder.Recognizer;
|
||||
import com.microsoft.bot.builder.RecognizerResult;
|
||||
import com.microsoft.bot.builder.TurnContext;
|
||||
import com.microsoft.bot.integration.Configuration;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* The class in charge of recognizing the booking information.
|
||||
*/
|
||||
public class FlightBookingRecognizer implements Recognizer {
|
||||
private LuisRecognizer recognizer;
|
||||
|
||||
/**
|
||||
* The constructor of the FlightBookingRecognizer class.
|
||||
* @param configuration The Configuration object to use.
|
||||
*/
|
||||
public FlightBookingRecognizer(Configuration configuration) {
|
||||
Boolean luisIsConfigured = StringUtils.isNotBlank(configuration.getProperty("LuisAppId"))
|
||||
&& StringUtils.isNotBlank(configuration.getProperty("LuisAPIKey"))
|
||||
&& StringUtils.isNotBlank(configuration.getProperty("LuisAPIHostName"));
|
||||
if (luisIsConfigured) {
|
||||
LuisApplication luisApplication = new LuisApplication(
|
||||
configuration.getProperty("LuisAppId"),
|
||||
configuration.getProperty("LuisAPIKey"),
|
||||
String.format("https://%s", configuration.getProperty("LuisAPIHostName")));
|
||||
// Set the recognizer options depending on which endpoint version you want to use.
|
||||
// More details can be found in
|
||||
// https://docs.microsoft.com/en-gb/azure/cognitive-services/luis/luis-migration-api-v3
|
||||
LuisRecognizerOptionsV3 recognizerOptions = new LuisRecognizerOptionsV3(luisApplication) {
|
||||
{
|
||||
setIncludeInstanceData(true);
|
||||
}
|
||||
};
|
||||
|
||||
this.recognizer = new LuisRecognizer(recognizerOptions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if the recognizer is configured.
|
||||
* @return True if it's configured, False if it's not.
|
||||
*/
|
||||
public Boolean isConfigured() {
|
||||
return this.recognizer != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an object with preformatted LUIS results for the bot's dialogs to consume.
|
||||
* @param context A {link TurnContext}
|
||||
* @return A {link RecognizerResult}
|
||||
*/
|
||||
public CompletableFuture<RecognizerResult> executeLuisQuery(TurnContext context) {
|
||||
// Returns true if luis is configured in the application.properties and initialized.
|
||||
return this.recognizer.recognize(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the From data from the entities which is part of the result.
|
||||
* @param result The recognizer result.
|
||||
* @return The object node representing the From data.
|
||||
*/
|
||||
public ObjectNode getFromEntities(RecognizerResult result) {
|
||||
String fromValue = "", fromAirportValue = "";
|
||||
if (result.getEntities().get("$instance").get("From") != null) {
|
||||
fromValue = result.getEntities().get("$instance").get("From").get(0).get("text").asText();
|
||||
}
|
||||
if (!fromValue.isEmpty() && result.getEntities().get("From").get(0).get("Airport") != null) {
|
||||
fromAirportValue = result.getEntities().get("From").get(0).get("Airport").get(0).get(0).asText();
|
||||
}
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper().findAndRegisterModules();
|
||||
ObjectNode entitiesNode = mapper.createObjectNode();
|
||||
entitiesNode.put("from", fromValue);
|
||||
entitiesNode.put("airport", fromAirportValue);
|
||||
return entitiesNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the To data from the entities which is part of the result.
|
||||
* @param result The recognizer result.
|
||||
* @return The object node representing the To data.
|
||||
*/
|
||||
public ObjectNode getToEntities(RecognizerResult result) {
|
||||
String toValue = "", toAirportValue = "";
|
||||
if (result.getEntities().get("$instance").get("To") != null) {
|
||||
toValue = result.getEntities().get("$instance").get("To").get(0).get("text").asText();
|
||||
}
|
||||
if (!toValue.isEmpty() && result.getEntities().get("To").get(0).get("Airport") != null) {
|
||||
toAirportValue = result.getEntities().get("To").get(0).get("Airport").get(0).get(0).asText();
|
||||
}
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper().findAndRegisterModules();
|
||||
ObjectNode entitiesNode = mapper.createObjectNode();
|
||||
entitiesNode.put("to", toValue);
|
||||
entitiesNode.put("airport", toAirportValue);
|
||||
return entitiesNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param result A {link RecognizerResult}
|
||||
* @return The Timex value without the Time model
|
||||
*/
|
||||
public String getTravelDate(RecognizerResult result) {
|
||||
JsonNode datetimeEntity = result.getEntities().get("datetime");
|
||||
if (datetimeEntity == null || datetimeEntity.get(0) == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
JsonNode timex = datetimeEntity.get(0).get("timex");
|
||||
if (timex == null || timex.get(0) == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String datetime = timex.get(0).asText().split("T")[0];
|
||||
return datetime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs an utterance through a recognizer and returns a generic recognizer result.
|
||||
* @param turnContext Turn context.
|
||||
* @return Analysis of utterance.
|
||||
*/
|
||||
@Override
|
||||
public CompletableFuture<RecognizerResult> recognize(TurnContext turnContext) {
|
||||
return this.recognizer.recognize(turnContext);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,206 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.microsoft.bot.sample.core;
|
||||
|
||||
import com.microsoft.bot.builder.MessageFactory;
|
||||
import com.microsoft.bot.builder.TurnContext;
|
||||
import com.microsoft.bot.dialogs.ComponentDialog;
|
||||
import com.microsoft.bot.dialogs.DialogTurnResult;
|
||||
import com.microsoft.bot.dialogs.WaterfallDialog;
|
||||
import com.microsoft.bot.dialogs.WaterfallStep;
|
||||
import com.microsoft.bot.dialogs.WaterfallStepContext;
|
||||
import com.microsoft.bot.dialogs.prompts.PromptOptions;
|
||||
import com.microsoft.bot.dialogs.prompts.TextPrompt;
|
||||
import com.microsoft.bot.schema.Activity;
|
||||
import com.microsoft.bot.schema.InputHints;
|
||||
import com.microsoft.recognizers.datatypes.timex.expression.TimexProperty;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* The class containing the main dialog for the sample.
|
||||
*/
|
||||
public class MainDialog extends ComponentDialog {
|
||||
private final FlightBookingRecognizer luisRecognizer;
|
||||
private final Integer plusDayValue = 7;
|
||||
|
||||
/**
|
||||
* The constructor of the Main Dialog class.
|
||||
* @param withLuisRecognizer The FlightBookingRecognizer object.
|
||||
* @param bookingDialog The BookingDialog object with booking dialogs.
|
||||
*/
|
||||
public MainDialog(FlightBookingRecognizer withLuisRecognizer, BookingDialog bookingDialog) {
|
||||
super("MainDialog");
|
||||
|
||||
luisRecognizer = withLuisRecognizer;
|
||||
|
||||
addDialog(new TextPrompt("TextPrompt"));
|
||||
addDialog(bookingDialog);
|
||||
WaterfallStep[] waterfallSteps = {
|
||||
this::introStep,
|
||||
this::actStep,
|
||||
this::finalStep
|
||||
};
|
||||
addDialog(new WaterfallDialog("WaterfallDialog", Arrays.asList(waterfallSteps)));
|
||||
|
||||
// The initial child Dialog to run.
|
||||
setInitialDialogId("WaterfallDialog");
|
||||
}
|
||||
|
||||
/**
|
||||
* First step in the waterfall dialog. Prompts the user for a command.
|
||||
* Currently, this expects a booking request, like "book me a flight from Paris to Berlin on march 22"
|
||||
* Note that the sample LUIS model will only recognize Paris, Berlin, New York and London as airport cities.
|
||||
* @param stepContext A {@link WaterfallStepContext}
|
||||
* @return A {@link DialogTurnResult}
|
||||
*/
|
||||
private CompletableFuture<DialogTurnResult> introStep(WaterfallStepContext stepContext) {
|
||||
if (!luisRecognizer.isConfigured()) {
|
||||
Activity text = MessageFactory.text("NOTE: LUIS is not configured. "
|
||||
+ "To enable all capabilities, add 'LuisAppId', 'LuisAPIKey' and 'LuisAPIHostName' "
|
||||
+ "to the appsettings.json file.", null, InputHints.IGNORING_INPUT);
|
||||
return stepContext.getContext().sendActivity(text)
|
||||
.thenCompose(sendResult -> stepContext.next(null));
|
||||
}
|
||||
|
||||
// Use the text provided in FinalStepAsync or the default if it is the first time.
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMMM d, yyyy");
|
||||
String weekLaterDate = LocalDateTime.now().plusDays(plusDayValue).format(formatter);
|
||||
String messageText = stepContext.getOptions() != null
|
||||
? stepContext.getOptions().toString()
|
||||
: String.format("What can I help you with today?\n"
|
||||
+ "Say something like \"Book a flight from Paris to Berlin on %s\"", weekLaterDate);
|
||||
Activity promptMessage = MessageFactory.text(messageText, messageText, InputHints.EXPECTING_INPUT);
|
||||
PromptOptions promptOptions = new PromptOptions();
|
||||
promptOptions.setPrompt(promptMessage);
|
||||
return stepContext.prompt("TextPrompt", promptOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Second step in the waterfall. This will use LUIS to attempt to extract the origin, destination and travel dates.
|
||||
* Then, it hands off to the bookingDialog child dialog to collect any remaining details.
|
||||
* @param stepContext A {@link WaterfallStepContext}
|
||||
* @return A {@link DialogTurnResult}
|
||||
*/
|
||||
private CompletableFuture<DialogTurnResult> actStep(WaterfallStepContext stepContext) {
|
||||
if (!luisRecognizer.isConfigured()) {
|
||||
// LUIS is not configured, we just run the BookingDialog path with an empty BookingDetailsInstance.
|
||||
return stepContext.beginDialog("BookingDialog", new BookingDetails());
|
||||
}
|
||||
|
||||
// Call LUIS and gather any potential booking details. (Note the TurnContext has the response to the prompt.)
|
||||
return luisRecognizer.recognize(stepContext.getContext()).thenCompose(luisResult -> {
|
||||
switch (luisResult.getTopScoringIntent().intent) {
|
||||
case "BookFlight":
|
||||
// Extract the values for the composite entities from the LUIS result.
|
||||
ObjectNode fromEntities = luisRecognizer.getFromEntities(luisResult);
|
||||
ObjectNode toEntities = luisRecognizer.getToEntities(luisResult);
|
||||
|
||||
// Show a warning for Origin and Destination if we can't resolve them.
|
||||
return showWarningForUnsupportedCities(stepContext.getContext(), fromEntities, toEntities)
|
||||
.thenCompose(showResult -> {
|
||||
// Initialize BookingDetails with any entities we may have found in the response.
|
||||
|
||||
BookingDetails bookingDetails = new BookingDetails();
|
||||
bookingDetails.setDestination(toEntities.get("airport").asText());
|
||||
bookingDetails.setOrigin(fromEntities.get("airport").asText());
|
||||
bookingDetails.setTravelDate(luisRecognizer.getTravelDate(luisResult));
|
||||
// Run the BookingDialog giving it whatever details we have from the LUIS call,
|
||||
// it will fill out the remainder.
|
||||
return stepContext.beginDialog("BookingDialog", bookingDetails);
|
||||
}
|
||||
);
|
||||
case "GetWeather":
|
||||
// We haven't implemented the GetWeatherDialog so we just display a TODO message.
|
||||
String getWeatherMessageText = "TODO: get weather flow here";
|
||||
Activity getWeatherMessage = MessageFactory
|
||||
.text(getWeatherMessageText, getWeatherMessageText, InputHints.IGNORING_INPUT);
|
||||
stepContext.getContext().sendActivity(getWeatherMessage);
|
||||
break;
|
||||
default:
|
||||
// Catch all for unhandled intents
|
||||
String didntUnderstandMessageText = String.format("Sorry, I didn't get that. Please "
|
||||
+ " try asking in a different way (intent was %s)", luisResult.getTopScoringIntent().intent);
|
||||
Activity didntUnderstandMessage = MessageFactory
|
||||
.text(didntUnderstandMessageText, didntUnderstandMessageText, InputHints.IGNORING_INPUT);
|
||||
stepContext.getContext().sendActivity(didntUnderstandMessage);
|
||||
break;
|
||||
}
|
||||
return stepContext.next(null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a warning if the requested From or To cities are recognized as entities
|
||||
* but they are not in the Airport entity list.
|
||||
* In some cases LUIS will recognize the From and To composite entities as a valid cities
|
||||
* but the From and To Airport values
|
||||
* will be empty if those entity values can't be mapped to a canonical item in the Airport.
|
||||
* @param turnContext A {@link WaterfallStepContext}
|
||||
* @param fromEntities An ObjectNode with the entities of From object
|
||||
* @param toEntities An ObjectNode with the entities of To object
|
||||
* @return A task
|
||||
*/
|
||||
private static CompletableFuture<Void> showWarningForUnsupportedCities(TurnContext turnContext,
|
||||
ObjectNode fromEntities,
|
||||
ObjectNode toEntities) {
|
||||
List<String> unsupportedCities = new ArrayList<String>();
|
||||
|
||||
if (StringUtils.isNotBlank(fromEntities.get("from").asText())
|
||||
&& StringUtils.isBlank(fromEntities.get("airport").asText())) {
|
||||
unsupportedCities.add(fromEntities.get("from").asText());
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(toEntities.get("to").asText())
|
||||
&& StringUtils.isBlank(toEntities.get("airport").asText())) {
|
||||
unsupportedCities.add(toEntities.get("to").asText());
|
||||
}
|
||||
|
||||
if (!unsupportedCities.isEmpty()) {
|
||||
String messageText = String.format("Sorry but the following airports are not supported: %s",
|
||||
String.join(", ", unsupportedCities));
|
||||
Activity message = MessageFactory.text(messageText, messageText, InputHints.IGNORING_INPUT);
|
||||
turnContext.sendActivity(message).thenApply(sendResult -> null);
|
||||
}
|
||||
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the final step in the main waterfall dialog.
|
||||
* It wraps up the sample "book a flight" interaction with a simple confirmation.
|
||||
* @param stepContext A {@link WaterfallStepContext}
|
||||
* @return A {@link DialogTurnResult}
|
||||
*/
|
||||
private CompletableFuture<DialogTurnResult> finalStep(WaterfallStepContext stepContext) {
|
||||
// If the child dialog ("BookingDialog") was cancelled,
|
||||
// the user failed to confirm or if the intent wasn't BookFlight
|
||||
// the Result here will be null.
|
||||
if (stepContext.getResult() instanceof BookingDetails) {
|
||||
// Now we have all the booking details call the booking service.
|
||||
|
||||
// If the call to the booking service was successful tell the user.
|
||||
|
||||
BookingDetails result = (BookingDetails) stepContext.getResult();
|
||||
TimexProperty timeProperty = new TimexProperty(result.getTravelDate());
|
||||
String travelDateMsg = timeProperty.toNaturalLanguage(LocalDateTime.now());
|
||||
String messageText = String.format("I have you booked to %s from %s on %s",
|
||||
result.getDestination(), result.getOrigin(), travelDateMsg);
|
||||
Activity message = MessageFactory.text(messageText, messageText, InputHints.IGNORING_INPUT);
|
||||
stepContext.getContext().sendActivity(message).thenApply(sendResult -> null);
|
||||
}
|
||||
|
||||
// Restart the main dialog with a different message the second time around
|
||||
String promptMessage = "What else can I do for you?";
|
||||
return stepContext.replaceDialog(getInitialDialogId(), promptMessage);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for
|
||||
// license information.
|
||||
|
||||
/**
|
||||
* This package contains the classes for the core-bot sample.
|
||||
*/
|
||||
package com.microsoft.bot.sample.core;
|
|
@ -0,0 +1,6 @@
|
|||
MicrosoftAppId=
|
||||
MicrosoftAppPassword=
|
||||
LuisAppId=
|
||||
LuisAPIKey=
|
||||
LuisAPIHostName=
|
||||
server.port=3978
|
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"$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"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"configuration": {
|
||||
"name": "Default",
|
||||
"appenders": {
|
||||
"Console": {
|
||||
"name": "Console-Appender",
|
||||
"target": "SYSTEM_OUT",
|
||||
"PatternLayout": {"pattern": "[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n"}
|
||||
}
|
||||
},
|
||||
"loggers": {
|
||||
"root": {
|
||||
"level": "debug",
|
||||
"appender-ref": {"ref": "Console-Appender","level": "debug"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
Manifest-Version: 1.0
|
||||
Class-Path:
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<servlet>
|
||||
<servlet-name>dispatcher</servlet-name>
|
||||
<servlet-class>
|
||||
org.springframework.web.servlet.DispatcherServlet
|
||||
</servlet-class>
|
||||
<init-param>
|
||||
<param-name>contextConfigLocation</param-name>
|
||||
<param-value>/WEB-INF/spring/dispatcher-config.xml</param-value>
|
||||
</init-param>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
|
@ -0,0 +1,417 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Core Bot Sample</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
font-family: Segoe UI;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
header {
|
||||
background-image: url("data:image/svg+xml,%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 4638.9 651.6' style='enable-background:new 0 0 4638.9 651.6;' xml:space='preserve'%3E%3Cstyle type='text/css'%3E .st0%7Bfill:%2355A0E0;%7D .st1%7Bfill:none;%7D .st2%7Bfill:%230058A8;%7D .st3%7Bfill:%23328BD8;%7D .st4%7Bfill:%23B6DCF1;%7D .st5%7Bopacity:0.2;fill:url(%23SVGID_1_);enable-background:new ;%7D%0A%3C/style%3E%3Crect y='1.1' class='st0' width='4640' height='646.3'/%3E%3Cpath class='st1' d='M3987.8,323.6L4310.3,1.1h-65.6l-460.1,460.1c-17.5,17.5-46.1,17.5-63.6,0L3260.9,1.1H0v646.3h3660.3 L3889,418.7c17.5-17.5,46.1-17.5,63.6,0l228.7,228.7h66.6l-260.2-260.2C3970.3,369.8,3970.3,341.1,3987.8,323.6z'/%3E%3Cpath class='st2' d='M3784.6,461.2L4244.7,1.1h-983.9l460.1,460.1C3738.4,478.7,3767.1,478.7,3784.6,461.2z'/%3E%3Cpath class='st3' d='M4640,1.1h-329.8l-322.5,322.5c-17.5,17.5-17.5,46.1,0,63.6l260.2,260.2H4640L4640,1.1L4640,1.1z'/%3E%3Cpath class='st4' d='M3889,418.8l-228.7,228.7h521.1l-228.7-228.7C3935.2,401.3,3906.5,401.3,3889,418.8z'/%3E%3ClinearGradient id='SVGID_1_' gradientUnits='userSpaceOnUse' x1='3713.7576' y1='438.1175' x2='3911.4084' y2='14.2535' gradientTransform='matrix(1 0 0 -1 0 641.3969)'%3E%3Cstop offset='0' style='stop-color:%23FFFFFF;stop-opacity:0.5'/%3E%3Cstop offset='1' style='stop-color:%23FFFFFF'/%3E%3C/linearGradient%3E%3Cpath class='st5' d='M3952.7,124.5c-17.5-17.5-46.1-17.5-63.6,0l-523,523h1109.6L3952.7,124.5z'/%3E%3C/svg%3E%0A");
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100%;
|
||||
background-position: right;
|
||||
background-color: #55A0E0;
|
||||
width: 100%;
|
||||
font-size: 44px;
|
||||
height: 120px;
|
||||
color: white;
|
||||
padding: 30px 0 40px 0px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.header-icon {
|
||||
background-image: url("data:image/svg+xml;utf8,%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20xmlns%3Axlink%3D%22http%3A//www.w3.org/1999/xlink%22%20x%3D%220px%22%20y%3D%220px%22%0A%09%20viewBox%3D%220%200%20150.2%20125%22%20style%3D%22enable-background%3Anew%200%200%20150.2%20125%3B%22%20xml%3Aspace%3D%22preserve%22%3E%0A%3Cstyle%20type%3D%22text/css%22%3E%0A%09.st0%7Bfill%3Anone%3B%7D%0A%09.st1%7Bfill%3A%23FFFFFF%3B%7D%0A%3C/style%3E%0A%3Crect%20x%3D%220.5%22%20class%3D%22st0%22%20width%3D%22149.7%22%20height%3D%22125%22/%3E%0A%3Cg%3E%0A%09%3Cpath%20class%3D%22st1%22%20d%3D%22M59%2C102.9L21.8%2C66c-3.5-3.5-3.5-9.1%2C0-12.5l37-36.5l2.9%2C3l-37%2C36.4c-1.8%2C1.8-1.8%2C4.7%2C0%2C6.6l37.2%2C37L59%2C102.9z%22%0A%09%09/%3E%0A%3C/g%3E%0A%3Cg%3E%0A%09%3Cpath%20class%3D%22st1%22%20d%3D%22M92.5%2C102.9l-3-3l37.2-37c0.9-0.9%2C1.4-2%2C1.4-3.3c0-1.2-0.5-2.4-1.4-3.3L89.5%2C20l2.9-3l37.2%2C36.4%0A%09%09c1.7%2C1.7%2C2.6%2C3.9%2C2.6%2C6.3s-0.9%2C4.6-2.6%2C6.3L92.5%2C102.9z%22/%3E%0A%3C/g%3E%0A%3Cg%3E%0A%09%3Cpath%20class%3D%22st1%22%20d%3D%22M90.1%2C68.4c-4.5%2C0-8-3.5-8-8.1c0-4.5%2C3.5-8.1%2C8-8.1c4.4%2C0%2C8%2C3.7%2C8%2C8.1C98.1%2C64.7%2C94.4%2C68.4%2C90.1%2C68.4z%0A%09%09%20M90.1%2C56.5c-2.2%2C0-3.8%2C1.7-3.8%2C3.9c0%2C2.2%2C1.7%2C3.9%2C3.8%2C3.9c1.9%2C0%2C3.8-1.6%2C3.8-3.9S91.9%2C56.5%2C90.1%2C56.5z%22/%3E%0A%3C/g%3E%0A%3Cg%3E%0A%09%3Cpath%20class%3D%22st1%22%20d%3D%22M61.4%2C68.4c-4.5%2C0-8-3.5-8-8.1c0-4.5%2C3.5-8.1%2C8-8.1c4.4%2C0%2C8%2C3.7%2C8%2C8.1C69.5%2C64.7%2C65.8%2C68.4%2C61.4%2C68.4z%0A%09%09%20M61.4%2C56.5c-2.2%2C0-3.8%2C1.7-3.8%2C3.9c0%2C2.2%2C1.7%2C3.9%2C3.8%2C3.9c1.9%2C0%2C3.8-1.6%2C3.8-3.9S63.3%2C56.5%2C61.4%2C56.5z%22/%3E%0A%3C/g%3E%0A%3C/svg%3E%0A");
|
||||
background-repeat: no-repeat;
|
||||
float: left;
|
||||
height: 140px;
|
||||
width: 140px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.header-text {
|
||||
padding-left: 1%;
|
||||
color: #FFFFFF;
|
||||
font-family: "Segoe UI";
|
||||
font-size: 72px;
|
||||
font-weight: 300;
|
||||
letter-spacing: 0.35px;
|
||||
line-height: 96px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.header-inner-container {
|
||||
min-width: 480px;
|
||||
max-width: 1366px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.header-inner-container::after {
|
||||
content: "";
|
||||
clear: both;
|
||||
display: table;
|
||||
}
|
||||
|
||||
.main-content-area {
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
.content-title {
|
||||
color: #000000;
|
||||
font-family: "Segoe UI";
|
||||
font-size: 46px;
|
||||
font-weight: 300;
|
||||
line-height: 62px;
|
||||
}
|
||||
|
||||
.main-text {
|
||||
color: #808080;
|
||||
font-size: 24px;
|
||||
font-family: "Segoe UI";
|
||||
font-size: 24px;
|
||||
font-weight: 200;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.main-text-p1{
|
||||
padding-top: 48px;
|
||||
padding-bottom: 28px;
|
||||
}
|
||||
|
||||
.endpoint {
|
||||
height: 32px;
|
||||
width: 571px;
|
||||
color: #808080;
|
||||
font-family: "Segoe UI";
|
||||
font-size: 24px;
|
||||
font-weight: 200;
|
||||
line-height: 32px;
|
||||
padding-top: 28px;
|
||||
}
|
||||
|
||||
.how-to-build-section {
|
||||
padding-top: 20px;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
.how-to-build-section>h3 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.35px;
|
||||
line-height: 22px;
|
||||
margin: 0 0 24px 0;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.step-container {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.step-container dl {
|
||||
border-left: 1px solid #A0A0A0;
|
||||
display: block;
|
||||
padding: 0 24px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.step-container dl>dt::before {
|
||||
background-color: white;
|
||||
border: 1px solid #A0A0A0;
|
||||
border-radius: 100%;
|
||||
content: '';
|
||||
left: 47px;
|
||||
height: 11px;
|
||||
position: absolute;
|
||||
width: 11px;
|
||||
}
|
||||
|
||||
.step-container dl>.test-bullet::before {
|
||||
background-color: blue;
|
||||
}
|
||||
|
||||
.step-container dl>dt {
|
||||
display: block;
|
||||
font-size: inherit;
|
||||
font-weight: bold;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.step-container dl>dd {
|
||||
font-size: inherit;
|
||||
line-height: 20px;
|
||||
margin-left: 0;
|
||||
padding-bottom: 32px;
|
||||
}
|
||||
|
||||
.step-container:last-child dl {
|
||||
border-left: 1px solid transparent;
|
||||
}
|
||||
|
||||
.ctaLink {
|
||||
background-color: transparent;
|
||||
border: 1px solid transparent;
|
||||
color: #006AB1;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
padding: 0;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.ctaLink:focus {
|
||||
outline: 1px solid #00bcf2;
|
||||
}
|
||||
|
||||
.ctaLink:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.step-icon {
|
||||
display: flex;
|
||||
height: 38px;
|
||||
margin-right: 15px;
|
||||
width: 38px;
|
||||
}
|
||||
|
||||
.step-icon>div {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.ms-logo-container {
|
||||
min-width: 580px;
|
||||
max-width: 980px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
left: 0;
|
||||
right: 0;
|
||||
transition: bottom 400ms;
|
||||
}
|
||||
|
||||
.ms-logo {
|
||||
float: right;
|
||||
background-image: url("data:image/svg+xml;utf8,%0A%3Csvg%20version%3D%221.1%22%20id%3D%22MS-symbol%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20xmlns%3Axlink%3D%22http%3A//www.w3.org/1999/xlink%22%20x%3D%220px%22%20y%3D%220px%22%0A%09%20viewBox%3D%220%200%20400%20120%22%20style%3D%22enable-background%3Anew%200%200%20400%20120%3B%22%20xml%3Aspace%3D%22preserve%22%3E%0A%3Cstyle%20type%3D%22text/css%22%3E%0A%09.st0%7Bfill%3Anone%3B%7D%0A%09.st1%7Bfill%3A%23737474%3B%7D%0A%09.st2%7Bfill%3A%23D63F26%3B%7D%0A%09.st3%7Bfill%3A%23167D3E%3B%7D%0A%09.st4%7Bfill%3A%232E76BC%3B%7D%0A%09.st5%7Bfill%3A%23FDB813%3B%7D%0A%3C/style%3E%0A%3Crect%20x%3D%220.6%22%20class%3D%22st0%22%20width%3D%22398.7%22%20height%3D%22119%22/%3E%0A%3Cpath%20class%3D%22st1%22%20d%3D%22M171.3%2C38.4v43.2h-7.5V47.7h-0.1l-13.4%2C33.9h-5l-13.7-33.9h-0.1v33.9h-6.9V38.4h10.8l12.4%2C32h0.2l13.1-32H171.3%0A%09z%20M177.6%2C41.7c0-1.2%2C0.4-2.2%2C1.3-3c0.9-0.8%2C1.9-1.2%2C3.1-1.2c1.3%2C0%2C2.4%2C0.4%2C3.2%2C1.3c0.8%2C0.8%2C1.3%2C1.8%2C1.3%2C3c0%2C1.2-0.4%2C2.2-1.3%2C3%0A%09c-0.9%2C0.8-1.9%2C1.2-3.2%2C1.2s-2.3-0.4-3.1-1.2C178%2C43.8%2C177.6%2C42.8%2C177.6%2C41.7z%20M185.7%2C50.6v31h-7.3v-31H185.7z%20M207.8%2C76.3%0A%09c1.1%2C0%2C2.3-0.3%2C3.6-0.8c1.3-0.5%2C2.5-1.2%2C3.6-2v6.8c-1.2%2C0.7-2.5%2C1.2-4%2C1.5c-1.5%2C0.3-3.1%2C0.5-4.9%2C0.5c-4.6%2C0-8.3-1.4-11.1-4.3%0A%09c-2.9-2.9-4.3-6.6-4.3-11c0-5%2C1.5-9.1%2C4.4-12.3c2.9-3.2%2C7-4.8%2C12.4-4.8c1.4%2C0%2C2.7%2C0.2%2C4.1%2C0.5c1.4%2C0.4%2C2.5%2C0.8%2C3.3%2C1.2v7%0A%09c-1.1-0.8-2.3-1.5-3.4-1.9c-1.2-0.5-2.4-0.7-3.6-0.7c-2.9%2C0-5.2%2C0.9-7%2C2.8c-1.8%2C1.9-2.7%2C4.4-2.7%2C7.6c0%2C3.1%2C0.8%2C5.6%2C2.5%2C7.3%0A%09C202.6%2C75.4%2C204.9%2C76.3%2C207.8%2C76.3z%20M235.7%2C50.1c0.6%2C0%2C1.1%2C0%2C1.6%2C0.1s0.9%2C0.2%2C1.2%2C0.3v7.4c-0.4-0.3-0.9-0.5-1.7-0.8%0A%09c-0.7-0.3-1.6-0.4-2.7-0.4c-1.8%2C0-3.3%2C0.8-4.5%2C2.3c-1.2%2C1.5-1.9%2C3.8-1.9%2C7v15.6h-7.3v-31h7.3v4.9h0.1c0.7-1.7%2C1.7-3%2C3-4%0A%09C232.2%2C50.6%2C233.8%2C50.1%2C235.7%2C50.1z%20M238.9%2C66.6c0-5.1%2C1.4-9.2%2C4.3-12.2c2.9-3%2C6.9-4.5%2C12.1-4.5c4.8%2C0%2C8.6%2C1.4%2C11.3%2C4.3%0A%09c2.7%2C2.9%2C4.1%2C6.8%2C4.1%2C11.7c0%2C5-1.4%2C9-4.3%2C12c-2.9%2C3-6.8%2C4.5-11.8%2C4.5c-4.8%2C0-8.6-1.4-11.4-4.2C240.3%2C75.3%2C238.9%2C71.4%2C238.9%2C66.6z%0A%09%20M246.5%2C66.3c0%2C3.2%2C0.7%2C5.7%2C2.2%2C7.4c1.5%2C1.7%2C3.6%2C2.6%2C6.3%2C2.6c2.7%2C0%2C4.7-0.9%2C6.1-2.6c1.4-1.7%2C2.1-4.2%2C2.1-7.6c0-3.3-0.7-5.8-2.2-7.5%0A%09c-1.4-1.7-3.4-2.5-6-2.5c-2.7%2C0-4.7%2C0.9-6.2%2C2.7C247.2%2C60.5%2C246.5%2C63%2C246.5%2C66.3z%20M281.5%2C58.8c0%2C1%2C0.3%2C1.9%2C1%2C2.5%0A%09c0.7%2C0.6%2C2.1%2C1.3%2C4.4%2C2.2c2.9%2C1.2%2C5%2C2.5%2C6.1%2C3.9c1.2%2C1.5%2C1.8%2C3.2%2C1.8%2C5.3c0%2C2.9-1.1%2C5.3-3.4%2C7c-2.2%2C1.8-5.3%2C2.7-9.1%2C2.7%0A%09c-1.3%2C0-2.7-0.2-4.3-0.5c-1.6-0.3-2.9-0.7-4-1.2v-7.2c1.3%2C0.9%2C2.8%2C1.7%2C4.3%2C2.2c1.5%2C0.5%2C2.9%2C0.8%2C4.2%2C0.8c1.6%2C0%2C2.9-0.2%2C3.6-0.7%0A%09c0.8-0.5%2C1.2-1.2%2C1.2-2.3c0-1-0.4-1.9-1.2-2.5c-0.8-0.7-2.4-1.5-4.6-2.4c-2.7-1.1-4.6-2.4-5.7-3.8c-1.1-1.4-1.7-3.2-1.7-5.4%0A%09c0-2.8%2C1.1-5.1%2C3.3-6.9c2.2-1.8%2C5.1-2.7%2C8.6-2.7c1.1%2C0%2C2.3%2C0.1%2C3.6%2C0.4c1.3%2C0.2%2C2.5%2C0.6%2C3.4%2C0.9v6.9c-1-0.6-2.1-1.2-3.4-1.7%0A%09c-1.3-0.5-2.6-0.7-3.8-0.7c-1.4%2C0-2.5%2C0.3-3.2%2C0.8C281.9%2C57.1%2C281.5%2C57.8%2C281.5%2C58.8z%20M297.9%2C66.6c0-5.1%2C1.4-9.2%2C4.3-12.2%0A%09c2.9-3%2C6.9-4.5%2C12.1-4.5c4.8%2C0%2C8.6%2C1.4%2C11.3%2C4.3c2.7%2C2.9%2C4.1%2C6.8%2C4.1%2C11.7c0%2C5-1.4%2C9-4.3%2C12c-2.9%2C3-6.8%2C4.5-11.8%2C4.5%0A%09c-4.8%2C0-8.6-1.4-11.4-4.2C299.4%2C75.3%2C297.9%2C71.4%2C297.9%2C66.6z%20M305.5%2C66.3c0%2C3.2%2C0.7%2C5.7%2C2.2%2C7.4c1.5%2C1.7%2C3.6%2C2.6%2C6.3%2C2.6%0A%09c2.7%2C0%2C4.7-0.9%2C6.1-2.6c1.4-1.7%2C2.1-4.2%2C2.1-7.6c0-3.3-0.7-5.8-2.2-7.5c-1.4-1.7-3.4-2.5-6-2.5c-2.7%2C0-4.7%2C0.9-6.2%2C2.7%0A%09C306.3%2C60.5%2C305.5%2C63%2C305.5%2C66.3z%20M353.9%2C56.6h-10.9v25h-7.4v-25h-5.2v-6h5.2v-4.3c0-3.3%2C1.1-5.9%2C3.2-8c2.1-2.1%2C4.8-3.1%2C8.1-3.1%0A%09c0.9%2C0%2C1.7%2C0%2C2.4%2C0.1c0.7%2C0.1%2C1.3%2C0.2%2C1.8%2C0.4V42c-0.2-0.1-0.7-0.3-1.3-0.5c-0.6-0.2-1.3-0.3-2.1-0.3c-1.5%2C0-2.7%2C0.5-3.5%2C1.4%0A%09s-1.2%2C2.4-1.2%2C4.2v3.7h10.9v-7l7.3-2.2v9.2h7.4v6h-7.4v14.5c0%2C1.9%2C0.3%2C3.3%2C1%2C4c0.7%2C0.8%2C1.8%2C1.2%2C3.3%2C1.2c0.4%2C0%2C0.9-0.1%2C1.5-0.3%0A%09c0.6-0.2%2C1.1-0.4%2C1.6-0.7v6c-0.5%2C0.3-1.2%2C0.5-2.3%2C0.7c-1.1%2C0.2-2.1%2C0.3-3.2%2C0.3c-3.1%2C0-5.4-0.8-6.9-2.5c-1.5-1.6-2.3-4.1-2.3-7.4%0A%09V56.6z%22/%3E%0A%3Cg%3E%0A%09%3Crect%20x%3D%2231%22%20y%3D%2224%22%20class%3D%22st2%22%20width%3D%2234.2%22%20height%3D%2234.2%22/%3E%0A%09%3Crect%20x%3D%2268.8%22%20y%3D%2224%22%20class%3D%22st3%22%20width%3D%2234.2%22%20height%3D%2234.2%22/%3E%0A%09%3Crect%20x%3D%2231%22%20y%3D%2261.8%22%20class%3D%22st4%22%20width%3D%2234.2%22%20height%3D%2234.2%22/%3E%0A%09%3Crect%20x%3D%2268.8%22%20y%3D%2261.8%22%20class%3D%22st5%22%20width%3D%2234.2%22%20height%3D%2234.2%22/%3E%0A%3C/g%3E%0A%3C/svg%3E%0A");
|
||||
}
|
||||
|
||||
.ms-logo-container>div {
|
||||
min-height: 60px;
|
||||
width: 150px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.row {
|
||||
padding: 90px 0px 0 20px;
|
||||
min-width: 480px;
|
||||
max-width: 1366px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.column {
|
||||
float: left;
|
||||
width: 45%;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.row:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.download-the-emulator {
|
||||
height: 20px;
|
||||
color: #0063B1;
|
||||
font-size: 15px;
|
||||
line-height: 20px;
|
||||
padding-bottom: 70px;
|
||||
}
|
||||
|
||||
.how-to-iframe {
|
||||
max-width: 700px !important;
|
||||
min-width: 650px !important;
|
||||
height: 700px !important;
|
||||
}
|
||||
|
||||
.remove-frame-height {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1300px) {
|
||||
.ms-logo {
|
||||
padding-top: 30px;
|
||||
}
|
||||
|
||||
.header-text {
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.column {
|
||||
float: none;
|
||||
padding-top: 30px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ms-logo-container {
|
||||
padding-top: 30px;
|
||||
min-width: 480px;
|
||||
max-width: 650px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.row {
|
||||
padding: 20px 0px 0 20px;
|
||||
min-width: 480px;
|
||||
max-width: 650px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1370px) {
|
||||
header {
|
||||
background-color: #55A0E0;
|
||||
background-size: auto 200px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1230px) {
|
||||
header {
|
||||
background-color: #55A0E0;
|
||||
background-size: auto 200px;
|
||||
}
|
||||
|
||||
.header-text {
|
||||
font-size: 44px;
|
||||
}
|
||||
|
||||
.header-icon {
|
||||
height: 120px;
|
||||
width: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1000px) {
|
||||
header {
|
||||
background-color: #55A0E0;
|
||||
background-image: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 632px) {
|
||||
.header-text {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.row {
|
||||
padding: 10px 0px 0 10px;
|
||||
max-width: 490px !important;
|
||||
min-width: 410px !important;
|
||||
}
|
||||
|
||||
.endpoint {
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
.main-text {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.step-container dl>dd {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.column {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.header-icon {
|
||||
height: 110px;
|
||||
width: 110px;
|
||||
}
|
||||
|
||||
.how-to-iframe {
|
||||
max-width: 480px !important;
|
||||
min-width: 400px !important;
|
||||
height: 650px !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.remove-frame-height {
|
||||
max-height: 10px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
loadFrame();
|
||||
});
|
||||
var loadFrame = function () {
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.setAttribute("id", "iframe");
|
||||
var offLineHTMLContent = "";
|
||||
var frameElement = document.getElementById("how-to-iframe");
|
||||
if (window.navigator.onLine) {
|
||||
iframe.src = 'https://docs.botframework.com/static/abs/pages/f5.htm';
|
||||
iframe.setAttribute("scrolling", "no");
|
||||
iframe.setAttribute("frameborder", "0");
|
||||
iframe.setAttribute("width", "100%");
|
||||
iframe.setAttribute("height", "100%");
|
||||
var frameDiv = document.getElementById("how-to-iframe");
|
||||
frameDiv.appendChild(iframe);
|
||||
} else {
|
||||
frameElement.classList.add("remove-frame-height");
|
||||
}
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header class="header">
|
||||
<div class="header-inner-container">
|
||||
<div class="header-icon" style="display: inline-block"></div>
|
||||
<div class="header-text" style="display: inline-block">Core Bot Sample</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="row">
|
||||
<div class="column" class="main-content-area">
|
||||
<div class="content-title">Your bot is ready!</div>
|
||||
<div class="main-text main-text-p1">You can test your bot in the Bot Framework Emulator<br />
|
||||
by connecting to http://localhost:3978/api/messages.</div>
|
||||
<div class="main-text download-the-emulator"><a class="ctaLink" href="https://aka.ms/bot-framework-F5-download-emulator"
|
||||
target="_blank">Download the Emulator</a></div>
|
||||
<div class="main-text">Visit <a class="ctaLink" href="https://aka.ms/bot-framework-F5-abs-home" target="_blank">Azure
|
||||
Bot Service</a> to register your bot and add it to<br />
|
||||
various channels. The bot's endpoint URL typically looks
|
||||
like this:</div>
|
||||
<div class="endpoint">https://<i>your_bots_hostname</i>/api/messages</div>
|
||||
</div>
|
||||
<div class="column how-to-iframe" id="how-to-iframe"></div>
|
||||
</div>
|
||||
<div class="ms-logo-container">
|
||||
<div class="ms-logo"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,2 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
Загрузка…
Ссылка в новой задаче