City Power & Light is a sample application that allows citizens to report "incidents" that have occurred in their community. It includes a landing screen, a dashboard, and a form for reporting new incidents with an optional photo. The application is implemented with several components:
- Front end web application contains the user interface and business logic. This component has been implemented three times in .NET, NodeJS, and Java.
- WebAPI is shared across the front ends and exposes the backend CosmosDB.
- CosmosDB is used as the data persistence layer.
In this lab, you will continue enhancing the overall City Power & Light experience by creating an incident reporting bot from scratch and via the Azure Bot Service and host it in Azure. The bot will be able to gather data from a user with an optional photo and submit it to the WebAPI.
In this hands-on lab, you will learn how to:
- Set up the developing environment to support the creation of bot applications.
- Create your own bot from scratch.
- Create your own bot using Azure Bot Service.
- Hosting your bot in Azure.
- The source for the starter app is located in the start folder.
- The finished project is located in the end folder.
- Deployed the starter ARM Template HOL 1.
- Completion of the HOL 5.
Note: If you did not complete the previous labs, the project in the start folder is cumulative. But you need to add the previous HOL's settings to the
Web.config
file.
This hands-on-lab has the following exercises:
- Exercise 1: Set up your environment
- Exercise 2: Create an interactive dialog
- Exercise 3: Integrate the API
- Exercise 4: Send attachments to the bot
- Exercise 5: Host your bot in Azure
- Exercise 6: Azure Bot Service
To develop a bot on your machine you need the Bot Application
template for Visual Studio and the Bot Framework Emulator
. To test your bot once it has been deployed to Azure you will need ngrok
.
-
The easiest way to install the
Bot Application
template is via the Visual Studio 2015New Project
dialog. SelectOnline
and enterBot
in the search box. SelectBot_Application
. Create a new project by clickingOK
and the template will be installed.If you are using Visual Studio 2017 download the Bot Application template and install the template by saving the .zip file to your Visual Studio 2017 project templates directory. The Visual Studio 2017 project templates directory is typically located here:
%USERPROFILE%\Documents\Visual Studio 2017\Templates\ProjectTemplates\Visual C#\
. -
We will use the locally installed
Bot Framework Emulator
to test our bot. Once the bot is registered it can also be tested in the Bot Framework Portal. There you can configure the channels your bot will support. It can be integrated into a website, reached via Skype and many other channels. -
To install the
Bot Framework Emulator
, download the current version from the release page and execute the setup. -
ngrok
is a tunneling software that allows you to debug a remotely hosted bot. Download the current version of ngrok and extract the.exe
file to your Desktop.
You have now installed all the necessary components to start developing a bot on your machine.
We are using the FormFlow template to create our bot. The template let's you define a number of properties, including ones based on enums, that the bot will automatically gather from the user. We can even modify the text the bot uses to prompt the user and simply add regular expressions that are verified by the bot.
-
Open Visual Studio and load the
CityPowerBot.sln
from thestart
folder. -
Open the
CityPowerBot
->Dialogs
->BasicForm.cs
and add the following code which will create a property for each value of our incident report. It will also let the bot introduce itself and greet the user with its own name. The order of the interaction is determined by theFormBuilder
in theBuildForm
method.using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Builder.FormFlow; using Microsoft.Bot.Builder.FormFlow.Advanced; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Threading; #pragma warning disable 649 namespace CityPowerBot { public enum IncidentTypes { GasLeak = 1, StreetLightStaysOn }; // For more information about this template visit http://aka.ms/azurebots-csharp-form [Serializable] public class BasicForm { [Prompt("What is your {&}?")] public string FirstName { get; set; } [Prompt("And your {&}?")] public string LastName { get; set; } [Prompt("What type of outage would you like to report? {||}")] public IncidentTypes IncidentType { get; set; } [Prompt("Is this issue an {&}? {||}")] public bool Emergency { get; set; } [Prompt("Please give a {&} of the problem.")] public string Description { get; set; } [Pattern(@"(<Undefined control sequence>\d)?\s*\d{3}(-|\s*)\d{4}")] [Prompt("What is the {&} where we can currently reach you?")] public string PhoneNumber { get; set; } [Prompt("In which {&} do you live?")] public string City { get; set; } [Prompt("And in which {&}?")] public string State { get; set; } [Prompt("Lastly, what {&} do you live on?")] public string Street { get; set; } [Pattern(@"^\d{5}(?:[-\s]\d{4})?$")] [Prompt("What is your {&}?")] public string ZipCode { get; set; } public static IForm<BasicForm> BuildForm() { // Builds an IForm<T> based on BasicForm return new FormBuilder<BasicForm>() .Message("I am the City Power Bot! You can file a new incident report with me :-)") .Field(nameof(FirstName)) .Field(nameof(LastName)) .Message("Hello {FirstName} {LastName}! Let's file your report!") .Field(nameof(Emergency)) .Field(nameof(IncidentType)) .Field(nameof(Description)) .Field(nameof(City)) .Field(nameof(State)) .Field(nameof(ZipCode)) .Field(nameof(Street)) .Field(nameof(PhoneNumber)) .Build(); } public static IFormDialog<BasicForm> BuildFormDialog(FormOptions options = FormOptions.PromptInStart) { // Generated a new FormDialog<T> based on IForm<BasicForm> return FormDialog.FromForm(BuildForm, options); } } }
Note how we can use the
IncidentTypes
enum and the boolean propertyEmergency
. TheFormBuilder
will automatically turn these types into choices presented to the user. Two regular expressions check the format of thePhoneNumber
andZipCode
properties. -
Let's test the new bot. Hit
F5
to start the debugging process. The Internet Explorer will open and display bot information. Note the address. -
Start the
Bot Framework Emulator
. -
As the endpoint URL enter your bot's address followed by
/api/messages
. It should look similar tohttp://localhost:3979/api/messages
. Since we are debugging locally you can ignore the textboxes forMicrosoft App ID
,Microsoft App Password
, andLocale
and just click theCONNECT
button. -
The
Log
section in the lower right corner will inform you, that you are now connected to your bot. -
Enter any text in the message window to start the dialog with your bot. It will then start to prompt you for incident details.
-
During your interaction you can use command words like
back
,quit
,reset
,status
andhelp
.
You have now created a rudimentary bot that gathers all the data we need for an incident report from the user using the FormFlow template which does most of the work for you. Next you will extend the bot to also accept an image for the report.
Did you notice the image button next to the message window? You can not only send text messages to your bot but also image files. Let's tell the bot how to handle them.
-
In the
CityPowerBot
->Controllers
->MessagesController.cs
replace the creation of theMainDialog
with a switch that filters messages containing images while letting the existingMainDialog
handle all the other messages.if (activity.Type == ActivityTypes.Message) { // Stores send images out of order. var connector = new ConnectorClient(new Uri(activity.ServiceUrl)); var imageAttachment = activity.Attachments?.FirstOrDefault(a => a.ContentType.Contains("image")); if (imageAttachment != null) { LastImage = await GetImageStream(connector, imageAttachment); LastImageName = imageAttachment.Name; LastImageType = imageAttachment.ContentType; Activity reply = activity.CreateReply("Got your image!"); await connector.Conversations.ReplyToActivityAsync(reply); } else { // Creates a dialog stack for the new conversation, adds MainDialog to the stack, and forwards all messages to the dialog stack. await Conversation.SendAsync(activity, () => new MainDialog()); } }
-
Add these two methods that will extract a stream from the image send by the user.
private static async Task<Stream> GetImageStream(ConnectorClient connector, Attachment imageAttachment) { using (var httpClient = new HttpClient()) { // The Skype attachment URLs are secured by JwtToken, // you should set the JwtToken of your bot as the authorization header for the GET request your bot initiates to fetch the image. // https://github.com/Microsoft/BotBuilder/issues/662 var uri = new Uri(imageAttachment.ContentUrl); if (uri.Host.EndsWith("skype.com") && uri.Scheme == "https") { httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", await GetTokenAsync(connector)); httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/octet-stream")); } return await httpClient.GetStreamAsync(uri); } } /// <summary> /// Gets the JwT token of the bot. /// </summary> /// <param name="connector"></param> /// <returns>JwT token of the bot</returns> private static async Task<string> GetTokenAsync(ConnectorClient connector) { var credentials = connector.Credentials as MicrosoftAppCredentials; if (credentials != null) { return await credentials.GetTokenAsync(); } return null; }
The code will store the last submitted image in the variables to be used when the report is submitted.
-
Now we have to tell the users that they can send images along with their incident reports. Open the
CityPowerBot
->Dialogs
->BasicForm.cs
and in theBuildForm
method add this additional message right after the first message your bot sends:.Message("Did you know? At any point during our conversation you can send me an image that I will attach to your report.")
-
Hit
F5
to start the debugging process and talk to your bot via theBot Framework Emulator
. Note that the bot is informing you about its new capability. During the interaction click the image button and send an image from your machine. The bot will confirm that it "got your image".The DevCamp folder contains many images that you can use to test the feature.
Sending the image will not affect the rest of your conversation with the bot.
Your bot now accepts and stores an image send by the user. Now that you have everything that can be submitted in an incident report you are going to send it to the incident API.
To file the reported incident we use the incident API. The necessary methods are present in the DataWriter
project. The project contains excerpts from the previous hands on labs. The code was shortened to just create an incident and upload the attached image. To complete it you have to add your Azure account information.
-
Open
web.config
of theCityPowerBot Project
and replace the value forINCIDENT_API_URL
with the URL of your incident API which you retrieved in HOL 2 exercise 1 and looks similar tohttp://incidentapi[...].azurewebsites.net
. -
Replace the value for
AZURE_STORAGE_ACCOUNT
with your storage account name and the value forAZURE_STORAGE_ACCESS_KEY
with your storage account key you retrieved in HOL 2 exercise 3. -
Your
web.config
should now be updated with your unique values.<add key="INCIDENT_API_URL" value="API URL" /> <add key="AZURE_STORAGE_ACCOUNT" value="STORAGEACCOUNT"/> <add key="AZURE_STORAGE_ACCESS_KEY" value="STORAGEKEY" />
-
Now that the
DataWriter
project is prepared we will call itsCreate
method after we got all the data from the user. OpenCityPowerBot
->Dialogs
->BasicForm.cs
and add the followingOnCompletionAsyncDelegate
declaration at the beginning of theBuildForm
method before thereturn
:OnCompletionAsyncDelegate<BasicForm> processReport = async (context, state) => { await context.PostAsync("We are currently processing your report. We will message you the status."); if (await DataWriter.IncidentController.CreateAsync(state.FirstName, state.LastName, state.Street, state.City, state.State, state.ZipCode, state.PhoneNumber, state.Description, state.IncidentType.ToString(), state.Emergency, MessagesController.LastImage, MessagesController.LastImageName, MessagesController.LastImageType)) { await context.PostAsync("The incident report has been logged."); } else { await context.PostAsync("An error occured logging the incident."); } };
-
We are going to let the users confirm that they want to submit the entered data using the template's confirm feature. If the user replies with
No
the input can be changed before it is submitted. Again the FormFlow template does all of the work for you. Once we get the confirmation we can process the incident report by sending it to our incident API. Add theConfirm
andOnCompletion
calls to the end of the form builder chain before theBuild()
call:.Field(nameof(PhoneNumber)) .Confirm(async (state) => { return new PromptAttribute($"OK, we have got all your data. Would you like to send your incident report now?"); }) .OnCompletion(processReport) .Build();
-
Hit
F5
to start the debugging process and talk to your bot via theBot Framework Emulator
. -
Answer all the bot's questions and confirm that you want to send the incident report.
-
In another browser tab, open the Dashboard of the City Power site you deployed in the previous hands on labs to check that your new incident has been logged. You can also run a local copy of your code from another instance of Visual Studio to run the City Power site.
-
Use the Azure Storage Explorer like you did in HOL 2 exercise 3 to check that the image you attached was uploaded to the blob storage. You can double-click the image to open it in a new window.
Your bot is finished. It gathers and uploads data to create a new incident report. Next you are going to deploy it to Azure to make it globally accessible.
To make our bot accessible we have to publish it in a public location. An Azure app is idealy suited for this. We will let Visual Studio do the publishing and automatically create a new Azure app in our resource group to host the bot. Once the Visual Studio publishing wizard has done this we will register the bot in the Bot Framework Portal and add the generated IDs to our Web.config
.
-
If your bot is still running, stop it. In Solution Explorer, right-click on the
CityPowerBot
project and selectPublish
. This starts the Microsoft Azure publishing wizard. -
Select
Microsoft Azure App Service
which will open the App Service dialog. -
Select the
DevCamp
resource group and clickNew...
. -
Accept the defaults and click
Create
. -
Note the
Destination URL
value (you'll need this value later to test the connection to the bot), and then clickValidate Connection
to verify that the settings have been configured correctly. If validation is successful, clickNext
. -
By default, your bot will be published in a
Release
configuration. (If you want to debug your bot, changeConfiguration
toDebug
.) ClickPublish
to publish your bot to Microsoft Azure. -
Open a browser and navigate to the Bot Framework Portal. After you sign in, click
Create
: -
In the dialog select
Register an existing bot using Bot Builder SDK
and clickOk
:Complete the Bot profile section of the form.
- Optionally upload an icon that will represent your bot in the conversation.
- Provide your bot's
Display Name
. When users search for this bot, this is the name that will appear in the search results. - Provide your bot's
Handle
. This value will be used in the URL for your bot and cannot be changed after registration. - Provide a
Description
of your bot. This is the description that will appear in search results, so it should accurately describe what the bot does.
Complete the Configuration section of the form.
-
Provide your bot's HTTPS messaging endpoint. This is the endpoint where your bot will receive HTTP POST messages from Bot Connector. Use the
Destination URL
you noted earlier and addhttps
and/api/messages
. It should look similar to this:https://citypowerbot20170712104043.azurewebsites.net/api/messages
. -
Click
Create Microsoft App ID and password
.- Note the App ID.
- On the next page, click
Generate an app password to continue
. - Copy and securely store the password that is shown, and then click
Ok
. - Click
Finish and go back to Bot Framework
. - Back in the Bot Framework Portal, the
App ID
field is now populated.
Complete the Admin section of the form.
-
Specify the email address(es) for the Owner(s) of the bot.
-
Check to indicate that you have read and agree to the Terms of Use, Privacy Statement, and Code of Conduct.
-
Click
Register
to complete the registration process.
-
In Visual Studio, open the
CityPowerBot
->Web.config
and enter the values you just gathered for theBotId
(the bot'sHandle
you entered during the registration process),MicrosoftAppId
andMicrosoftAppPassword
in theappSettings
section.<appSettings> <!--OMITTED--> <!-- update these with your BotId, Microsoft App Id and your Microsoft App Password--> <add key="BotId" value="YourBotId" /> <add key="MicrosoftAppId" value="" /> <add key="MicrosoftAppPassword" value="" /> </appSettings>
-
In Solution Explorer, right-click on the
CityPowerBot
project and selectPublish
. This starts the Microsoft Azure publishing wizard. ClickPublish
to publish the changes. -
Verify the deployment of your bot by using the
Bot Framework Emulator
. You have to configurengrok
to connect to your Azure hosted bot. Click the three dots and selectApp Settings
to open the settings dialog. -
In the dialog select the
ngrok.exe
file you extracted to your Desktop in the first exercise, check theUse version 1.0 authentication tokens
option and clickSAVE
to close the dialog. -
Enter the bot's HTTPS endpoint into the address bar of the Emulator. It should look similar to this:
https://citypowerbot20170712104043.azurewebsites.net/api/messages
. Also provide theMicrosoft App ID
and theMicrosoft App Password
you noted earlier. Then clickCONNECT
. Test your bot as before.If you get a Server 500 error message try removing the
Microsoft App ID
and theMicrosoft App Password
and reconnect to the bot. -
If you like you can now configure the bot to run on one or more channels.
You have now manually created a bot and uploaded it to Azure. An alternative way of creating a bot is using the Azure Bot Service.
You have seen some of the basics of bot development. In the exercises you have used the FormFlow template to create the interaction between the user and the bot. Many other templates are available. You can also use Azure Bot Service to quickly create a bot from within the Azure portal.
-
To create a bot using the Azure Bot Service navigate to the DevCamp resource group and click
Add
. EnterBot Service
in the filter box, then select theBot Service (Preview)
and clickCreate
on the details blade. -
Enter a name for your bot and click
Create
again. -
Navigate to the bot in your resource group. You will be redirected to the template blade. Now you can select your preferred programming language and the template you want to use for your bot. Choose the
Form
to end up with a bot that is again based on the FormFlow template and clickNext
. -
Click
Manage Microsoft App ID and password
and create a new id for your bot. Copy the App Id and the password to the template blade, check and agree to the Terms of Use and clickCreate bot
. -
The deployment will take some minutes.
-
You can find more information on the templates and their application here.
-
You can now develop and test your bot directly within the Azure Portal but should configure continuous integration to be able to add additional files. If you make changes to the code you might have to reload the page for the test section to show the changes. Your new bot is also available via the Bot Framework Portal. You can use a second browser window to test the bot in the Bot Framework Portal.
-
When you follow the guide provided by Azure to enable continuous integration using what you learned in HOL 4 you will need to download a project file to run your bot's downloaded code in a local Visual Studio (2015) installation. (See this guide which wrongly states that you don't need the file for Visual Studio 2015). Place the downloaded project file in the
messages
folder before you continue.-
Run
dotnet restore
on the same folder. -
Follow the guide and install Azure Functions CLI.
-
And also DotNet CLI.
-
And finally the Command Task Runner Visual Studio Extension.
-
Now start Visual Studio and add the reference
, "Microsoft.Bot.Connector": "1.1.0"
to theproject.json
file. -
Your downloaded bot code created by the Azure Bot Service should now compile and you can also use the
Bot Framework Emulator
to debug it locally.
-
You have now seen an alternative way to create and debug a bot using the Azure Bot Service. A basic bot can be created completely without a development environment.
In this hands-on lab, you learned how to:
- Set up the developing environment to support the creation of bot applications.
- Create your own bot from scratch.
- Create your own bot using Azure Bot Service.
- Hosting your bot in Azure.
After completing this module, you can continue on to Module 9: IoT.
View Module 9 instructions for .NET
Copyright 2017 Microsoft Corporation. All rights reserved. Except where otherwise noted, these materials are licensed under the terms of the MIT License. You may use them according to the license as is most appropriate for your project. The terms of this license can be found at https://opensource.org/licenses/MIT.