[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:
Martin Battaglino 2021-03-10 11:32:07 -03:00 коммит произвёл GitHub
Родитель e51a84dd1e
Коммит 3b23c785a8
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
24 изменённых файлов: 3003 добавлений и 0 удалений

Просмотреть файл

@ -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'))]"
]
}
]
}

253
samples/13.core-bot/pom.xml Normal file
Просмотреть файл

@ -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.